GitHub Repo: BoyangBlog

A simple overview

Blocks are extensions of C and can be thought of as anonymous functions with automatic variables.

A block is an anonymous inline collection of code:

  • Argument list, just like a function.
  • It’s an object!
  • There is a declared return type
  • To obtain the state of the lexical lexical range.
  • Optionally modify the state of lexical scope.
  • The possibility of modification can be shared with other blocks defined in the same lexical scope
  • After the lexical scope (stack frame) is broken, the state defined in the lexical scope (stack frame) can continue to be shared and modified

How do you write block

The most simple.

    int (^DefaultBlock1)(int) = ^int (int a) {
        return a + 1;
    };
    DefaultBlock1(1);
    
Copy the code

Updated version.

// Declare a block using a typedef
typedef return_type (^BlockTypeName)(var_type);

/ / properties
@property (nonatomic, copy ,nullable) BlockTypeName blockName;

// make method arguments
- (void)requestForSomething:(Model)model handle:(BlockTypeName)handle;
Copy the code

The realization of the block

In the LLVM file, I found a document, block_private.h, where you can see the implementation of blocks

struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void*,...). ;struct Block_descriptor *descriptor;
    /* Imported variables. */
};
struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void *);
};

Copy the code

The invoke is a function pointer to the implementation, and when a block is called, the program eventually jumps to the code area that the function pointer points to. The most important functions in Block_descriptor are copy and dispose, which are used to capture variables and hold references, and dispose, which are used to release captured variables. The variables captured by the function are stored after the Block_layout structure and read out before the invoke function executes.

By convention, compiling and transforming a code using clang-rewrite-objc produces a copy of C++ code. Strip out the other useless code:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

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; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}

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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
    }
    return 0;
}
Copy the code

Let’s start with the most straightforward __block_impl code,

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};
Copy the code

So here’s a structure, where the elements are

  • Isa, a pointer to the owning class, which is the type of the block
  • Flags, the flag variable, is used when implementing internal operations on blocks
  • Reserved, Reserved variable
  • FuncPtr, a function pointer called when a block executes

Next, __main_block_IMPL_0, since it contains __block_impl, we can turn it on and view it as

__main_block_impl_0{
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
    struct __main_block_desc_0 *Desc;
}
Copy the code

In this way, we can think of a block as an OC object, a function.

The type of the block

We can see that there are three common blocks:

  • __NSGlobalBlock
  • __NSStackBlock
  • __NSMallocBlock
void (^block)(void) = ^{
    NSLog(@"biboyang");
};
block();
Copy the code

or

static int age = 10;
    void(^block)(void) = ^{
        NSLog(@"Hello, World! %d",age);
    };
block();
Copy the code

Something like this, with no external variables captured, is GlobaBlock.

    int b = 10;
    void(^block2)(void) = ^{
        NSLog(@"Hello, World! %d",b);
    };
    block2();
Copy the code

This block, in MRC, is a StackBlock. In ARC, this is called MallocBlock because the compiler is optimized to copy automatically.

The reason for this optimization is easy to understand:

If StackBlock accesses an auto variable, it will be stored on the Stack because it exists on the Stack itself. But because the data on the stack is automatically managed by the system, it can be reclaimed at any time. It’s very easy to cause wild Pointers.

How do you solve it? Just copy it to the heap!

ARC does the same thing. It automatically copies blocks on the stack to the heap, so you can use both strong and copy for ARC block attributes, but copy is a good way to get used to it.

The class Blcok The configuration storage domain of the copy source Print effect
__NSStackBlock The stack The heap
__NSGlobalBlock The data area of the program useless
__NSMallocBlock The heap Reference count increment

There are four cases in which the copy method is called by default

  1. Manually calling copy
  2. Block is the return value of the function
  3. Blocks are strongly referenced, and blocks are assigned to __strong or id types
  4. The call system API entry contains Cocoa methods for usingBlcok or related GCD apis

In ARC, once a Block is assigned, copy is triggered, and __block, also __NSMallocBlock, is copied to the heap. ARC environments also have __nsstackBlocks, in which case __block is on the stack.

How to intercept variables

Take ice frost’s article directly here to use

