Returning Method Object from Inside Block

Returning an object from inside block within category class method implementation

Since you have async operations happening inside your blocks, you'll need to pass a completion handler (block) to your photoWithFlickrData:inManagedObjectContext: method and call it when you have valid photo data.

You'll need to add a new parameter to your method so you can pass in the completion handler. I'd do something like this:

+ (void)photoWithFlickrData:(NSDictionary *)photoDictionary
inManagedObjectContext:(NSManagedObjectContext *)context
withCompletionHandler:(void(^)(Photo *photo))completionHandler

Then, when you have a valid photo object, call completionHandler like so:

completionHandler(photo);

It looks like you'd want to put that at the very end of the block you're passing to dispatch_async:

/* ... */
dispatch_async(dispatch_get_main_queue(), ^{
Photo *photo = nil;

/* ... */

completionHandler(photo);
});
/* ... */

Then, you can call your method like so:

[Photo photoWithFlickrData:photoDictionary
inManagedObjectContext:context
withCompletionHandler:^(Photo* photo) {
/* use your valid photo object here */
}
];

Returning method object from inside block

You can't. Embrace the fact that what you're trying to do is asynchronous and add a completion block parameter to your getMyData method which is called when the inner completion handler is called. (And remove the return from the method signature):

- (void)getMyDataWithCompletion:(void(^)(NSData *data))completion {
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
completion((success ? doc.myResponseData : nil));
}];
}

The same problem exists in swift and you can add a similar completion block:

func getMyData(completion: ((data: NSData?) -> Void) {
data = ...
completion(data)
}

How to return method object from inside block iOS

As Quentin already mentioned, you can not directly do that because you are internally performing an asynchronous request. That means that your program starts the request and then continues its next statements and does not wait for the request to finish. What you should do is either

  • make the request synchronous to cause the program to wait until the request has finished. Be carefull however to not call this method from the main thread since that would block your app from continuing to respond until the request returned. Or
  • use a block as callback for the startParsing method (the same way blocks are used for the actual request callbacks)

That would loke for example like the following:

- (void)startParsing:(void (^)(NSArray*))parsingFinished {
allProductsID = [[NSMutableArray alloc] init];
NSString *string = [NSString stringWithFormat:@"http://%@:@%@",kPrestaShopAPIKey, kPrestaShopURLString];
NSURL *url = [NSURL URLWithString:string];

AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];

[manager GET:@"categories/21" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
// do your parsing...
parsingFinished([allProductsID copy]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
parsingFinished([[NSArray alloc] init]);
// or return nil or provide a parsingFailed callback
}];
}

which you would then call like

[yourObject startParsing:^(NSArray *parsedData) {
// do something with the parsed data
}];

Return object for a method inside completion block

If you want the MakeGetRequest method to return data obtained via dataTaskWithURL, you can't. That method performs an asynchronous call, which is most likely completed after the MakeGetRequest has already returned - but more generally it cannot be know in a deterministic way.

Usually asynchronous operations are handled via closures - rather than your method returning the data, you pass a closure to it, accepting the parameters which are returned in your version of the code - from the closure invoked at completion of dataTaskWithURL, you call that completion handler closure, providing the proper parameters:

class func MakeGetRequest(urlString: String, completionHandler: (data: NSData, error: NSError) -> Void) -> Void
{
let url = NSURL(string: urlString)
var dataResponse: NSData
var err: NSError

let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
completionHandler(data: data, error: error)
})

task.resume()
}

Swift 5 update:

class func makeGetRequest(urlString: String, completionHandler: @escaping (Data?, Error?) -> Void) -> Void {
let url = URL(string: urlString)!
var dataResponse: Data
var err: NSError

let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, respone, error) -> Void in
completionHandler(data, error)
})

task.resume()
}

Return value for function inside a block

You can't use the completion Block to create a return value for your method. The AFJSONRequestOperation does its work asynchronously. someFunction is going to return while the operation is still working. The success and failure Blocks are how you get resulting values where they need to go.

One option here is to pass in the caller as an argument to your wrapper method so that the completion Block can hand the array off.

- (void)goFetch:(id)caller
{
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {

[caller takeThisArrayAndShoveIt:[JSON valueForKey:@"posts"]];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {}
}

You could also make your caller create and pass a Block to be run on success. Then goFetch: no longer needs to know what properties exist on the caller.

- (void)goFetch:(void(^)(NSArray *))completion
{
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if( completion ) completion([JSON valueForKey:@"posts"]);
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {}
}

Returning a value from a block in class method

You are calling asynchronous method, so you cannot return the value immediately, but rather you want to adopt the asynchronous completion block pattern:

+ (void) photoCountWithCompletionHandler:(void (^)(NSInteger count, NSError *error))completionHandler {
NSParameterAssert(completionHandler);

PFQuery *photoQuery = [PFQuery queryWithClassName:@"PhotoContent"];
[photoQuery whereKey:@"usr" equalTo:[PFUser currentUser]];
[photoQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (objects) {
completionHandler([objects count], nil);
} else {
completionHandler(-1, error);
}
}];
}

And then when you call it, it would be something like:

[MyClass photoCountWithCompletionHandler:^(NSInteger count, NSError *error) {
if (error) {
// handle the error here
NSLog(@"photoCountWithCompletionHandler error: %@", error);
self.textLabel.text = @"?";
} else {
// use `count` here
self.textLabel.text = [NSString stringWithFormat:@"%ld", (long) count];
}
}];

// do not use `count` here, as the above block is called later, asynchronously

How can you return objects from methods that use blocks to retrieve data?

There are two options.

No. 1, and I don't recommend this, is to wait for the block to finish and the return the value. Like I said, you should avoid this unless you REALLY know what you are doing, for this reason, I'm not going to go into details on it's implementation.

No. 2 is to not return the value, but to handle it with a completion block. Basically, you make your function return void, and add a block parameter with an object argument. A call to this method would look a lot like the call to downloadData.

Let's say your method now looks like this -(void)getDataFromWebWithCompletion:(CompletionBlock)block, at the end of the block for downloadData, you would do something like this:

if(block){
block(dataArray)
}

Returning from method inside a @synchronized block

It's fine. @synchronized is aware of the return statement.

(Ref: http://www.thaesofereode.info/clocFAQ/#sync-advs) - dead link

(Ref: http://nextstep.sdf-eu.org/clocFAQ/#sync-advs) - this link reference above dead one and may not be up to date as its header says

Calling a method inside using block

The compiler will turn your first code into

SomeDisposable someDisposable = new SomeDisposable(1);
try
{
}
finally
{
if (someDisposable != null)
{
((IDisposable)someDisposable).Dispose();
}
}

It will turn the second code into

SomeDisposable someDisposable2 = Get(1);
try
{
}
finally
{
if (someDisposable2 != null)
{
((IDisposable)someDisposable2).Dispose();
}
}
public SomeDisposable Get(int x)
{
return new SomeDisposable(x);
}

(Source)
As you can see, it doesn't make a difference. The finally block will be executed the very same way in both variants.



Related Topics



Leave a reply



Submit