Preface a circular reference is when self owns a block, and the method of self is called in block. Form you have me, I have you, who can not release who the dilemma. or the solution in short, just one thing: __weak typeof (self) weakself = self;
This article explaining the use of block in iOS development must be aware of memory management issues, it is easy to create circular references. The goal of this article is to help you quickly master the skill of using block.
I believe that we all feel that using block to develop a lot of convenience, but there are many developers of block memory management is not good enough, resulting in frequent circular reference problems. For the novice, there is a circular reference, it is difficult to find, so through the leaks may not be able to detect, more important or to rely on their own analysis to infer.
Sound Scene One: The controller is passed between the block value
Now, we declare two controller classes, one called Viewcontroller and the other called Hybacontroller. Where Viewcontroller has a button that will push to hybacontroller when clicked.
First Look at Hybacontroller:
1234567 |
Disclosed a Method-(Instancetype) Initwithcallback: (Hybcallbackblock) callback; The non-public attributes, which are put out here just to tell you that Hybacontroller will be strong on this attribute reference @property (nonatomic, copy) Hybcallbackblock Callbackblock; |
Here are a few small scenarios to look at circular reference issues:
1234567891011121314151617181920 |
@interface Viewcontroller ()//reference button just to test @property (nonatomic, strong) UIButton *button;//just to test the memory problem, refer to it. In the development, there are many times we are//need to refer to another controller, so here simulation. @property (nonatomic, strong) Hybacontroller *VC; @end//Click Button-(void) GoToNext {Hybacontroller *VC = [[Hybacontroller alloc] initwithcallback:^{[Self.button sett Itlecolor:[uicolor Greencolor] forstate:uicontrolstatenormal]; }]; SELF.VC = VC; [Self.navigationcontroller PUSHVIEWCONTROLLER:VC animated:yes];} |
Now look Viewcontroller here, here in the block where the circular reference is formed, so the VC attribute is not released. Analyze the reason why it forms a circular reference (for example):
To put it simply, two rings are formed here:
- Viewcontroller-> strongly references the attribute vc-> strongly references the callback-> strong reference to the Viewcontroller
- The viewcontroller-> strongly references the attribute vc-> strongly references the Callback-> strongly referenced property of the Viewcontroller button
For this problem, we want to solve the memory loop reference problem, we can do this:
The VC attribute is not declared or the VC attribute is declared as a type of weak reference, in callback back to the mediation, the Self.button changed to Weakself.button, that is, callback this block to Viewcontroller to a weak reference. If it is changed to the following, the memory can be released normally:
12345678910 |
-(void) GoToNext {__weak __typeof (self) weakself = self; Hybacontroller *VC = [[Hybacontroller alloc] initwithcallback:^{[Weakself.button settitlecolor:[uicolor GreenColor] fo Rstate:uicontrolstatenormal]; }];//self.vc = VC; [Self.navigationcontroller PUSHVIEWCONTROLLER:VC animated:yes];} |
I tried to use leaks to detect memory leaks, but all through, a green tick, let you think the memory processing is very good, and actually the memory is not released.
For this scenario, give us some suggestions:
- Print logs in the controller's lifecycle Viewdidappear:
1234567 |
-(void) Viewdidappear: (BOOL) animated {[Super viewdidappear:animated]; NSLog (@ "Enter Controller:%@", [[Self class] description]);} |
- Print logs in the controller's lifecycle Dealloc:
12345 |
-(void) Dealloc {NSLog (@ "Controller is dealloc:%@", [[Self class] description]);} |
This way, as long as the log does not print out, indicating that the memory is not released, you need to learn to analyze the memory reference problem.
Scenario Two: Block value between Controller and view
Let's start by defining a view to interact with the controller. When the View button is clicked, it is passed to the controller by the block callback, and the corresponding data is passed to the controller to record:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546 |
typedef void (^hybfeedbackblock) (ID model); @interface hybaview:uiview -(instancetype) Initwithblock: (hybfeedbackblock) block; @end @interface Hybaview () @property (nonatomic, copy) Hybfeedbackblock block; @end @implementation hybaview -(void) Dealloc { nslog (@ "Dealloc: %@ ", [[Self class] description]);} -(Instancetype) Initwithblock: (hybfeedbackblock) block { if (self = [super init]) { self.block = Block; uibutton *button = [UIButton buttonWithType : Uibuttontypecustom]; [button settitle:@ "Feedback to Controller" Forstate:uicontrolstatenormal]; button.frame = CGRectMake (A. button.backgroundcolor); [Uicolor Redcolor]; [button Settitlecolor:[uicolor Yellowcolor] ForState: Uicontrolstatenormal]; [button addtarget:self Action: @selector (feedback) forcontrolevents:uicontroleventtouchupinside]; [self AddSubview:button]; } return self;} -(void) Feedback { if (self.block) { //Pass the model back, there is no data, assuming nil self.block (nil); }} @end |
Next look at Hybacontroller, which adds two properties, and when Viewdidload, creates the Aview property:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
@interface Hybacontroller () @property (nonatomic, copy) Hybcallbackblock callbackblock; @property ( Nonatomic, Strong) Hybaview *aview, @property (nonatomic, strong) ID currentmodel; @end @implementation hybacontroller -(Instancetype) Initwithcallback: (Hybcallbackblock) Callback { if (self = [super init] { self.callbackblock = Callback; } return self;} -(void) viewdidload { [super viewdidload]; self.title = @ "HYBAController"; self.view.backgroundcolor = [Uicolor Whitecolor]; self.aview = [[HYBAView alloc] initwithblock:^ (ID model) { //assumes to update model self.currentmodel = model; }]; //assume full screen self.aview.frame = Self.view.bounds; [self.view Addsubview:self.aview]; self.aview.backgroundcolor = [Uicolor whitecolor];} -(Void) Viewdidappear: (BOOL) Animated { [super Viewdidappear:animated]; nslog (@ " Enter controller:%@ ", [[Self class] description]);} -(void) Dealloc { nslog (@ "Controller is dealloc:%@", [[Self class] description]);} @end |
The circular reference to the previous scenario has been resolved, so the focus of this section is on the controller and view reference issues.
Analysis: As shown in:
The rings are formed by:
- VC->AVIEW->BLOCK->VC (self)
- Vc->aview->block->vc.currentmodel
The solution can be: When creating Aview, the reference to Currentmodel in the block is changed to a weak reference:
1234567 |
__weak __typeof (self) weakself = Self;self.aview = [[Hybaview alloc] initwithblock:^ (ID model) {//Suppose to update model Weaks Elf.currentmodel = model;}]; |
I've seen a lot of code like this, directly using member variables instead of attributes, and then they thought that it would not refer to self, which is the controller, thus not forming a ring:
123456 |
Self.aview = [[Hybaview alloc] initwithblock:^ (ID model) {//Suppose to update model _currentmodel = model;}]; |
This is a false understanding that when we refer to _currentmodel, it is a member variable of the controller and therefore references the controller. To resolve this issue, you also want to change to a weak reference:
1234567 |
__block __weak __typeof (_currentmodel) Weakmodel = _currentmodel;self.aview = [[Hybaview alloc] initWithBlock:^ (ID model {//Assume to update model Weakmodel = model;}]; |
Here also add __block Oh!
Simulating a circular reference
Suppose the following code is written, is there a problem with memory not being released? (where the Controller property is a strongly-referenced declaration)
12345678 |
@autoreleasepool {a *AVC = [[a alloc] init]; b *BVC = [[B allcok] init]; Avc.controller = BVC; Bvc.controller = AVC;} |
Analysis:
- Avc-> a strong reference to BVC
- Bvc-> a strong reference to AVC
If this is the case, it forms a ring. AVC->BVC->AVC, this forms the ring.
See if it forms a ring
1234567 |
Nsmutablearray *marray = [Nsmutablearray arraywithobjects:@ "a", @ "B", @ "abc", nil]; Hybacontroller *VC = [[Hybacontroller alloc] initwithcallback:^{[Marray removeobjectatindex:0];}]; [Self.navigationcontroller PUSHVIEWCONTROLLER:VC Animated:yes]; |
- VC--strong reference block
- Block-> a strong reference to MyArray
Therefore, there is no ring formed.
If the myarray is strongly referenced to the current controller, it does not form a ring:
- VC--strong reference block
- Block-> Strong reference to self (current controller)
A circular reference is formed if the VC is set to the SELF.VC attribute and is a strong reference.
Written in the last
This article is about so much, writing an article is to teach you how to analyze whether the memory is formed ring, as long as you know how to analyze the memory is a circular reference, then in the development of special attention to memory management problems, and find memory-related problems when the bug, it is also easier.
IOS Block Circular reference explaining