#import <Foundation/Foundation.h>

int global_i = 1;

static int static_global_j = 2;

int main(int argc, const char * argv[]) {
   
    static int static_k = 3;
    int val = 4;
    
    void (^myBlock)(void) = ^{
        global_i ++;
        static_global_j ++;
        static_k ++;
        NSLog(@Global_i = %d,static_global_j = %d,static_k = %d,val = %d,global_i,static_global_j,static_k,val);
    };
    
    global_i ++;
    static_global_j ++;
    static_k ++;
    val ++;
    NSLog(@Global_i = %d,static_global_j = %d,static_k = %d,val = %d,global_i,static_global_j,static_k,val);
    
    myBlock();
    
    return 0;
}

Copy the code

The results

Outside Block global_i =2,static_global_j = 3,static_k = 4,val = 5In the Block global_i =3,static_global_j = 4,static_k = 5,val = 4
Copy the code

The result of the transformation is

int global_i = 1;

static int static_global_j = 2;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_k;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_k, int _val, int flags=0) : static_k(_static_k), val(_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_k = __cself->static_k; // bound by copy
  int val = __cself->val; // bound by copyglobal_i ++; static_global_j ++; (*static_k) ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*s tatic_k),val); }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)};


int main(int argc, const char * argv[]) {

    static int static_k = 3;
    int val = 4;

    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val)); global_i ++; static_global_j ++; static_k ++; val ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_1,global_i,static_global_j,sta tic_k,val); ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

Copy the code

Global variables global_i and static_global_j increase in value and are captured by the Block. This is easy to understand, because they are global and have a wide scope, so the Block captures them and performs ++ operations in the Block. When the Block is done, Their values can still be preserved.

In __main_block_IMPL_0, you can see that the static variables static_k and the automatic variables val are captured by the Block as members of the __main_block_IMPL_0 structure. When a Block syntax is executed, the values of the automatic variables used by the Block syntax expressions are stored in the struct instance of the Block, that is, the Block itself.

This makes it much clearer that automatic variables are passed to the Block’s constructor by value. Blocks capture only the variables that will be used in the Block. Since only the value of the automatic variable is captured, not the memory address, the value of the automatic variable cannot be changed inside the Block.

Modify automatic variables

There are two ways to intercept variables and modify them: __block and Pointers (though __block is, after all, a pointer). Here is a description of the pointer method:

    NSMutableString * str = [[NSMutableString alloc]initWithString:@"Hello,"];
    
    void (^myBlock)(void) = ^{
        [str appendString:@"World!"];
        NSLog(@"Block STR = %@",str);
    };
    NSLog(@"Outer Block STR = %@",str);
    myBlock();
    
    const char *text = "hello";
    void(^block)(void) = ^ {printf("%caaaaaaaaaaa\n",text[2]);
    };
    block();
Copy the code

Direct manipulation of Pointers to intercept, but generally speaking, this method is used for C arrays. Most OC uses __block.

Write an __block capture code here, and do it again using the same method as before:

1. Ordinary non-object variables

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;// Point to the real block
 int __flags;
 int __size;
 int i;/ / object
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_i_0 *i = __cself->i; // bound by ref

        (i->__forwarding->i) ++;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_3b0837_mi_0,(i->__forwarding->i));
    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/); }static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0.sizeof(__Block_byref_i_0), 0};

    void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

Copy the code

We can see that there are two more structures here

struct __Block_byref_i_0 {
  void *__isa;
__Block_byref_i_0 *__forwarding;
 int __flags;
 int __size;
 int i;
};
Copy the code

This instance contains the __isa pointer, a flag bit __flags, and a record size __size. Most importantly, there is an __forwarding pointer and a val variable. To make a long story short, a new __forwarding pointer points to the memory address of the structure instance itself.

The block stores the value of the automatic variable used in the block’s struct instance through continuous passing of the pointer. Modify the __block0 variable in the block body. Through a series of pointer pointing relations, finally point to the __Block_byref_age_0 structure with the same name and type as the local variable, and successfully modify the variable value.

In the stack, __forwarding refers to itself, but if copied to the heap, __forwarding refers to the block copied to the heap, and __forwarding in the block on the heap refers to itself.

