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
- Manually calling copy
- Block is the return value of the function
- Blocks are strongly referenced, and blocks are assigned to __strong or id types
- 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