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:

  • NSGlobalBlockGlobal block:
    • Located in the global zone;
    • inblockDo not use external variables internally, or just use static and global variables;
  • NSMallocBlock: heap block
    • Located in the heap area;
    • inblockInternally use local variables orOCProperty, and assign to a strong reference;
  • NSStackBlock: the stack block
    • Located in stack area;
    • withNSMallocBlockAgain, you can use local variables internally orOCProperties. But it cannot be assigned to strong references orCopyModified variable;

The operation of the block

  • NSGlobalBlock: global block, internal without external variables

  • NSMallocBlock: heap blocks, which use variables internallya

  • NSStackBlock: stack block, used__weakforA 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,strongBlockprint3I think it’s a surprise. Why is that?
    • First of all:strongBlockWill captureobjc, thus resulting inobjcReference count of+ 1;
    • But becausestrongBlockInternally usedobjc, and by defaultStrong reference, sostrongBlockIs aPile of the block; Due to thestrongBlockIs aNon-global blocks, then when the operation is performedblockOpen up a memoryresultAnd then the originalblockThat isablockthesize.invoke.flagsandisaAll member variables are assigned toresultOne serving is equivalent to a quarteraBlockSo primitiveblockI made a copy forresultThis opens up new memory spaceblock; So at this timeaBecause thestrongBlockStrong hold, will putThe stackI made a copy of my memoryThe heapUp, then its reference count should also be again+ 1So it ends up being3; This part of the analysis is available inblockSee in the underlying source code:

  • 3,weakBlockBecause of using__weakEmbellish, then he’s aStack block.Stack blockMemory copy operations are not performed. Its reference technique can only+ 1for4;
  • 4,weakBlockA copy is placed under the namemallocBlocktheHeap blockOn, so reference counts again+ 1for5;

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:

  • _LGBlockWe rewrote itblockThe structure of the
  • struct _LGBlock *blc = (__bridge struct _LGBlock *)weakBlock;The system ofblockCast to_LGBlock;
  • blc->invoke = nil;Here we want to argue a point: after we strongblcandweakBlockIs it the same pieceThe stackMemory space? If so, yesblctheinvokefor=nilOperation, thenweakBlockWill 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:

  • becausestrongBlockLocal variables are used, but are used__weakforA weak reference, sostrongBlockIs aStack blockAnd thenstrongBlockAssigned toweakBlock, so at this timeweakBlockPoints to theStack blockMemory space, whilestrongBlockIs released after the block is executed; butweakBlockThe life cycle of theblockDemo3The entire code area of So althoughstrongBlockMost have been released, but becauseweakBlockIt 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__weakthestrongBlockBecome theHeap block, its life cycle is the code block area;
  • strongBlockAssigned toweakBlockAnd theweakBlockIs aHeap block, so willstrongBlockThe pointer toweakBlock, they both have the same pointer address; When out of thestrongBlockAfter the scope ofstrongBlockWill be released, thenweakBlockThe 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:

  • vcIs a temporary variable, rightselfTo hold; Its life cycle is only inNSLogBefore it makes sense, then proceed=nil;
  • If you don’t=nilOperation; thenselfholdblock.blockholdvc.vcAnd 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;

  • becausevcIt’s passed as an argument, so it’s not going to beblockCapture;

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;