Front extremely burn brain, suggest -> like to see again


  • I remember when we first metBlockThe first feeling is that the grammar is strange, only to know that this is the right way to write, and then confused for a period of time, then found in theiOSIn,BlockFrequent use, such as the official API used a lotBlockDo things back and forth. After a long period of use and studyBlockThere’s more to it than that.
  • So here is a summary of what I have learned aboutBlockAll knowledge points, after all, a good memory is not as good as bad writing, writing down memory will be more profound and the process of writing will have more thinking.

I’ll cover blocks in the following ways

  • BlockThe definition of
  • BlockBasic use of
  • BlockThe underlying data structure of
  • BlockThe type of
  • BlockCapture variable mechanism
  • __BlockWhat exactly does the modifier do?
  • BlockMemory management
  • BlockA circular reference
  • BlockThe exchange
  • BlockRelated interview questions
  • .

The definition of the Block

Blocks is an extension of THE C language. The extension of Blocks can be expressed in one sentence: anonymous functions with automatic variables (local variables).

As the name implies, an anonymous function is a function without a name. — From iOS and OS X Multithreading and Memory Management

That is, Blocks are similar to closure functions in some languages. Here is the syntax declaration for Blocks

Return value type (^ variable name)(argument list) = ^ Return value type (argument list) expression

I’ll write it in code

void (^block)(void) = ^void (void){};
Copy the code

The return value type and parameter type on the right can be omitted if they are empty

void (^block)(void) = ^{};
Copy the code

Of course, we can also use the typedef property to define a Block

typedef void (^block)(void);
Copy the code

This makes it easier to use. For example, AFNetworking, a third-party networking framework, makes extensive use of blocks in this way

typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef id (^AFURLSessionTaskAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, void (^completionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential));
Copy the code

The above excerpt is from afurlssession Manager in AFNetworking


Basic use of blocks

Blocks can be used as properties, parameters, return values, and so on

  • A, whenblockAs a property
@property(nonatomic, copy)  void (^NormalBlock)(void);
Copy the code

or

typedef void (^NormalBlock)(void);

@property(nonatomic, copy)  NormalBlock block;
Copy the code

This usage is most commonly used when we respond to events in the cell, sometimes it is more convenient to use blocks to call back and forth to the VC location

@interface Cell : UITableViewCell
@property(nonatomic, copy)  void (^clickBlock)(void);
@end

@implementation Cell

- (void)ClickAction{
    if(self. clickBlock){
        self. clickBlock();
    }  
}

@end

@interface VC : UIViewController

@end

@implementation VC

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    Cell *cell = [ProGoldRiceRankCell makeCellWithTableView:tableView];
    cell. clickBlock = ^{
    //do anything
    };
    return cell;
}

@end
Copy the code
  • Second, whenblockAs a parameter

Sometimes we need to return a value from a method, but it needs to be assigned after a GCD delay. This is not possible when we return a value from a GCD, because the block return type is empty.

typedef void (^NormalBlock)(NSString *value); - (void)test{ [self doSomeThingWithBlock:^(NSString *value) { NSLog(@"%@",value); }]; } - (void)doSomeThingWithBlock:(NormalBlock)block{ NSString *value = @"1"; Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), dispatch_get_main_queue(), ^{ value = @"2"; block(value); }); }Copy the code
  • Third, whenblockAs a return value

The most commonly used internal implementation of the navigation framework makes heavy use of the block return value to implement the chained call syntax

[_iconImg mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.bottom.right.mas_equalTo(0);
    }];
Copy the code

The source code for the navigation framework for iOS will be revealed in this post. For details, see the source code for the navigation framework.

This implementation of the mas_makeConstraints method looks like this, and you can see that all of the constraints that we write in our constraints code are set to MASConstraintMaker by passing a Block argument and then calling install to install all the constraints

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints =  NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install]; }Copy the code

make.top.left.bottom.right.mas_equalTo(0); Here’s what happens inside this chain call

  • By encapsulating factory classes for various constraint methodsMASConstraintMaker, first calltop
