8.iOS开发之block
block简介
block(闭包)的本质是对象,是带有自动变量(局部变量)的匿名函数。
block与变量
值拷贝 会自动生成相应的属性来捕获外界变量,外部局部变量的变化不会影响它的的状态,block内部不能修改变量的值。
使用__block修饰符可以修改自动变量的值,是跟局部static变量一样指针传递。
全局变量则是直接访问。
对于捕获ObjC对象,不同于基本类型,Block内部访问了对象类型的auto对象时,如果Block是在栈上,将不会对auto对象产生强引用;当Block被拷贝到堆上,Block会调_Block_object_assign函数,根据auto对象的修饰符做出相应的操作;如果Block从堆上移除,会调用block内部的dispose函数,内部会调用_Block_object_dispose函数,这个函数会自动释放引用的auto对象。
解决block的循环引用
1.使用最简单的weak关键字修饰
(会遇到在block中self还需要使用然而被释放)
可在block作用域内对weakSelf进行strong修饰的临时变量 weak-strong-dance强弱共舞
self.name = @"name";
__weaktypeof(self) weakSelf =self;
self.block= ^{
__strongtypeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"name:%@",strongSelf.name);
});
}
2.用__block修饰中间者变量使用,并在block中置为nil
self.name = @"name"; __block ViewController *vc = self; self.block = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"name:%@",vc.name); vc = nil; }); };
3.使用传参把self传入
self.name = @"name"; self.block = ^(ViewController *vc){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"name:%@",vc.name); }); };
self.block(self);
Block与内存管理
根据Block在内存中的位置分为三种类型:
NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中。
NSStackBlock是位于栈区,超出变量作用域,栈上的Block以及 __block变量都被销毁。
NSMallocBlock是位于堆区,在变量作用域结束时不受影响。
Block的复制
在全局block调用copy什么也不做
在栈上调用copy那么复制到堆上
在堆上调用block 引用计数增加
Block从栈中复制到堆的几种情况:
调用Block的copy实例方法时
Block作为函数返回值返回时
在带有usingBlock的Cocoa方法或者GCD的API中传递Block时候
将block赋给带有__strong修饰符的id类型或者Block类型时
Block的底层原理
研究工具:clang
block结构体成员:
isa,指向所属类的指针,也就是block的类型
Flags,标志变量,在实现block的内部操作时会用到
Reserved,保留变量
FuncPtr,block执行时调用的函数指针
包含了isa指针(包含isa指针的皆为对象),也就是说block也是一个对象(runtime里面,对象和类都是用结构体表示)。