__main_block_impl_0
分析aotu修饰的局部变量和static修饰的局部变量之间的差异html
int main(int argc, const char * argv[]) { @autoreleasepool { auto int a = 10; static int b = 10; void(^block)(void) = ^{ NSLog(@"age is %d, height is %d", a, b); }; a = 1; b = 2; block(); } return 0; } // log : 信息--> age = 10, height = 2 // block中a的值没有被改变而b的值随外部变化而变化。
从新生成c++代码看一下内部结构中两个参数的区别。ios
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int a; // a 为值 int *b; // b 为指针 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) { impl.isa = &_NSConcremainackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int a = __cself->a; // bound by copy int *b = __cself->b; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_fd2a14_mi_0, a, (*b)); } 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 age_ = 10; static int height_ = 10; int main(int argc, const char * argv[]) { @autoreleasepool { void(^block)(void) = ^{ NSLog(@"age is %d, height is %d", age_, height_); }; age_ = 1; height_ = 2; block(); } return 0; } // log 信息--> age = 1, height = 2
一样生成c++代码查看全局变量调用方式c++
int age_ = 10; static int height_ = 10; 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 = &_NSConcremainackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_fd2a14_mi_0, age_, height_); } 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)};
如下Persion类代码中block变量分析数组
@interface Person : NSObject @property (copy, nonatomic) NSString *name; - (void)test; - (instancetype)initWithName:(NSString *)name; @end #import "Person.h" @implementation Person int age_ = 10; - (void)test { void (^block)(void) = ^{ NSLog(@"-------%d", [self name]); }; block(); } - (instancetype)initWithName:(NSString *)name { if (self = [super init]) { self.name = name; } return self; } @end
一样转化为c++代码查看其内部结构函数
int age_ = 10; struct __Person__test_block_impl_0 { struct __block_impl impl; struct __Person__test_block_desc_0* Desc; Person *self; __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) { Person *self = __cself->self; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_Person_1027e6_mi_0, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name"))); } static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);} static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);} static struct __Person__test_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __Person__test_block_impl_0*, struct __Person__test_block_impl_0*); void (*dispose)(struct __Person__test_block_impl_0*); } __Person__test_block_desc_0_DATA = { 0, sizeof(struct __Person__test_block_impl_0), __Person__test_block_copy_0, __Person__test_block_dispose_0}; static void _I_Person_test(Person * self, SEL _cmd) { void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } static instancetype _I_Person_initWithName_(Person * self, SEL _cmd, NSString *name) { if (self = ((Person *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("init"))) { ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString *)name); } return self; } static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); } extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool); static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); } // @end struct _prop_t { const char *name; const char *attributes; };
不论对象方法仍是类方法都会默认将self做为参数传递给方法内部,既然是做为参数传入,那么self确定是局部变量。上面讲到局部变量确定会被block捕获。源码分析
在block内部使用name成员变量或者调用实例的属性编码
- (void)test { void(^block)(void) = ^{ NSLog(@"%@",self.name); NSLog(@"%@",_name); }; block(); }
获得结论:在block中使用的是实例对象的属性,block中捕获的仍然是实例对象,并经过实例对象经过不一样的方式去获取使用到的属性。atom
经过源码分析获得,block中的isa指针指向的是_NSConcreteStackBlock类对象地址。那么block是否就是_NSConcreteStackBlock类型的呢?spa
咱们经过代码用class方法或者isa指针查看具体类型。指针
int main(int argc, const char * argv[]) { @autoreleasepool { // __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject void (^block)(void) = ^{ NSLog(@"Hello"); }; NSLog(@"%@", [block class]); NSLog(@"%@", [[block class] superclass]); NSLog(@"%@", [[[block class] superclass] superclass]); NSLog(@"%@", [[[[block class] superclass] superclass] superclass]); } return 0; } // log 打印结果 __NSGlobalBlock__ // log 打印结果 __NSGlobalBlock // log 打印结果 NSBlock // log 打印结果 NSObjcet
从上述打印内容能够看出block最终都是继承自NSBlock类型,而NSBlock继承于NSObjcet。那么block其中的isa指针实际上是来自NSObject中的。这也更加印证了block的本质其实就是OC对象。
经过代码查看一下block在什么状况下其类型会各不相同
int main(int argc, const char * argv[]) { @autoreleasepool { // 1. 内部没有调用外部变量的block void (^block1)(void) = ^{ }; // 2. 内部调用外部变量的block int a = 10; void (^block2)(void) = ^{ NSLog(@"log :%d",a); }; // 3. 直接调用的block的class NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{ NSLog(@"%d",a); } class]); } return 0; } // 最后一行 Log :打印结果 __NSGlobalBlock__, __NSStackBlock__ ,__NSMallocBlock__
咱们能够推测runtime运行时过程当中也许对类型进行了转变。最终类型固然以runtime运行时类型也就是咱们打印出的类型为准。
经过下面一张图看一下不一样block的存放区域
block是如何定义其类型
接着咱们使用代码验证上述问题,首先关闭ARC回到MRC环境下,由于ARC会帮助咱们作不少事情,可能会影响咱们的观察。
// MRC环境!!! int main(int argc, const char * argv[]) { @autoreleasepool { // Global:没有访问auto变量:__NSGlobalBlock__ void (^block1)(void) = ^{ NSLog(@"block1---------"); }; // Stack:访问了auto变量: __NSStackBlock__ int a = 10; void (^block2)(void) = ^{ NSLog(@"block2---------%d", a); }; NSLog(@"%@ %@", [block1 class], [block2 class]); // __NSStackBlock__调用copy : __NSMallocBlock__ NSLog(@"%@", [[block2 copy] class]); } return 0; } // Log 打印信息 --> __NSGlobalBlock__ ,__NSStackBlock__ ,__NSMallocBlock__
可是__NSStackBlock__访问了aotu变量,而且是存放在栈中的,上面提到过,栈中的代码在做用域结束以后内存就会被销毁,那么咱们颇有可能block内存销毁以后才去调用他,那样就会发生问题,经过下面代码能够证明这个问题。MRC 环境下的。
void (^block)(void); void test() { // __NSStackBlock__ int a = 10; block = ^{ NSLog(@"block---------%d", a); }; } int main(int argc, const char * argv[]) { @autoreleasepool { test(); block(); } return 0; } // Log 打印信息 :MRC 环境下 : block---------272632424 // Log 打印信息 :ARC 环境下 : block---------10
void (^block)(void); void test() { // __NSStackBlock__ 调用copy 转化为__NSMallocBlock__ int age = 10; block = [^{ NSLog(@"block---------%d", age); } copy]; [block release]; } int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... test(); block(); // Log 打印信息 : block---------10 } return 0; }
能够发现a的值变为了避免可控的一个数字。为何会发生这种状况呢?由于上述代码中建立的block是__NSStackBlock__类型的,所以block是存储在栈中的,那么当test函数执行完毕以后,栈内存中block所占用的内存已经被系统回收,所以就有可能出现乱得数据。查看其c++代码能够更清楚的理解。
为了不这种状况发生,能够经过copy将__NSStackBlock__类型的block转化为__NSMallocBlock__类型的block,将block存储在堆中,如下是修改后的代码。
void (^block)(void); void test() { // __NSStackBlock__ 调用copy 转化为__NSMallocBlock__ int age = 10; block = [^{ NSLog(@"block---------%d", age); } copy]; [block release]; } // Log 打印信息 : block---------10
那么其余类型的block调用copy会改变block类型吗?下面表格已经展现的很清晰了。
因此在平时开发过程当中MRC环境下常常须要使用copy来保存block,将栈上的block拷贝到堆中,即便栈上的block被销毁,堆上的block也不会被销毁,须要咱们本身调用release操做来销毁。而在ARC环境下回系统会自动copy,是block不会被销毁。
在ARC环境下,编译器会根据状况自动将栈上的block进行一次copy操做,将block复制到堆上。
会自动将block进行一次copy操做的状况。
block做为函数返回值时
typedef void (^Block)(void); Block myblock() { int a = 10; // 上文提到过,block中访问了auto变量,此时block类型应为__NSStackBlock__ Block block = ^{ NSLog(@"---------%d", a); }; return block; } int main(int argc, const char * argv[]) { @autoreleasepool { Block block = myblock(); block(); // 打印block类型为 __NSMallocBlock__ NSLog(@"%@",[block class]); } return 0; } Log 打印信息 :---------10 Log 打印信息 :__NSMallocBlock__
int main(int argc, const char * argv[]) { @autoreleasepool { // block内没有访问auto变量 Block block = ^{ NSLog(@"block---------"); }; NSLog(@"%@",[block class]); int a = 10; // block内访问了auto变量,但没有赋值给__strong指针 NSLog(@"%@",[^{ NSLog(@"block1---------%d", a); } class]); // block赋值给__strong指针 Block block2 = ^{ NSLog(@"block2---------%d", a); }; NSLog(@"%@",[block1 class]); } return 0; } Log 打印信息 :__NSGlobalBlock__ Log 打印信息 :__NSStackBlock__ Log 打印信息 :__NSMallocBlock__
NSArray *array = @[]; [array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { }];
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);