- (MASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
Copy the code
  • And then calltopThe constraint abstract class is returnedMASConstraint(What is actually returned isMASConstraintA subclass ofMASViewConstraintorMASCompositeConstraint)
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]; } - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; if ([constraint isKindOfClass:MASViewConstraint.class]) { //replace with composite constraint NSArray *children = @[constraint, newConstraint]; MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self; / / set to agents [self constraint, the constraint shouldBeReplacedWithConstraint: compositeConstraint]; return compositeConstraint; Return MASCompositeConstraint} if (! constraint) { newConstraint.delegate = self; // Set it to agent [self.constraints addObject:newConstraint]; } return newConstraint; // return MASViewConstraint type}Copy the code
  • And then call it againleft(this isMASConstraintThe method of
- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
Copy the code
  • whileMASConstraintThrough theMASConstraintMakerSet to proxy to enable the callMASConstrainttheleftMethod pass toMASConstraintMakerAnd then the proxy method returns the constraint classMASConstraintThis allows you to set multiple constraints in succession, and eventually invoke the topmost factory classMASConstraintMaker In the method
- (MASConstraint *)left { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft]; } - (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { NSAssert(! self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation"); / / call the proxy method return [self. Delegate constraint: self addConstraintWithLayoutAttribute: layoutAttribute]; }Copy the code
  • Let’s take a look atmas_equalToandoffset
- (MASConstraint * (^)(id))mas_equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}
Copy the code

Both methods are in MASConstraint, so the MASConstraint class returned after the constraint is set can be called directly. As you can see, both methods return a Block of type MASConstraint, so mas_equalTo (0) equals (MASConstraint * (^)(id))(0), MASConstraint * (^)(id) is the same as Block(0). You can then call the Block and return the MASConstraint type to proceed to the next method, which is where the Block is used as the return value to implement chained calls.

Is the so-called light said (see) do not practice fake kung fu, so now we personally implement a chain call example!! Create a Student class.h file

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
@class Student;

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger tall;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) CGSize size;

- (Student * (^)(NSString *))per_name;
- (Student * (^)(int))per_tall;
- (Student * (^)(int))per_age;
- (Student * (^)(CGSize))per_size;
- (Student * (^)(void))run;

@end

NS_ASSUME_NONNULL_END

Copy the code

.m files

#import "Student.h" @interface Student () @end @implementation Student - (Student * (^)(NSString *))per_name{ return ^ Student * (NSString *name){ self.name = name; return self; }; } - (Student * (^)(int))per_tall{ return ^ Student * (int tall){ self.tall = tall; return self; }; } - (Student * (^)(int))per_age{ return ^ Student * (int age){ self.age = age; return self; }; } - (Student * (^)(CGSize))per_size{ return ^ Student * (CGSize size){ self.size = size; return self; }; } - (Student * (^) (void)) run {return ^ Student * (void) {NSLog (@ "I'm running"); return self; }; } @endCopy the code

Use it in TestVC

- (void)test{ Student *s = [Student new]; Supachai panitchpakdi er_name (@ "small strong"). Per_tall (173). Per_age (18) per_size (CGSizeMake (180, 80)). The run (); NSLog (@ "I am a student, my name is % @, % ld height, age % ld, % @" size, s.n ame, s.t all, s.a ge, NSStringFromCGSize (s.s considering)); }Copy the code

print

2020-08-18 12:02:21.422766+0800 CJJFramework[3846:74527] I am a student, my name is Xiaoqiang, height 173, age 18, size {180, 80} (LLDB)Copy the code

This is a simple implementation of the chain syntax call, simple too beautiful there! More cumbersome than oc objects. The call is too neat. By the way play a small advertisement iOS-CJJTimer high performance countdown tool (SMS, commodity seconds kill Github address I package a countdown tool, which also used the chain syntax call, interested can see.


The underlying data structure of a Block

A Block is essentially an OC object, and because it inherits from NSBlock, which in turn inherits from NSObject, there is an ISA pointer inside the Block. Also, a Block is an OC object that encapsulates a function call and its environment.

  • A function call
void (^block)(void) = ^{
    NSLog(@"%d",a);
};
Copy the code

By looking underneath, we’ll find out

NSLog(@"%d",a);
Copy the code

This line of code exists directly in the Block. In the initialization method of the Block, we pass a parameter *fp (finally pass the function address to the Block ->impl->FuncPtr), which means that passing the entire code Block directly into the Block saves (encapsulating the function address, a reference pass).

  • Function call environment

Blocks encapsulate (store) variables passed in from outside

You can find the following source code by looking at apple’s official source code or directly compiling oc code into the underlying language C++ code

  • blockThe underlying structure of the
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_0xn052bn6dgb9z7pfk8bbg740000gn_T_main_88f00d_mi_0); } int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; }Copy the code

