Block笔记 前置知识点 栈: 由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。
堆区(heap) :一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。
全局区/静态区 :顾名思义,全局变量和静态变量存储在这个区域。只不过初始化的全局变量和静态变量存储在一块,未初始化的全局变量和静态变量存储在一块。程序结束后由系统释放。
文字常量区 :这个区域主要存储字符串常量。程序结束后由系统释放。
程序代码区 :这个区域主要存放函数体的二进制代码。
What block什么? Block 是带有自动变量的匿名函数。
匿名函数 (英语:anonymous function)是指一类无需定义标识符 (函数名)的函数 或子程序 ,普遍存在于多种编程语言中。OC中用block 表示
自动变量 (Automatic Variable )指的是局部作用域 变量 ,具体来说即是在控制流 进入变量作用域时系统自动为其分配存储空间 ,并在离开作用域时释放空间的一类变量。在许多程序语言 中,自动变量与术语“局部变量 ”(Local Variable )所指的变量实际上是同一种变量,所以通常情况下“自动变量”与“局部变量”是同义的。自动变量在C与C++中的实现即为“自动变量”(Automatic Variable )。默认情况下,在代码块 内声明的变量都是自动变量,但亦可用自动变量的关键字auto明确标识存储类 [1] ;而如若使用register(而非auto)存储类标识代码块内的变量,编译器 就会将变量缓存 于处理器 内的寄存器 中,此种情况下不能对该变量或其成员变量 使用引用操作符 &以获取其地址,因为&只能获取内存空间中的地址;除此以外,由于寄存器的数量及其所能存储的数据类型 受硬件 限制而可能无法存储指定变量,编译器可以忽略声明 内的register关键字。对于一个未初始化 的自动变量来说,在为其赋值 之前其值都为undefined(未定义)[2] 。
在C++中,如谚语“资源获取即初始化 ”(Resource Acquisition Is Initialization ,常缩写为RAII)所述,自动变量的构造函数 在程序运行至声明部分的时候才会被调用,而当程序运行至所给的程序块末端的时候则调用析构函数 ,这一特性常应用于资源的分配与释放管理,如自动关闭已开启的文件 或自动释放空闲内存。
形如 ^ 返回值类型 (参数列表) {表达式}
(我想起高中的一位数学老师定义概念时候的用法)
1 2 3 ^ int (int count) { return count + 1; };
可以省略返回值和参数,最终如下
1 2 3 ^ { NSLog(@"No Parameter"); };
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int a = 0; //全局初始化 char *p1; //全局未初始化区 int main(int argc, const char * argv[]) { @autoreleasepool { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456 在常量区,p3在栈区 static int c = 0; //全局静态初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(10); // 手动分配是分配在堆区 strcpy(p1, "123456"); // 123456在常量区m,这个函数的作用是将“123456”。p3指向的“123456”与这里的123456可能被编译器优化成一个地址 NSLog(@"%c",*p1); } return 0; }
有兴趣自看源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct __ViewController__viewDidLoad_block_impl_0 { struct __block_impl impl ; struct __ViewController__viewDidLoad_block_desc_0 * Desc ; NSMutableArray *array ; __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, NSMutableArray *_array, int flags=0 ) : array (_array) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) { NSMutableArray *array = __cself->array ; ((void (*)(id, SEL, NSUInteger))(void *)objc_msgSend)((id)array , sel_registerName("removeObjectAtIndex:" ), (NSUInteger)0 ); } static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) { _Block_object_assign((void *)&dst->array , (void *)src->array , 3 ); } static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void *)src->array , 3 );}static struct __ViewController__viewDidLoad_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*); void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*); } __ViewController__viewDidLoad_block_desc_0_DATA = { 0 , sizeof (struct __ViewController__viewDidLoad_block_impl_0), __ViewController__viewDidLoad_block_copy_0, __ViewController__viewDidLoad_block_dispose_0}; static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) { ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController" ))}, sel_registerName("viewDidLoad" )); NSMutableArray *array = ((NSMutableArray *(*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray" ), sel_registerName("alloc" )), sel_registerName("initWithObjects:" ), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_f7___d4s9f93n5c4xh77y4hd5880000gp_T_ViewController_7587fa_mi_0, (NSString *)&__NSConstantStringImpl__var_folders_f7___d4s9f93n5c4xh77y4hd5880000gp_T_ViewController_7587fa_mi_1, __null); NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7___d4s9f93n5c4xh77y4hd5880000gp_T_ViewController_7587fa_mi_2, ((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array , sel_registerName("count" ))); void (*blk)(void ) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, array , 570425344 )); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7___d4s9f93n5c4xh77y4hd5880000gp_T_ViewController_7587fa_mi_3, ((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array , sel_registerName("count" ))); }
提取关键信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor ; };
Block的类型 Block常见的一般有三种类型:NSConcreteGlobalBlock
、_NSConcreteStackBlock
、NSConcreteMallocBlock
。还有几种在GC环境下使用的_NSConcreteFinalizingBlock
_NSConcreteAutoBlock``_NSConcreteWeakBlockVariable
。
_NSConcreteGlobalBlock
1 2 3 4 5 6 if (!block->hasCaptures()) { info.StructureType = llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true ); info.CanBeGlobal = true ; return ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 统计需要布局(layout)的变量: * `this` (为了访问 `c++` 的成员变量和函数,需要 `this` 指针) * 依次按下列规则处理捕获的变量: * 不需要计算布局的变量: * 生命周期为静态的变量(被 `const` `static` 修饰的变量,不被函数包含的静态常量,c++中生命周期为静态的变量) * 函数参数 * 需要计算布局的变量:被 `__block` 修饰的变量,以上未提到的类型(比如block) **Tips**:当需要布局(layout)的变量的统计完毕后,会按照以下顺序进行一次稳定排序。 * __strong 修饰的变量 * ByRef 类型 * __weak 修饰的变量 * 其它类型
_NSConcreteMallocBlock 在非垃圾收集环境下,当 _NSConcreteStackBlock
类型的block 被真正复制时,在 _Block_copy_internal
方法内部,会转换为 _NSConcreteMallocBlock
libclosure-65/runtime.c
1 2 3 4 5 6 7 8 9 10 11 12 if (!isGC) { struct Block_layout *result = malloc (aBlock->descriptor->size); if (!result) return NULL ; memmove(result, aBlock, aBlock->descriptor->size); result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); result->flags |= BLOCK_NEEDS_FREE | 2 ; result->isa = _NSConcreteMallocBlock; _Block_call_copy_helper(result, aBlock); return result; }
_NSConcreteFinalizingBlock&_NSConcreteAutoBlock 在垃圾收集环境下,当 block 被复制时,如果block 有 ctors & dtors 时,则会转换为 _NSConcreteFinalizingBlock
类型,反之,则会转换为 _NSConcreteAutoBlock
类型
1 2 3 4 5 6 if (hasCTOR) { result->isa = _NSConcreteFinalizingBlock; } else { result->isa = _NSConcreteAutoBlock; }
_NSConcreteWeakBlockVariable GC环境下,当对象被 __weak __block
修饰,且从栈复制到堆时,block 会被标记为 _NSConcreteWeakBlockVariable
类型。
1 2 3 4 5 6 7 8 9 10 bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false , isWeak);copy->flags = src->flags | _Byref_flag_initial_value; copy->forwarding = copy; src->forwarding = copy; copy->size = src->size; if (isWeak) { copy->isa = &_NSConcreteWeakBlockVariable; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 NSLog(@"\n--------------------全局Block---------------------\n"); void (^blk)(void) = ^{ NSLog(@"Global Block"); }; blk(); NSLog(@"Global Block Addres:%p", blk); NSLog(@"%@", [blk class]); NSLog(@"\n--------------------堆Block---------------------\n"); int i = 1; void (^blka)(void) = ^{ NSLog(@"Malloc Block, %d", i); }; blka(); NSLog(@"Malloc Block Address:%p", blka); NSLog(@"%@", [blka class]); NSLog(@"\n--------------------栈Block---------------------\n"); __weak void (^blkb)(void) = ^{ NSLog(@"Stack Block, %d", i); }; blkb(); NSLog(@"Stack Block Address:%p", blkb); NSLog(@"%@", [blkb class]);
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 2018-09-11 17:02:46.791542+0800 BlockDemo[37483:492370] --------------------全局Block--------------------- 2018-09-11 17:02:46.791670+0800 BlockDemo[37483:492370] Global Block 2018-09-11 17:02:46.791761+0800 BlockDemo[37483:492370] Global Block Addres:0x1098a70c8 2018-09-11 17:02:46.791836+0800 BlockDemo[37483:492370] __NSGlobalBlock__ 2018-09-11 17:02:46.792008+0800 BlockDemo[37483:492370] --------------------堆Block--------------------- 2018-09-11 17:02:46.792151+0800 BlockDemo[37483:492370] Malloc Block, 1 2018-09-11 17:02:46.792288+0800 BlockDemo[37483:492370] Malloc Block Address:0x60800025a910 2018-09-11 17:02:46.792410+0800 BlockDemo[37483:492370] __NSMallocBlock__ 2018-09-11 17:02:46.792653+0800 BlockDemo[37483:492370] --------------------栈Block--------------------- 2018-09-11 17:02:46.792742+0800 BlockDemo[37483:492370] Stack Block, 1 2018-09-11 17:02:46.792942+0800 BlockDemo[37483:492370] Stack Block Address:0x7ffee6357a10 2018-09-11 17:02:46.793101+0800 BlockDemo[37483:492370] __NSStackBlock__
But why?
Why 从数据结构上分析
copy stack to heap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // Copy, or bump refcount, of a block. If really copying, call the copy helper if present. void *_Block_copy(const void *arg) { struct Block_layout *aBlock; if (!arg) return NULL; // The following would be better done as a switch statement aBlock = (struct Block_layout *)arg; if (aBlock->flags & BLOCK_NEEDS_FREE) { // latches on high latching_incr_int(&aBlock->flags); return aBlock; } else if (aBlock->flags & BLOCK_IS_GLOBAL) { return aBlock; } else { // Its a stack block. Make a copy. struct Block_layout *result = malloc(aBlock->descriptor->size); if (!result) return NULL; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1 _Block_call_copy_helper(result, aBlock); // Set isa last so memory analysis tools see a fully-initialized object. result->isa = _NSConcreteMallocBlock; return result; } }
block的实现原理是C语言的函数指针。 函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。
Block是NSObject的子类,拥有NSObject的所有属性,所以block对象也有自己的生命周期,生存期间也会被持有和释放。
How 吹了半天牛B,这玩意有啥用啊?听我慢慢道来!
参考资料 https://blog.csdn.net/Deft_MKJing/article/details/53165799
iOS block之三种block
Block的使用