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
Scrolling Issue on Position Fixed Element on Ios
Uialertview Addsubview in Ios7
Having a Uitextfield in a Uitableviewcell
Importing Commoncrypto in a Swift Framework
How to Intercept Touches Events on a Mkmapview or Uiwebview Objects
Class Does Not Implement Its Superclass'S Required Members
How to Count Occurrences of an Element in a Swift Array
Uploading File With Parameters Using Alamofire
Returning Method Object from Inside Block
What Is "Constrain to Margin" in Storyboard in Xcode 6
Uilabel Sizetofit Doesn't Work With Autolayout Ios6
How to Detect First Time App Launch on an Iphone
How to Change the Background Color of a Uibutton While It's Highlighted
How to Resize the Uiimage to Reduce Upload Image Size
How to Trigger a Block After a Delay, Like -Performselector:Withobject:Afterdelay:
Swift - Json Error: the Data Couldn'T Be Read Because It Isn'T in the Correct Format