2. Object variables

// The following code is executed under ARC
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
     
    __block id block_obj = [[NSObject alloc]init];
    id obj = [[NSObject alloc]init];

    NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
    
    void (^myBlock)(void) = ^{
        NSLog(@"* * * * * * * in the Block block_obj = [p] % @, %, obj = [p] % @, %",block_obj , &block_obj , obj , &obj);
    };
    
    myBlock();
   
    return 0;
}
Copy the code

After the transformation

struct __Block_byref_block_obj_0 {
  void *__isa;
__Block_byref_block_obj_0 *__forwarding;// Point to the real block
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 id block_obj;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id obj;
  __Block_byref_block_obj_0 *block_obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref
  id obj = __cself->obj; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_1,(block_obj->__forwarding->bl ock_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj); }static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};


int main(int argc, const char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)&block_obj, 33554432.sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};

    id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")); NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_0,(block_obj.__forwarding->blo ck_obj) , &(block_obj.__forwarding->block_obj) , obj , &obj);void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)&block_obj, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    return 0;
}

Copy the code

Block captures __block and strongly references it, because in the __Block_byref_block_obj_0 structure, one of the variables is the id block_obj, which also comes with the __strong ownership modifier by default.

According to the printed result, in ARC environment, Block capture external object variable, is a copy, the address is different. Only variables with a __block modifier are captured and held inside the Block.

In ARC, external objects declared as __block are retained within the block so that external objects can be safely referenced within the block environment.

3. Instance variables

A question that hadn’t occurred to me before:

We know we shouldn’t use instance variables in blocks because circular references occur; So why does circular referencing happen? Well, normally we think of it as an instance variable of _age, which is really self->_age. So what if you dig a little deeper?

Inspired by a direct interview with Ivar, I began to explore the reasons here as well.

Write the following code:


#import <Foundation/Foundation.h>
#import "objc/runtime.h"

typedef void(^MyBlock)(void);

@interface MyObject : NSObject
@property (nonatomic) NSUInteger BRInteger;
@property (nonatomic, copy) NSString *BRString;
@property (nonatomic, copy) MyBlock BRBlock;

- (void)inits;

@end

@implementation MyObject
- (void)inits
{
    self.BRBlock = ^{
        _BRInteger = 5;
        _BRString = @"Balaeniceps_rex";
    };
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        MyObject *object = [MyObject new];
        [object inits];
    }
    return 0;
}
Copy the code

Use clang-rewrite-objc-fobjc-arc-stdlib =libc++ -mmacosx-version-min= 10.7-fobjc-runtime =macosx-10.7 – wno-deprecated -declarations main.m command is converted. We get the following code (omitted for simplicity) :

