This is the 20th day of my participation in the August More Text Challenge
The classification of the block
There are three types of blocks in iOS:
NSGlobalBlock
Global block:- Located in the global zone;
- in
block
Do not use external variables internally, or just use static and global variables;
NSMallocBlock
: heap block- Located in the heap area;
- in
block
Internally use local variables orOC
Property, and assign to a strong reference;
NSStackBlock
: the stack block- Located in stack area;
- with
NSMallocBlock
Again, you can use local variables internally orOC
Properties. But it cannot be assigned to strong references orCopy
Modified variable;
The operation of the block
NSGlobalBlock
: global block, internal without external variables
NSMallocBlock
: heap blocks, which use variables internallya
NSStackBlock
: stack block, used__weak
forA weak reference
(Default is strong reference)
Note that the block holds the memory space ^{};
Block operation reference count
When we use blocks, we often have problems with circular references, which can lead to memory leaks, and the view can’t release the dealloc. So we need to be familiar with how different blocks hold variables;
Resolution:
- 1.
[NSObject new]
After that, its reference count is1
; I believe that this step printing everyone should be no objection; - 2,
strongBlock
print3
I think it’s a surprise. Why is that?- First of all:
strongBlock
Will captureobjc
, thus resulting inobjc
Reference count of+ 1
; - But because
strongBlock
Internally usedobjc
, and by defaultStrong reference
, sostrongBlock
Is aPile of the block
; Due to thestrongBlock
Is aNon-global blocks
, then when the operation is performedblock
Open up a memoryresult
And then the originalblock
That isablock
thesize
.invoke
.flags
andisa
All member variables are assigned toresult
One serving is equivalent to a quarteraBlock
So primitiveblock
I made a copy forresult
This opens up new memory spaceblock
; So at this timea
Because thestrongBlock
Strong hold, will putThe stack
I made a copy of my memoryThe heap
Up, then its reference count should also be again+ 1
So it ends up being3
; This part of the analysis is available inblock
See in the underlying source code:
- First of all:
- 3,
weakBlock
Because of using__weak
Embellish, then he’s aStack block
.Stack block
Memory copy operations are not performed. Its reference technique can only+ 1
for4
; - 4,
weakBlock
A copy is placed under the namemallocBlock
theHeap block
On, so reference counts again+ 1
for5
;
Here, in fact, the second step can be regarded as the combination of the third step and the fourth step; The second step decomposition is followed by the third and fourth steps of operations;
Memory copy of a block
In order to facilitate the manipulation of block member variables, we rewrite the structure of a block according to the system block; So we can force the block of the system and then manipulate the member variables;
Let’s look at a piece of code:
_LGBlock
We rewrote itblock
The structure of thestruct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;
The system ofblock
Cast to_LGBlock
;blc->invoke = nil;
Here we want to argue a point: after we strongblc
andweakBlock
Is it the same pieceThe stack
Memory space? If so, yesblc
theinvoke
for=nil
Operation, thenweakBlock
Will also be modified, will not be able to call;void(^strongBlock1)(void) = strongBlock;
Because we putstrongBlock
The declaration is an ID, written here to give strongBlock the properties of a block, and can be called with strongBlock(); StrongBlock (^strongBlock1)(void) __strong strongBlock = weakBlock;
Let’s look at the code execution result:
The code crashes, indicating that the invoke is empty and the block cannot be found.
After making some changes to the code, let’s run it again:
After copy operation, strongBlock memory will be copied from the stack to the heap, so strongBlock and weakBlock are not the same memory;
At this point, we should have some knowledge of stack blocks and heap blocks;
Release of the block stack
Let’s move on to a piece of code execution:
- because
strongBlock
Local variables are used, but are used__weak
forA weak reference
, sostrongBlock
Is aStack block
And thenstrongBlock
Assigned toweakBlock
, so at this timeweakBlock
Points to theStack block
Memory space, whilestrongBlock
Is released after the block is executed; butweakBlock
The life cycle of theblockDemo3
The entire code area of So althoughstrongBlock
Most have been released, but becauseweakBlock
It points to its memory space, and it still prints normally1 0
;
The code is executed as follows:
Let’s change the code slightly to remove strongBlock’s __weak and see the result:
Crash straight!! According to???
Resolution:
- Remove the
__weak
thestrongBlock
Become theHeap block
, its life cycle is the code block area; strongBlock
Assigned toweakBlock
And theweakBlock
Is aHeap block
, so willstrongBlock
The pointer toweakBlock
, they both have the same pointer address; When out of thestrongBlock
After the scope ofstrongBlock
Will be released, thenweakBlock
The memory space pointed to will not exist, so it will crash;
As shown in the figure:
A circular reference to a block
- Can release normally
- The circular reference could not be released
Let’s look at this code:
UIView holds a block, and the block captures self, so it doesn’t make a circular reference;
Self holds the block, which in turn captures self, resulting in a retain cycle, resulting in a circular reference;
So how do we solve this circular reference?
Weak typeof(self) weakSelf = self; To solve circular references;
Weak Typeof (self) weakSelf = self; And you can get away with it once and for all? Let’s look at another piece of code:
There is a task in the block that is delayed by 2 seconds, we stay in the current screen for more than 2 seconds and when we pop back to the parent screen, everything is fine;
What if we go back in 2 seconds?
As you can see, if we go back to the parent interface quickly, after dealloc is executed, the name value is not available; Because weakSelf has been released, nil sends messages that are nil;
So how to solve it? We continue to modify the code as follows:
At this point, even if we quickly return to the superior interface, dealloc execution, name can print normally; __weak typeof(self) weakSelf = self; And __strong typeof(weakSelf) strongSelf = weakSelf; Combined use can avoid data loss;
StrongSelf is a temporary variable that is released after the scope completes execution; WeakSelf can be set to nil automatically;
Is there any other way to avoid circular references?
We modify the code as follows:
At this point, even if we quickly return to the superior interface, dealloc execution, name can print normally;
Resolution:
vc
Is a temporary variable, rightself
To hold; Its life cycle is only inNSLog
Before it makes sense, then proceed=nil
;- If you don’t
=nil
Operation; thenself
holdblock
.block
holdvc
.vc
And holdself
, eventually leading to circular references;
In addition, we can also solve the circular reference in the way of parameters;
So let’s change the definition of block, typedef void(^KCBlock)(void); Typedef void(^KCBlock)(ViewController *); , the code is modified as follows:
Normal operation, can be released;
- because
vc
It’s passed as an argument, so it’s not going to beblock
Capture;
Block loop quotes interview questions
The interview questions a
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
Copy the code
Does the above method have a circular reference? The answer is that circular references happen!!
WeakSelf is a weak reference, so why can’t it be freed due to a memory leak?
Resolution:
WeakSelf and self are the same piece of memory space, which is a mapping relationship. This can be verified by printing:
So staticSelf actually holds self, and staticSelf is a global static variable, so self cannot be freed;
The interview questions 2
@property (nonatomic.copy) KCBlock doWork;
@property (nonatomic.copy) KCBlock doStudent;
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@ "% @", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
Copy the code
This method still causes memory leaks!
Resolution:
The problem with this approach is that
weakSelf.doStudent = ^{
NSLog(@"doStudent:%@", strongSelf);
};
weakSelf.doStudent();
Copy the code
StrongSelf is captured by doStudent, so strongSelf’s reference count is +1; So after the current code block is executed and the strongSelf reference count is -1, its reference count is still not 0; So it can’t be released;