kv0
kv0

Reputation: 914

Passing block to a method

Is it correct to pass completion block from one method to another one which performs long operation:

- (void)searchWithCompletion:(void (^)(NSString* result))completion {
    SearchManager *manager = [[SearchManager alloc] init]; // only one reference here
    [manager search:^( NSString *name){ // it takes 3-5 seconds to execute
        NSString *str = [NSString stringWithFormat@"Search result: %@", name];
        completion (str);
    }];
}

And then call it like this

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[MyManager sharedInstance] searchWithCompletion:^( NSString * result){

        NSLog(@"foo");
    }];
}

From my understandings manager can be destroyed as there is no reference to it and its completion block will be never called. But after some attempts I figured out that this code always works fine, I can see 'foo', even if searchWithCompletionexecutes ~10 seconds. Maybe something else still holds the manager? Why this works correctly?

Thanks in advance.

Upvotes: 2

Views: 100

Answers (4)

newacct
newacct

Reputation: 122518

From a memory management point of view what you're doing is correct. In both ARC and MRC, manager would be released at the end of the method, because you are no longer using it.

As to what happens to the asynchronous operation, that entirely depends on how the SearchManager is designed, which you cannot tell from the outside. A "search manager" probably internally performs some kind of asynchronous operation, which somehow calls back to the manager when it's done (who in turn calls your completion handler), which means the asynchronous search operation must have some kind of reference to the search manager (possibly through a completion handler). There are two possibilities:

  • The search operation (or the completion block passed to it by the manager) has a strong reference to the search manager. In this case, the search manager will automatically stay alive until the operation is done.
  • The search operation (or the completion block passed to it by the manager) has a weak reference to the search manager. In this case, if you don't keep a reference to the search manager, it will be deallocated, and the search operation will be effectively "cancelled" (well, the underlying operation might still finish, but it will call back to a nil'ed reference, so it probably won't do anything). If this is the case, you would have to keep a reference to it.

In your case the first one is true.

Upvotes: 1

CouchDeveloper
CouchDeveloper

Reputation: 19164

There are two possible scenarios, depending on the implementation:

  1. The completion handler might be called correctly even in case the object manager has been destroyed.

  2. The object manager might be retained in method search and released immediately before (or after) the completion handler gets called.

For example, NSOperation will behave like #2.

For case #1 one could imagine, that SearchManager search sets up a NSOperation with a completion handler. Once the operation has been started, it is strictly not needed anymore and can be destroyed.

Upvotes: 0

Brandon
Brandon

Reputation: 2427

Without seeing the code inside your search function it's hard to tell. However the most likely reason that manager isn't being released before the completion block is called is because of lexical scope. It's the same reason that manager isn't being released immediately after it is created in the first line of searchWithCompletion. In your case if manger is being released it's being done at the very end of the searchWithCompletion method call, after you block has executed. Your search method very likely looks something like

- (void)search:(void (^)(NSString *name))completionBlock;
{
    //do some searching

    if (completionBlock)
    {
        completionBlock(@"joe");
    }
}

This is assuming that your not doing anything like storing the block as a property or using some other sort of asynchronous mechanism. However if you are then remember that blocks can be copied and passed around just like any other variable. That means that if search passes it off to some asynchronous function then that function can take over ownership of the block and allow the manager object to go out of scope.

Upvotes: 1

Rafał Sroka
Rafał Sroka

Reputation: 40038

To tell more you should post all initialisation related code of SearchManager. However, if SearchManager is a singleton then it will not be released until the app terminates.

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object.

That would explain why the manager object is not being released.

Upvotes: 0

Related Questions