I have an object that declared inside a function. this object makes a function call which has a completion block. the function executes properly.
This function makes a network call (which is in another class). after getting the result from network call, I am making a check to see if the class is still in memory (using weakSelf and strongSelf)
during this check, it shows, the self is nil.
I know if I use a class method or property variable I can resolve this. but is there any way to retain this object (which declared inside a function). I tried __strong and __block with the object, but not working.
Here is my code
@implementation AViewController {
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self methodA];
}
-(void)methodA {
LocalClass *object = [LocalClass alloc] init];
[object aMethodWithCompletionBlock:^(NSDictionay *result) {
}];
}
}
@implementation LocalClass {
- (void)aMethodWithCompletionBlock:(void (^)(NSDictionay *result))completionHandler {
__weak typeof(self) weakSelf = self;
[NetworkClass methodToMakeRESTRequestOnComplete:^(NSDictionay *someResult) {
__strong typeof(self) strongSelf = weakSelf;
if(!strongSelf) { // this check fails as strongSelf is nil
return;
}
//some code execution
if (completionHandler != nil) {
completionHandler(someModifiedResult);
}
}];
}
}
I know if I use a class method or property variable I can resolve this. but is there any way to retain this object (which declared inside a function). I tried
__strong
and__block
with the object, but not working.
Your code is trying to solve a problem which doesn't exist and it doing so creates one.
The purpose of the weakSelf
/strongSelf
pattern it to deal with harmful reference cycles (not all reference cycles are harmful, indeed some are useful). You should only use it if you have identified you that there is such a harmful cycle.
Let's look at your code without any weakSelf
/strongSelf
dance:
1 @implementation AViewController
2 {
3 -(void)methodA
4 { LocalClass *object = [LocalClass alloc] init];
5 [object aMethodWithCompletionBlock:^(NSDictionay *result) { ... }];
6 }
7 }
8
9 @implementation LocalClass
10 {
11 - (void)aMethodWithCompletionBlock:(void (^)(NSDictionay *result))completionHandler
12 { [NetworkClass methodToMakeRESTRequestOnComplete:^(NSDictionay *someResult)
13 {
14 if (!self)
15 return;
16
17 //some code execution
18 if (completionHandler != nil)
19 completionHandler(someModifiedResult);
20 }];
21 }
22 }
Now what will happen when you have an instance of AViewController
and invoke methodA
?
LocalClass
is created and a reference to it stored in object
. The type of object
is implicitly __strong LocalClass *
and so the new instance has a strong reference to it and will stay alive.aMethodWithCompletionBlock:
is called on the object referenced by object
passing it a closure. Note that neither methodA
or the instance of AViewController
it is being called for keep a reference to this closure, it is just passed to the method. Therefore after the call there is no possibility of a reference cycle between a local variable belonging to methodA
or an instance variable belonging to AViewController
and the closure.At line 12 method methodToMakeRESTRequestOnComplete:
of NetworkClass
is called passing it a closure
self
so it contains a strong reference back to the instance of LocalClass
on which aMethodWithCompletionBlock:
was calledLocalClass
that was created at Line 3 and so there are now two strong references to that object.completionHandler
At line 20 methodToMakeRESTRequestOnComplete:
returns, as the passed block is a completion block it is unlikely to have been called yet. So at this point NetworkClass
has a reference to that completion block, and the completion block has a reference to the LocalClass
instance.
aMethodWithCompletionBlock:
returns. The instance of LocalClass
it was called on has kept no references to the parameter completionHandler
.methodA
returns. This destroys its local variable object
which drops the strong reference to the LocalClass
instance it referenced. The system can consider destroying that instance at this point, however as NetworkClass
has a strong reference to a completion block which in turn has a strong reference to this same LocalClass
instance it is still needed and not destroyed.NetworkClass
invokes the block reference it kept. The self
variable contains a strong reference to the LocalClass
instance originally created at line 4, that instance therefore still exists and all is well with the world.NetworkClass
drops its strong reference to the block the block can be (probably – assuming there are no other strong references to it) destroyed. That destruction removes the strong references the block has to the objects referenced by its self
and completionHandler
and so those objects can also (probably...) be destroyed and the objected originally created at line 4 bites the dust.There are no harmful cycles, no need for any weak references to manage them.
HTH