http://stackoverflow.com/questions/11168916/weak-or-strong-for-iboutlet-and-other
When a parent has a reference to a child object, you should use a strong reference. When a child has a reference to its parent object, you should use a weak reference or a unsafe_unretained one (if the former is not available). A typical scenario is when you deal with delegates.
First, let’s set up a protocol called TaskDelegate. It basically is a protocol for a parent, guardian, or whatever person that is in charge of taking care of the child. We require the guardian to be able to feed the child and bathe it.
MyProtocol.h
1 2 3 4 5 6 7 8 9 10 |
@protocol TaskDelegate <NSObject> @required -(void)feedIt; -(void)batheIt; @optional -(void)kickOut; @end |
Second, we create a Child Object with a delegate that strong references a class (Parent, Guardian,…etc) that conforms to the protocol TaskDelegate. Hence we use id because we don’t know whether it will be Parent class or Guardian class or CareTaker class or whatever. As long as it conforms to the Protocol, then we’re fine. See //1
We have methods that represents the action of the Child. Specifically, the child gets hungry. We represent that by the method gotHungry.
When that action happens, gotHungry checks to see if the delegating class of either Parent, or Guardian, or CareTaker, or whomever, can respond to the method feedIt. This method should feed our Child. If it does, then we call it. See //2
Child.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#import "MyProtocol.h" @class Parent; //The child must conform to Task, when it does, it must do Task's required , and @interface Child : NSObject { } @property(nonatomic, strong) Parent * parentPtr; @property(nonatomic, weak) id <TaskDelegate> delegate; //1 -(void)gotHungry; @end |
Child.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#import "Child.h" @interface Child () { } @end @implementation Child -(instancetype)init { if(self=[super init]) { NSLog(@"Child.m (init) - init"); } return self; } -(void)gotHungry { NSLog(@"Child.m - I'm HUNGRY! whoever is taking care of this child (delegate) gots to feed it!"); //2 if([self.delegate respondsToSelector:@selector(feedIt)]) { NSLog(@"Child.m - Yup, this guardian (or parent) can feed"); [self.delegate feedIt]; } } -(void)dealloc { NSLog(@"Child.m - I'm dealloc.."); } @end |
So create a class called Parent. This Parent conforms to protocol, so hence it can take care of the Child. It creates that Child object in its init method using a strong reference. This is standard..just like how in UIViewController’s viewDidLoad, you’d often see UI objects being created, then assigning that UI’s delegate to self…which then the UIViewController must conform to.
In the same way, our Parent creates a Child object. Assigns the Child delegate to self because it conforms to the Child’s delegate.
Parent.h
1 2 3 4 5 6 |
#import "MyProtocol.h" @class Child; @interface Parent : NSObject {} -(instancetype)initWithChild:(Child*)someKid; @end |
Parent.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import "Parent.h" #import "Child.h" @interface Parent () <TaskDelegate> { } @property(nonatomic, strong) Child * childPtr; @end @implementation Parent -(instancetype)initWithChild:(Child*)someKid { if(self=[super init]) { NSLog(@"Parent.m (init) - init"); self.childPtr = someKid; //child has weak reference to Parent self.childPtr.delegate = self; } return self; } -(void)feedIt { NSLog(@"Parent.m - shoves baby food into child's mouth"); } -(void)batheIt { NSLog(@"Parent.m - Throw it into bathtub"); } -(void)dealloc{ NSLog(@"Parent.m - I'm dealloc..."); self.childPtr = nil; //retain count of 0 } @end |
1) Finally we are able to use our delegate. We have a Parent who creates (or strong references a Child Object) in its init method.
2) When the child got hungry, it delegates a task to the Parent object. Then you’ll see that the Parent feeds it.
3) Let’s say the Parent needs to take a vacation from this program of taking care of its kid and leaves. So we nil the parent, and ARC garbage collects it. Because the Child’s delegate is weak, it will then be set to nil when the Parent Object gets deallocated.
4) you’ll see that the Child’s delegate then will be set to nil. It can be set to another Object that conforms to TaskDelegate later…such as if we decide to create a Nanny or Guardian object so that they can feed or bathe the Child.
main.m
1 2 3 4 5 6 7 |
Child * brat = [[Child alloc] init]; Parent * fumu = [[Parent alloc] initWithChild:brat]; //1 [brat gotHungry]; //2 fumu=nil; //3 NSLog(@"Child object's delegate %p", brat.delegate); //4 brat=nil; |
Result:
2015-09-11 10:12:34.144 RetainCycleEx[68141:1785252] Child.m (init) – init
2015-09-11 10:12:34.145 RetainCycleEx[68141:1785252] Parent.m (init) – init
2015-09-11 10:13:05.141 RetainCycleEx[68141:1785252] Child.m – I’m HUNGRY! whoever is taking care of this child (delegate) gots to feed it!
2015-09-11 10:13:05.141 RetainCycleEx[68141:1785252] Child.m – Yup, this guardian (or parent) can feed
2015-09-11 10:13:05.142 RetainCycleEx[68141:1785252] Parent.m – shoves baby food into child’s mouth
2015-09-11 10:13:06.012 RetainCycleEx[68141:1785252] Parent.m – I’m dealloc…
2015-09-11 10:13:06.759 RetainCycleEx[68141:1785252] Child object’s delegate 0x0
2015-09-11 10:13:08.956 RetainCycleEx[68141:1785252] Child.m – I’m dealloc..
(lldb)
The delegate Retain Cycle!!
main.m
1 2 3 4 5 6 7 8 9 |
Child * brat = [[Child alloc] init]; Parent * fumu = [[Parent alloc] initWithChild:brat]; [brat gotHungry]; fumu=nil; //nils Parent. But ARC won't garbage collect Parent because of Child's strong delegate reference to Parent. This was assigned in Parent's init method. NSLog(@"Child object's delegate %p", brat.delegate); //weak delegate, so when Parent is gone, the child's delegate should be nil brat=nil; //nils Child. But because in Parent's init method, parent has strong reference to Child. |
In order to create a retain cycle, the Child object can changes it delegate from weak to strong like so:
1 |
@property(nonatomic, strong) id <TaskDelegate> delegate; |
Thus, when we run main.m we create a Child, assigns the Child to a Parent, Child gets hungry, Parent feeds it. Very standard.
When the Parent gets niled, ARC doesn’t send dealloc to Parent because child’s delegate is strong and is referencing the Parent. And because Parent is not dealloc’ed, then it will never nil the objects that it may own. In our case, the parent strong references the Child and own it, thus, it will never dealloc that Child.
Hence, the Child and Parent both do not get dealloc’ed and thus is forever floating in memory.
That’s why when you run the code, you’ll see that the dealloc of Parent is never called and thus Child’s dealloc will never be called…and that the Child’s delegate to its Parent still shows a valid Object address.:
2015-09-11 10:41:51.695 RetainCycleEx[69880:1794099] Child.m (init) – init
2015-09-11 10:41:51.696 RetainCycleEx[69880:1794099] Parent.m (init) – init
2015-09-11 10:41:54.587 RetainCycleEx[69880:1794099] Child.m – I’m HUNGRY! whoever is taking care of this child (delegate) gots to feed it!
2015-09-11 10:41:54.587 RetainCycleEx[69880:1794099] Child.m – Yup, this guardian (or parent) can feed
2015-09-11 10:41:54.587 RetainCycleEx[69880:1794099] Parent.m – shoves baby food into child’s mouth
2015-09-11 10:41:55.659 RetainCycleEx[69880:1794099] Child object’s delegate 0x1003003d0