原做于:2018-01-02
GitHub Repo:BoyangBloghtml
block是C语言的扩充功能,咱们能够认为它是 带有自动变量的匿名函数。c++
block是一个匿名的inline代码集合:git
- 参数列表,就像一个函数。
- 是一个对象!
- 有声明的返回类型
- 可得到义词法范围的状态,。
- 可选择性修改词法范围的状态。
- 能够用相同的词法范围内定义的其它block共享进行修改的可能性
- 在词法范围(堆栈框架)被破坏后,能够继续共享和修改词法范围(堆栈框架)中定义的状态
最简单。github
int (^DefaultBlock1)(int) = ^int (int a) { return a + 1; }; DefaultBlock1(1); 复制代码
升级版。macos
// 利用 typedef 声明block typedef return_type (^BlockTypeName)(var_type); // 做属性 @property (nonatomic, copy ,nullable) BlockTypeName blockName; // 做方法参数 - (void)requestForSomething:(Model)model handle:(BlockTypeName)handle; 复制代码
在LLVM的文件中,我找到了一份文档,Block_private.h,这里能够查看到block的实现状况数组
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 *); }; 复制代码
里面的invoke就是指向具体实现的函数指针,当block被调用的时候,程序最终会跳转到这个函数指针指向的代码区。 而 Block_descriptor里面最重要的就是 copy函数和 dispose函数,从命名上能够推断出,copy函数是用来捕获变量并持有引用,而dispose函数是用来释放捕获的变量。函数捕获的变量会存储在结构体 Block_layout的后面,在invoke函数执行前所有读出。安全
按照惯例,使用 clang -rewrite-objc 将一个代码进行编译转换,将获得一份C++代码。刨除其余无用的代码:markdown
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; } 复制代码
先看最直接的 __block_impl代码,app
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; 复制代码
这里是一个结构体,里面的元素分别是框架
- isa,指向所属类的指针,也就是block的类型
- flags,标志变量,在实现block的内部操做时会用到
- Reserved,保留变量
- FuncPtr,block执行时调用的函数指针
接着, __main_block_impl_0由于包含了__block_impl,咱们能够将它打开,直接当作
__main_block_impl_0{ void *isa; int Flags; int Reserved; void *FuncPtr; struct __main_block_desc_0 *Desc; } 复制代码
这么一来,咱们能够将block理解为,一个OC对象、一个函数。
咱们能够知道,咱们常见的block是有三种:
- __NSGlobalBlock
- __NSStackBlock
- __NSMallocBlock
void (^block)(void) = ^{ NSLog(@"biboyang"); }; block(); 复制代码
或
static int age = 10; void(^block)(void) = ^{ NSLog(@"Hello, World! %d",age); }; block(); 复制代码
像是这种,没有对外捕获变量的,就是GlobaBlock。
int b = 10; void(^block2)(void) = ^{ NSLog(@"Hello, World! %d",b); }; block2(); 复制代码
这种block,在MRC中,便是StackBlock。在ARC中,由于编译器作了优化,自动进行了copy,这种就是MallocBlock了。
之因此作这种优化的缘由很好理解:
若是StackBlock访问了一个auto变量,由于本身是存在Stack上的,因此变量也就会被保存在栈上。可是由于栈上的数据是由系统自动进行管理的,随时都有可能被回收。很是容易形成野指针的问题。
怎么解决呢?复制到堆上就行了!
ARC也是如此作的。它会自动将栈上的block复制到堆上,因此,ARC下的block的属性关键词其实使用strong和copy都不会有问题,不过为了习惯,仍是使用copy为好。
Blcok的类 | 副本源的配置存储域 | 复制效果 |
---|---|---|
__NSStackBlock | 栈 | 堆 |
__NSGlobalBlock | 程序的数据区域 | 无用 |
__NSMallocBlock | 堆 | 引用计数增长 |
系统默认调用copy方法把Block赋复制的四种状况
- 手动调用copy
- Block是函数的返回值
- Block被强引用,Block被赋值给__strong或者id类型
- 调用系统API入参中含有usingBlcok的Cocoa方法或者GCD的相关API
ARC环境下,一旦Block赋值就会触发copy,__block就会copy到堆上,Block也是__NSMallocBlock。ARC环境下也是存在__NSStackBlock的时候,这种状况下,__block就在栈上。
这里直接拿冰霜的文章来用
#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(@"Block中 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(@"Block外 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val); myBlock(); return 0; } 复制代码
运行结果
Block 外 global_i = 2,static_global_j = 3,static_k = 4,val = 5 Block 中 global_i = 3,static_global_j = 4,static_k = 5,val = 4 复制代码
转换的结果为
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 copy global_i ++; static_global_j ++; (*static_k) ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*static_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,static_k,val); ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock); return 0; } 复制代码
首先全局变量global_i和静态全局变量static_global_j的值增长,以及它们被Block捕获进去,这一点很好理解,由于是全局的,做用域很广,因此Block捕获了它们进去以后,在Block里面进行++操做,Block结束以后,它们的值依旧能够得以保存下来。
在__main_block_impl_0中,能够看到静态变量static_k和自动变量val,被Block从外面捕获进来,成为__main_block_impl_0这个结构体的成员变量了。 在执行Block语法的时候,Block语法表达式所使用的自动变量的值是被保存进了Block的结构体实例中,也就是Block自身中。
这么来就清晰了不少,自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。因为只捕获了自动变量的值,并不是内存地址,因此Block内部不能改变自动变量的值。
截获变量并修改有两种方法 __block 和 指针法(不过__block法归根结底,其实也是操做指针)。 这里描述一下指针法:
NSMutableString * str = [[NSMutableString alloc]initWithString:@"Hello,"]; void (^myBlock)(void) = ^{ [str appendString:@"World!"]; NSLog(@"Block中 str = %@",str); }; NSLog(@"Block外 str = %@",str); myBlock(); const char *text = "hello"; void(^block)(void) = ^{ printf("%caaaaaaaaaaa\n",text[2]); }; block(); 复制代码
直接操做指针去进行截获,不过通常来说,这种方法多用于C语言数组的时候。使用OC的时候多数是使用__block。
这里写一个__block的捕获代码,使用刚才的方法再来一次:
struct __Block_byref_i_0 { void *__isa; __Block_byref_i_0 *__forwarding;//指向真正的block int __flags; int __size; int i;//对象 }; 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; } 复制代码
咱们能够发现这里多了两个结构体
struct __Block_byref_i_0 { void *__isa; __Block_byref_i_0 *__forwarding; int __flags; int __size; int i; }; 复制代码
这个实例内,包含了 __isa 指针、一个标志位 __flags 、一个记录大小的 __size 。最最重要的,多了一个 __forwarding 指针和 val 变量. 这里长话短说,出来了一个新的 __forwarding指针,指向告终构体实例自己在内存的地址。
block经过指针的持续传递,将使用的自动变量值保存到了block的结构体实例中。在block体内修改 __block0变量,经过一系列指针指向关系,最终指向了__Block_byref_age_0结构体内与局部变量同名同类型的那个成员,并成功修改变量值。
在栈中, __forwarding指向了本身自己,可是若是复制到了堆上,__forwarding就指向复制到堆上的block,而堆上的block中的 __forwarding这时候指向了本身。
//如下代码是在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(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj); }; myBlock(); return 0; } 复制代码
转换以后
struct __Block_byref_block_obj_0 { void *__isa; __Block_byref_block_obj_0 *__forwarding;//指向真正的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 copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_1,(block_obj->__forwarding->block_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->block_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; } 复制代码
在转换出来的源码中,咱们也能够看到,Block捕获了__block,而且强引用了,由于在__Block_byref_block_obj_0结构体中,有一个变量是id block_obj,这个默认也是带__strong全部权修饰符的。
根据打印出来的结果来看,ARC环境下,Block捕获外部对象变量,是都会copy一份的,地址都不一样。只不过带有__block修饰符的变量会被捕获到Block内部持有。
在ARC中,对于声明为__block的外部对象,在block内部会进行retain,以致于在block环境内能安全的引用外部对象。
以前一直没有想到过一个问题:
咱们知道不该该在block中使用实例变量,是由于会发生循环引用;那为何会发生循环引用呢? 通常咱们会理解为,一个_age的实例变量,其实是self->_age。那么若是往下深究下去呢?
受谈谈ivar的直接访问的启发,我也开始探索一下这里的缘由。
写以下的代码:
#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; } 复制代码
使用 clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations main.m命令进行转换。获得如下的代码(为了简便,将代码作了省略):
typedef void(*MyBlock)(void);
#ifndef _REWRITER_typedef_MyObject
#define _REWRITER_typedef_MyObject
typedef struct objc_object MyObject;
typedef struct {} _objc_exc_MyObject;
#endif
//对于每一个ivar,都有对应的全局变量
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, MyObject *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block的函数方法(也就是方法layout中第四行的那个)
static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) {
MyObject *const __strong self = __cself->self; // bound by copy
//这里是经过self的地址,那倒全局变量的偏移去获取实例变量的地址
(*(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;
}
复制代码
咱们能够发现,每一个实例变量都是被建立了对应的全局变量:
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRInteger; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRString; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRBlock; 复制代码
下面是block的layout中的第四排的函数调用方法。
//block的函数方法(也就是方法layout中第四行的那个) static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) { MyObject *const __strong self = __cself->self; // bound by copy //这里是经过self的地址,那倒全局变量的偏移去获取实例变量的地址 (*(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; } 复制代码
经过这里,咱们其实也能发现,这里是经过self的偏移去获取实例变量的地址,也是和self息息相关的。
若是这个还不会证实实例变量中的self的做用的话,咱们接着往下看;
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, MyObject *const __strong _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; 复制代码
在这个方法里,咱们能够发现,在block当中,其实也引用到MyObject,是一个强引用的self!而block的构造函数中也屡次引用了self。
咱们若是了解过property的话,也会知道实例变量是在编译期就肯定地址了。内部实现的全局变量就表明了地址的offset。
若是咱们把block设置为nil,而后去调用,会发生什么?
void (^block)(void) = nil; block(); 复制代码
当咱们运行的时候,它会崩溃,报错信息为 Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)。
咱们给一个对象发送nil消息是没有问题的,可是给若是是NULL就会发生崩溃。
它直接访问到了函数指针,由于前三位分别是void、int、int,大小分别是八、四、4,加一块就为16,因此在十六位中,就表示出0x10地址的崩溃。 若是是在32位的系统中,void的大小是4,崩溃的地址应该就是0x0c。
下一篇文章block(二):block的copy
Blocks Programming Topics
Working with Blocks
fuckingblocksyntax.com