First, preface
The Block and dispatch Quene of cocoa multithreaded programming in the comprehensible language
The source of this article download: Click here to download
Second, block precautions
1,block, when implemented, makes a read-only copy of the stack variable that it refers to in the method it is defined in, and then uses that read-only copy within the block blocks.
The following code:
-(void) testaccessvariable{ nsinteger outsidevariable = ten; __block Nsinteger outsidevariable = ten; Nsmutablearray * Outsidearray = [[Nsmutablearray alloc] init]; void (^blockobject) (void) = ^ (void) { Nsinteger insidevariable =; Kslog (@ " > member variable =%d", self.membervariable); Kslog (@ " > Outside variable =%d", outsidevariable); Kslog (@ " > Inside variable =%d", insidevariable); [Outsidearray addobject:@ "Addedinsideblock"]; }; Outsidevariable = +; Self.membervariable = +; Blockobject (); Kslog (@ " >%d items in Outsidearray", [Outsidearray Count]);}
The output is:
> member variable = > Outside variable = ten > Inside variable = > 1 items in Outsidearray
Did you notice? The output value of the outside variable is 10, although the outside variable is modified to 20 in the method testaccessvariable where the block is defined after the block is defined. The rule here is that blockobject will make a read-only copy of the outside variable when it is implemented, and use that read-only copy within block blocks. So the output here is the value of the variable at copy 10. If we want Blockobject to modify or synchronize with the outside variable, we need to use __block to decorate the outside variable.
__block Nsinteger outsidevariable = 10;
Attention:
A), in the block above, we added a value to the Outsidearray array, but did not modify the Outsidearray itself, which is allowed because the copy is Outsidearray itself.
b), for static variables, global variables, there are read and write permissions in the block because, in the internal implementation of the block, pointers to these variables are copied.
c), the internal implementation of the __block variable is much more complex, the __block variable is actually a struct object, and the copy is a pointer to the struct object.
2, the non-inline block cannot access self directly, only by passing self as a parameter to the block, and self can only access its properties through setter or getter methods, and cannot use a period method. However, inline block is not subject to this limitation.
typedef nsstring* (^inttostringconverter) (id self, nsinteger paraminteger);-(NSString *) Convertinttostring: ( Nsinteger) Paraminteger Usingblockobject: (inttostringconverter) paramblockobject{return Paramblockobject (self, Paraminteger);} typedef nsstring* (^inttostringinlineconverter) (Nsinteger Paraminteger);-(NSString *) Convertinttostringinline: ( Nsinteger) Paraminteger Usingblockobject: (inttostringinlineconverter) paramblockobject{return ParamBlock Object (Paraminteger);} Inttostringconverter Independentblockobject = ^ (id self, nsinteger paraminteger) {kslog (@ ' >> self%@, Membervar Iable%d ", self, [self membervariable]); NSString *result = [NSString stringwithformat:@ "%d", Paraminteger]; Kslog (@ ">> independentblockobject%@", result); return result;}; -(void) testaccessself{//Independent//[self convertinttostring:20 usingblockobject:independentblockobject]; Inline//InttostringinlineconverterInlineblockobject = ^ (Nsinteger paraminteger) {Kslog @ ' >> self%@, membervariable%d ', self, Self.membervar iable); NSString *result = [NSString stringwithformat:@ "%d", Paraminteger]; Kslog (@ ">> inlineblockobject%@", result); return result; }; [Self convertinttostringinline:20 usingblockobject:inlineblockobject];}
3, use Weak–strong dance technology to avoid circular references
In the second article, I mentioned that inline block can directly refer to self, but be very careful to refer to self in the block. Because the self is referenced in some inline blocks, it may cause circular references. As shown in the following example:
@interface Ksviewcontroller () { id _observer;} @end @implementation ksviewcontroller-(void) viewdidload{ [Super Viewdidload]; Additional setup after loading the view, typically from a nib. Kstester * tester = [[Kstester alloc] init]; [Tester run]; _observer = [[Nsnotificationcenter defaultcenter] addobserverforname:@ "Testnotificationkey" Object:nil Queue:nil usingblock:^ (nsnotification *n) { NSLog (@ "%@", self); }];} -(void) dealloc{ if (_observer) { [[Nsnotificationcenter Defaultcenter] removeobserver:_observer]; }}
In the code above, we add an observer to the notification hub and then release the registration at Dealloc, and everything looks normal. But here are two questions:
A) in the message notification block refers to self, where the Self object is block retain, and _observer retain a copy of the block, Notification Center and hold _observer. So as long as the _observer object has not been de-registered, the block will be held by the Notification center, so that self will not be freed and its dealloc will not be invoked. And we expect to removeobserver in Dealloc to remove the notification center from the _observer/block retain.
b) At the same time, _observer is defined as an assignment in the Self's class, and therefore is self retain, thus forming a circular reference.
The above procedure a) deserves an in-depth analysis:
The block variables in AddObserverForName:object:queue:usingBlock: In Apple's official document are described below:
The block is copied by the Notification center and (the copy) held until the observer registration is removed.
Therefore, the notification center copies the block and holds the copy until the _observer registration is released. In ARC, the block will retain self, either directly referencing self or by referencing the member variable of self, in the block being copied.
These two problems can be solved with Weak–strong dance technology. This technique was introduced in the WWDC: WWDC Session #322 (objective-c advancements in Depth)
__weak Ksviewcontroller * wself = self; _observer = [[Nsnotificationcenter defaultcenter] addobserverforname:@ "Testnotificationkey" Object:nil Queue:nil usingblock:^ (nsnotification *n) { Ksviewcontroller * sself = wself; if (sself) { NSLog (@ "%@", sself); } else { NSLog (@ <self> Dealloc before we could run this code. "); } ];
Let's analyze why this technique works.
First, a weak reference to self is defined before the block wself, because it is a weak reference, so wself becomes nil when Self is freed, and then references the weak application in the block, considering the multithreaded case, by using a strong reference sself to reference the weak reference, as If self is not nil, self is retain to prevent self from being freed during subsequent use, and the strong reference sself is then used in subsequent block blocks, noting that the sself is nil-detected before use because a weak reference to WSE in a multithreaded environment LF The sself is assigned a strong reference, the weak reference wself may already be nil.
In this way, the block does not hold the self reference, thus breaking the circular reference.
Extensions: Others need to be aware of where to avoid circular references
This is similar to the case with Nstimer. Apple's official document mentions "Note in particular this run loops retain their timers, so can release a timer after you had added it to a R Un loop, while on the interface
+ (Nstimer *) Scheduledtimerwithtimeinterval: (nstimeinterval) seconds target: (ID) Target selector: (SEL) Aselector UserInfo: (ID) UserInfo repeats: (BOOL) repeats
The target description in the document mentions:
The object to which to send the message specified by Aselector when the timer fires. The target object is a retained by the timer and released while the timer is invalidated.
In combination with these two documents, we know that as long as the repetitive timer has not been invalidated,target the object will be held and will not be released. So when you use self as target, you can't expect to invalidate a timer in Dealloc because Dealloc will never be called until the timer is invalidate. Therefore, it is necessary to find a suitable time and place to invalidate the timer, but not in the dealloc.
4,block Memory Management Analysis
Block is also a NSObject object, and in most cases the block is allocated on top of the stack, and the block is allocated on the global data segment only if the block is defined as a global variable or if no automatic variable is referenced in block blocks. The __block variable is also allocated on top of the stack.
Under ARC, the compiler automatically detects that we have handled most of the block's memory management, but when the block is treated as a method parameter, the compiler does not automatically detect it and requires us to manually copy the Block object. Fortunately, most of the names in the Cocoa library contain the "Usingblock" interface and the GCD interface has been copied inside its interface, and we do not need to handle it manually. But beyond that, we need to intervene manually.
-(ID) getblockarray{ int val = ten; return [[Nsarray alloc] initwithobjects: ^{kslog (@ " > Block 0:%d", Val);}, //block on the stack ^{K SLog (@ " > Block 1:%d", Val);}, //block on the stack nil]; return [[Nsarray alloc] initwithobjects:// [^{kslog (@ " > Block 0:%d", Val);} copy], //block Copy to heap// [^{kslog (@ " > Block 1:%d", Val);} copy], //block copy to heap// nil];} -(void) testmanageblockmemory{ id obj = [self getblockarray]; typedef void (^blocktype) (void); Blocktype Blockobject = (blocktype) [obj objectatindex:0]; Blockobject ();}
Executing the above code, when calling Testmanageblockmemory, the program will crash out. Because the block returned from Getblockarray is allocated on the stack, but beyond the scope where the block is defined, the block is gone. The correct approach (The block code) is to copy the block to the heap when it is added to the Nsarray, so that it can be accessed normally in later use.
Under ARC, copying a block variable is always safe, whether it is on a stack, a global data segment, or has been copied to a heap. Copy the block on the stack to copy it to the heap, and copy the block in the global data segment without any effect; Copy the block on the heap only increases its reference count.
If a variable of type __block is referenced in a block on the stack, the __block variable is copied onto the heap when the block is copied to the stack if the __block variable does not have a corresponding copy on the heap, the reference count of the corresponding copy on the heap is incremented.
IOS Block Trap parsing