typedef void(*MyBlock)(void); #ifndef _REWRITER_typedef_MyObject #define _REWRITER_typedef_MyObject typedef struct objc_object MyObject; typedef struct {} _objc_exc_MyObject; Extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRInteger; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRString; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRBlock; Struct MyObject_IMPL {struct NSObject_IMPL NSObject_IVARS; NSUInteger _BRInteger; NSString *__strong _BRString; __strong MyBlock _BRBlock; }; // @property (nonatomic) NSUInteger BRInteger; // @property (nonatomic, copy) NSString *BRString; // @property (nonatomic, copy) MyBlock BRBlock; // - (void)inits; /* @end */ // @implementation MyObject struct __MyObject__inits_block_impl_0 { struct __block_impl impl; struct __MyObject__inits_block_desc_0* Desc; MyObject *const __strong self; Self __MyObject__inits_block_impl_0(void *fp, struct __MyObject__inits_block_desc_0 *desc, struct __MyObject__inits_block_desc_0 *desc, MyObject *const __strong _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; Static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) { MyObject *const __strong self = __cself->self; // bound by copy // The address of the instance variable (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = 5; (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)) = (NSString *)&__NSConstantStringImpl__var_folders_m1_05zb_zbd1g1f8k27nc6yn_th0000gn_T_main_e9db32_mi_0; } static void __MyObject__inits_block_copy_0(struct __MyObject__inits_block_impl_0*dst, struct __MyObject__inits_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __MyObject__inits_block_dispose_0(struct __MyObject__inits_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __MyObject__inits_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __MyObject__inits_block_impl_0*, struct __MyObject__inits_block_impl_0*); void (*dispose)(struct __MyObject__inits_block_impl_0*); } __MyObject__inits_block_desc_0_DATA = { 0, sizeof(struct __MyObject__inits_block_impl_0), __MyObject__inits_block_copy_0, __MyObject__inits_block_dispose_0}; static void _I_MyObject_inits(MyObject * self, SEL _cmd) { ((void (*)(id, SEL, MyBlock))(void *)objc_msgSend)((id)self, sel_registerName("setBRBlock:"), ((void (*)())&__MyObject__inits_block_impl_0((void *)__MyObject__inits_block_func_0, &__MyObject__inits_block_desc_0_DATA, self, 570425344))); } static NSUInteger _I_MyObject_BRInteger(MyObject * self, SEL _cmd) { return (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)); } static void _I_MyObject_setBRInteger_(MyObject * self, SEL _cmd, NSUInteger BRInteger) { (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = BRInteger; } static NSString * _I_MyObject_BRString(MyObject * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)); } extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool); static void _I_MyObject_setBRString_(MyObject * self, SEL _cmd, NSString *BRString) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct MyObject, _BRString), (id)BRString, 0, 1); } static void(* _I_MyObject_BRBlock(MyObject * self, SEL _cmd) )(){ return (*(__strong MyBlock *)((char *)self + OBJC_IVAR_$_MyObject$_BRBlock)); } static void _I_MyObject_setBRBlock_(MyObject * self, SEL _cmd, MyBlock BRBlock) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct MyObject, _BRBlock), (id)BRBlock, 0, 1); } // @end int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; MyObject *object = ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new")); ((void (*)(id, SEL))(void *)objc_msgSend)((id)object, sel_registerName("inits")); } return 0; }Copy the code

We can see that each instance variable is created with a corresponding global variable:

extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRInteger;
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRString;
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRBlock;
Copy the code

Here is the function call method in the fourth row of the Block layout.

// Block's function method (the fourth line of the layout method)
static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) {
    MyObject *const __strong self = __cself->self; // bound by copy
    // Get the address of the instance variable from the offset of the global variable
    (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = 5;
    (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)) = (NSString *)&__NSConstantStringImpl__var_folders_m1_05zb_zbd1g1f8k27nc6yn_th0000gn_T_main_e9db32_mi_0;
}
Copy the code

We can actually see from this that we’re getting the address of the instance variable by the offset of self, which is also dependent on self.

If that doesn’t prove self in the instance variable, let’s move on;

struct __MyObject__inits_block_impl_0 {
    struct __block_impl impl;
    struct __MyObject__inits_block_desc_0* Desc;
    MyObject *const __strong self;
    
    // Notice that self is captured here
    __MyObject__inits_block_impl_0(void *fp, struct __MyObject__inits_block_desc_0 *desc, MyObject *const __strong _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

In this method, we can see that inside the block, which also refers to MyObject, is a strongly referenced self! The block constructor also references self multiple times.

If we know about property, we know that instance variables are addressed at compile time. The global variable implemented internally represents the address offset.

Let’s look at a question

What happens if we set block to nil and call it?

void (^block)(void) = nil;
block();
Copy the code

When we run it, it crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x10).

We have no problem sending nil messages to an object, but if it is NULL it will crash.

  • Nil: a null pointer to an object in OC
  • Nil: a null pointer to a class in OC
  • NULL: a NULL pointer to another type, such as a C memory pointer
  • NSNull: In a collection object, an object representing a null value
  • If obj is nil:[obj message] returns NO, not NSException
  • If obj is NSNull:[obj message] an NSException will be thrown

It accesses the function pointer directly, because the first three bits are void, int, int, and size 8, 4, 4, and 16, so in 16 bits, it represents the crash of address 0x10. On a 32-bit system, void of size 4 would crash at 0x0c.

Block (2) : Block copy

reference

Blocks Programming Topics

Working with Blocks

fuckingblocksyntax.com