Returning method object from inside block

2018-12-31 20:53发布

问题:

I am wondering how to do the following correctly: I have a method that is to return an NSData object. It gets the NSData object from a UIDocument. The NSData object can get large, so I want to make sure it is fully loaded before the response starts. I would therefore like to return the value of the method from within the block itself. So something like this:

- (NSData*)getMyData {
  MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
  [doc openWithCompletionHandler:^(BOOL success) {

    if (success) {
      return doc.myResponseData; // this is to be the return for the method not the block
    }
  }];
}

This causes an error because the return apparently refers to the block\'s return.

How can I accomplish this without having to make a thread blocking wait/while loop?

Thanks.

回答1:

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)
}


回答2:

The open method is asynchronous which is why you have to provide a block to be run when the open is completed. You need to copy this and make your method also receive a block of code that you will execute when the open is finished.

You should also pass through the success argument of the call you are wrapping or create an error, you need to do this so that the calling code can take the right action.

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


回答3:

Following Are method how to declare method with completionHandler:

Objective-C

- (void)getMyDataWithCompletionHandler:(void(^)(NSString *str))completionHandler
{
    completionHandler(@\"Test\");
}

Swift-3

func showDatePicker(superc: UIViewController, completionHandler:@escaping (String) -> Void)  {
            completionHandler(\"Test\")
}