Reputation: 914
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 searchWithCompletion
executes ~10 seconds. Maybe something else still holds the manager
? Why this works correctly?
Thanks in advance.
Upvotes: 2
Views: 100
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:
In your case the first one is true.
Upvotes: 1
Reputation: 19164
There are two possible scenarios, depending on the implementation:
The completion handler might be called correctly even in case the object manager has been destroyed.
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
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
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