If you have never read the source code or are not familiar with C++, you may be confused. Blocks can be simplified into the following structure

struct __main_block_impl_0{ //struct __block_impl impl; Void *isa; // Block is an oc object int Flags; int Reserved; void *FuncPtr; Struct __main_block_desc_0* Desc; // Description of the block size_t reserved; size_t Block_size; // Block size};Copy the code

As you can see, the underlying data structure of a Block is a structure that contains the following member variables after simplification

  • Void *isa // DescriptionblockIs aocobject
  • Int Flags // Some Flags, apple uses thisflagsEnumerations above and below to judge something
// Values for Block_layout->flags to describe block objects enum { BLOCK_DEALLOCATING = (0x0001), // runtime BLOCK_REFCOUNT_MASK = (0xfffe), // runtime BLOCK_NEEDS_FREE = (1 << 24), // runtime BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code BLOCK_IS_GC = (1 << 27), // runtime BLOCK_IS_GLOBAL = (1 << 28), // compiler BLOCK_USE_STRET = (1 << 29), // compiler: undefined if ! BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30), // compiler BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler };Copy the code

For example, copy and dispose are determined by flags & BLOCK_HAS_COPY_DISPOSE, which will be described later

if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
        desc += sizeof(struct Block_descriptor_2);
    }
Copy the code

The above code comes from apple’s official source code libclosure-74

  • Int Reserved // Size of the area required for the version upgrade
  • Void *FuncPtr // The address of the encapsulated function
  • Size_t reserved // Size of the area required for the version upgrade
  • Size_t Block_size // Size of a block

And the initialization function

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

FuncPtr = fp (fp is the function pointer (void *)__main_block_func_0), __main_block_desc_0_DATA Desc = Desc


The type of the Block

Blocks have three types, and you can see their type and inheritance chain by calling the class method

  • Global Block (1._NSConcreteGlobalBlock)
(__NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject)
Copy the code
  • 2. The stack Block (_NSConcreteStackBlock)
(__NSStackBlock__ : __NSStackBlock : NSBlock : NSObject)
Copy the code
  • 3. The pile Block (_NSConcreteMallocBlock)
(__NSMallocBlock__ : __NSMallocBlock : NSBlock : NSObject)
Copy the code

whyBlockWill there be three types?

This is determined by the memory location in which it is stored. The following figure shows three types of memory in an applicationBlockThe region that exists, that is to say, to judge aBlockThe type depends on where it exists in memory. So how do you distinguish the threeBlockWhat are the similarities and differences between them?

Here are the threeBlockThe contrast of

  • NSGlobalBlock

Store location: data area (global area) environment: No access to the auto variable copy effect: nothing

  • NSStackBlock

Stack environment: The effect of accessing the auto variable copy: assignment from stack to heap

  • NSMallocBlock

Storage location: heap environment: __NSStackBlock__ Call copy copy effect: reference count increased

For example,

typedef void (^block0)(void) int val1 = 10; - (void)test{ //NSGlobalBlock block0 = = ^{ }; //NSGlobalBlock void (^block)(void) = ^{ }; //NSGlobalBlock void (^block1)(void) = ^{ NSLog(@"%d",val1); }; // NSStackBlock (MRC), NSMallocBlock (ARC) int val2 = 20; void (^block2)(void) = ^{ NSLog(@"%d",val2); }; //NSMallocBlock __block int val3 = 20; void (^block3)(void) = ^{ NSLog(@"%d",val3); }; }Copy the code

Block captures variable mechanism

It is well known that blocks have a mechanism for capturing variables in order to ensure that variables outside the Block can be accessed properly.

When a Block captures a variable, it adds a member variable to the Block structure. First variables can be divided into two types, local variables and global variables. Local variables are divided into local (automatic) variables and local static variables. Global variables are divided into global variables and global static variables

  • A local variable
    • 1. Automatic variable (meaning, out of scope will destroy itself, so called automatic variable, byBlockCapture is value passing (capture is the stored value)
    {
        auto int a = 0;
    }
    Copy the code
    • 2. Local static variables (will always exist in memory, byBlockCapture by reference (the address of the variable is captured)
    {
       static int a = 0;
     }
    Copy the code
  • Global variables (will always exist in memory, not beBlockCapture)
    • The global variable
    int a = 0;
    Copy the code
    • Global static variables that can only be accessed in this file, not externallyextern
    static int b = 0;
    Copy the code

Summary: Only local variables are captured by blocks, not global variables

Why are global variables not captured?

Because it’s always accessible

Why should local variables be captured?

The problem with scope is that using a local variable declared outside the Block inside a Block is equivalent to using that local variable across functions. If you don’t save a copy of the local variable inside the Block, you can’t use it. This will cause access to invalid memory because local variables outside the Block may be destroyed automatically when they are out of scope for example

typedef void (^Block)(void);

@property(nonatomic, copy) Block block;

- (void)test{
    int a = 0;
    self.block = ^{
        NSLog(@"%d",a);
    };
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self test];
    self.block();
}
Copy the code

This code responds to self.view with touchBegin and calls test. Test creates a local automatic variable a and initializes the self.block variable that uses A, but after test is called, a is destroyed and then the block is called. If a Block is not captured first, it will crash and access invalid memory. This is why local variables need to be captured and global variables do not need to be captured.

One more special case, can Self be captured?
- (void)test{
    self.block = ^{
        NSLog(@"%p",self);
    };
}
Copy the code

Yes, since self is also a local variable, let’s recall that calling a method in OC actually passes the argument to the self pointer, and captures the pointer, so it’s referential passing.

objc_msgSend(id self, SEL _cmd, ...)
Copy the code

So the reason we can use self in every method is because the self variable is passed in by default

Another special case, will a member variable be captured?
@property(nonatomic, copy) NSString *name;

- (void)test{
    self.block = ^{
        NSLog(@"%@",_name);
    };
}
Copy the code

Yes, because the member variables called here are also local variables, equivalent to

- (void)test{
    self.block = ^{
        NSLog(@"%@",self->_name);
    };
}
Copy the code

What exactly does the __Block modifier do?

Let’s look at this piece of code

int val = 10; void (^block)(void) = ^{ val = 20; // This is an error and will not compile because val is automatically local and will be destroyed when it is out of scope.Copy the code

So how do you make val changeable? There are several ways to modify val to be a global or static variable, but it is better to use the __block modifier

__block qualifier

  • __blockCan be used to solveBlockInternal cannot be modifiedautoVariable value problem
  • __blockYou cannot modify global variables, static variables (static)
  • The compiler will take__blockA variable is wrapped as an object (__Block_byref_age_0Type)

Take this paragraph

__block int val = 1; Int (^block)(CGFloat num) = ^ int (CGFloat num){NSLog(@" this is a block "); val = 2; return val; };Copy the code

Compiled into C++ code as follows, I reorganize the format for easy viewing

__attribute__((__blocks__(byref))) __Block_byref_ val_0 val =
{
  (void*)0,//void *__isa
  (__Block_byref_ val_0 *)& val,//__Block_byref_val_0 *__forwarding
  0,//int __flags
  sizeof(__Block_byref_val_0),//int __size
  1 //int val
};
int (*block)(CGFloat num) = (
  (int (*)(CGFloat))
  &__main_block_impl_0(
    (void *)__main_block_func_0, //
    &__main_block_desc_0_DATA, 
    (__Block_byref_val_0 *)& val, 
    570425344
  )
);
Copy the code

The automatic variable val is wrapped as a __Block_byref_val_0 object, That is, the Block’s __main_block_IMPL_0 struct instance holds a pointer to the __Block_byref_val_0 struct instance of the __block variable.

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; struct __Block_byref_val_0{ void *__isa; __Block_byref_age_0 *__forwarding; // This pointer points to the object's own address int __flags; int __size; int val; };Copy the code

You’ll find one in therevalIn fact, herevalIs theBlockThe one that came inval.

There is also a member variable__forwarding

And the __Block_byref_val_0 variable in __main_block_IMPL_0 does not existBlockInside the structure,BlockIt simply saves a pointer to the address of the __Block_byref_val_0 variable so that it can be stored in multiple differentBlockInside access to the same__blockThe variable.

Look at this picture and you might wonder. Why not just store val inside the Block structure instead of going through the trouble of generating a val structure and storing the val variable inside it?

  • My understanding is, because directly inBlockStored in thevalVariables, they’re stored on the stack, and when the scope of the variable expires the variable is destroyed, so it can’t be stored inBlockAccess the variable in; And by packaging it into one__Block_byref_val_0Type to store the variable in the object whenBlockFrom the stackcopyBy the time we get to the heap, it’s equal to__blockVariables are also removed from the stackcopyI put a copy in the heap, so that when I’m scoped out,BlockStill accessiblevalVariable, while incopyThe process on the stack__blockVariables in the__forwardingThe pointer will change to point to the heap__blockThe address of the structure instance of the variable, while in this way, either inBlockIn grammar,BlockExtralegal use__blockVariable, or__blockVariables configured on the stack or heap can be accessed without any problems__blockThe variable.


Block memory Management

What happens if the Block captures the auto variable of the object type?

It’s really just more memory management. Block generates two functions in desc after copy

  • copyfunction

Call time when blocks on the stack are copied to the heap

  • disposefunction

Call time when a Block on the heap is discarded

When a Block accesses the auto variable of the object type with the __block modifier

  • whenblockWhen you’re on the stack, it doesn’t__blockVariables generate strong references
  • whenblockbecopyTo the heap
    • Will be calledblockThe inside of thecopyfunction
    • copyThe function is called internally_Block_object_assignfunction
    • _Block_object_assignThe function is based on the modifier of the object it points to (__strong.__weak.__unsafe_unretained) perform corresponding operations to form a strong reference (retain) or weak references (note: this is only for ARCretainNot MRCretain)

  • whenblockWhen it’s removed from the heap
    • Will be calledblockThe inside of thedisposefunction
    • disposeThe function is called internally_Block_object_disposefunction
    • _Block_object_disposeThe function automatically releases the referenced __block variable (release)


The auto and __block variables of the object type

//auto
{
    (auto) Person *person = [Person new];
    void (^block)(void) = ^{
        NSLog(@"%@",person);
    };
}

//__block
{
    __block Person *person = [Person new];
    void (^block)(void) = ^{
        NSLog(@"%@",person);
    };
}

Copy the code
  • whenblockWhen on the stack, there is no strong reference to either of them
  • whenblockWhen you copy it to the heap, it all passescopyFunction to handle them
_Block_object_assign((void*)& DST ->a, (void*) SRC ->a, 8/*BLOCK_FIELD_IS_BYREF*/); // The auto variable of the object type (assuming the variable name is p) _Block_object_assign((void*)& DST ->p, (void*) SRC ->p, 3/*BLOCK_FIELD_IS_OBJECT*/);Copy the code
  • whenblockWhen it’s removed from the heap, it goes throughdisposeFunction to release them
_Block_object_dispose((void*) SRC ->a, 8/*BLOCK_FIELD_IS_BYREF*/); // The auto variable of the object type (assuming the variable name isp) _Block_object_dispose((void*) SRC ->p, 3/*BLOCK_FIELD_IS_OBJECT*/);Copy the code

Block loop reference

There’s an object A, A Block and when A strongly references A Block, the Block strongly references A, that’s A circular reference, causing A memory leak. I’ll write it in code

@interface A : NSObject
@property(nonatomic, copy) void (^block)(void);
@end

@implementation

- (void)viewDidLoad{
    [super viewDidLoad];
    self.block = ^{
        NSLog(@"%@",self);
    };
}

@end
Copy the code

As shown above, self holds the block property, and then the block holds self, which strongly references each other, causing no one to free. This is just the simplest case. In fact, it is possible to encounter much more complicated cases than this, with self-reference loops (A->A), two-way reference loops (A->B->A), Multi-reference loop (A->B->C->A) and so on, but only we know the nature of reference loop, these situations are actually easy to find and solve, we just cut off the strong reference in the random side of the reference chain can solve the problem of reference loop.

Solutions resolve circular references under ARC and MRC in different ways. Under ARC, you can use __weak, __unsafe_unretained, and __block

//__weak __weak typeof(self) weakSelf = self; self.block = ^{ NSLog(@"%p",weakSelf); }; //__unsafe_unretained __unsafe_unretained id weakSelf = self; self.block = ^{ NSLog(@"%p",weakSelf); }; WeakSelf = self; weakSelf = self; weakSelf = nil; weakSelf = self; self.block = ^{ NSLog(@"%p",weakSelf); weakSelf = nil; }; self.block();Copy the code

Under MRC, you can use __unsafe_unretained and __block

//__unsafe_unretained
__unsafe_unretained id weakSelf = self;
self.block = ^{
    NSLog(@"%p",weakSelf);
};

//__block
__block id weakSelf = self;
self.block = ^{
    NSLog(@"%p",weakSelf);
};
Copy the code

__weak for ARC and __unsafe_unretained for MRC.

In addition

In daily development, I found that some colleagues use WeakSelf wherever there are blocks. Even if they have worked for three or four years, they also have this problem. In fact, they do not understand the nature of the reference cycle

Example 1: Controller does not strongly reference block, block strongly references self(weakSelf is not required)

@interface A : NSObject

@end

@implementation

- (void)viewDidLoad{
    [super viewDidLoad];
    id block = ^{
        NSLog(@"%@",self);
    };
}

@end
Copy the code

Analysis: Self does not hold block, and block holds self, which does not constitute a two-way circular reference, so weakSelf is not needed

Example 2: Block of class method (no need for WeakSelf)

@interface A : NSObject

@end

@implementation

- (void)viewDidLoad{
    [super viewDidLoad];
    [UIView animateWithDuration:duration floatValue] animations:^{
            NSLog(@"%@",self);
     }];

}

@end
Copy the code

Analysis: Same as Example 1

Example 3: Callback block of AFNetworking request method (no need for WeakSelf)

[[AFNetWorkManager sharedManager] requestWithUserMethod:POST Url:url parameters:paramsDic success:^(NetWorkResultModel *  _Nonnull resultModel) { NSLog(@"%@",self); } failure:^(NSError * _Nonnull error) { }];Copy the code

Analysis: First of all, most of the ones that encapsulate AFN use singletons. Under normal circumstances, if the singletons hold self, it will cause that self cannot be released, because the singletons will always exist in the memory unless they are released artificially. Then why does the BLOCK of AFN here reference self not need WeakSelf? Because AFN has already done internal processing to remove the reference to the block after the request ends, weakSelf is not needed in this case.


Block switching implementation

Because this topic is too much content, so another article to talk about how to hook a block implementation? Portal ->iOS- Play Block (Hook Block exchange Block implementation)


Block related interview questions

  • A,BlockHow does it work? What is the essence?

An OC object that encapsulates the function call and the calling environment. (To be added, based on the actual interview situation)

  • Second,__blockWhat is the function of? What is the use of attention?

Note: memory management problems, under MRC __block modification internal does not generate strong reference to the object (retain); If ARC does, you need to avoid circular references.

  • Three,BlockWhy is the attribute modifier ofcopy? useBlockWhat are the usage considerations?

Cause: Once a Block has not been copied, it is not on the heap, so we can use memory management by copying the Block to the heap. Note: Circular reference problem Also: ARC uses Strong and Copy, both of which Copy blocks to the heap. MRC uses only Copy, so Copy is best

  • Four,BlockIn the modifiedNSMutableArray, whether to add__block?

No, because you’re just manipulating the contents of the array, not modifying its memory address