本质:带有自动变量值的匿名函数
block的实质是一个oc对象,它有自己的isa指针、invoke变
量 ,是NSObject的子类,可以接收消息
1._NSConcreteGlobalBlock,全局的静态block,不会访问外部的变量。
就是说如果你的block没有调用其他 的外部变量,那你的block类型就是这
种。例如:你仅仅在你的block里面写一个NSLog("hello world");
2._NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销
毁。这个block就是你声明的时候不用copy修饰,并且你的block访问了外部
变量。
3._NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时
会被销毁。
写法:
void (^block)(void) = ^{
NSLog(@"无参无返回值");
};
void (^xiaoming1aa)() = ^(NSString *c){
NSLog(@"带参数");
};
NSString *(^xiaming2)() = ^NSString *(NSString *dd) {
return @"带参数与返回";
};
@property (nonatomic, copy)
void(^myBlock)();//无参无返回值
@property (nonatomic, copy)
void(^myBlock1)(NSString *);//带参数
@property (nonatomic, copy)
NSString *(^myBlock2)(NSString *);//带参数与返回
block为什么不能修改外部变量
默认情况下,在block中访问的外部变量是复制过去的,
复制过去的是值,即:写操作不对原变量生效。
__block都做了什么?
Block不允许修改外部变量的值,这里所说的外部变量的值,
指的是栈中指针的内存地址。__block 所起到的作用就是
只要观察到该变量被 block 所持有,就将“外部变量”
在栈中的内存地址放到了堆中。
进而在block内部也可以修改外部变量的值
这也保证了原对象不被销毁,但与此同时,也会导致循环引用问题
block为什么为引起循环引用
因为block在拷贝到堆上的时候,会retain其引用的外部变量,
那么如果block中如果引用了他的宿主对象,
那很有可能引起循环引用
循环引用发生的条件就是持有这个block的对象,
被block里边加入的对象持有
如何解决循环引用
使用__weak修饰self,使其在block中不被持有,打破循环引用。
__weak typeof(self) weakSelf = self;
Block为什么要用copy修饰
针对block的三种类型,
也就是copy修饰的block。他的生命 周期就是随着对象的销毁而结束的。只要
对象不销毁,我们就可以调用的到在堆中的block。这就是为什么我们要用
copy来修饰block。因为不用copy修饰的访问外部变量的block,只在他所在
的函数被调用的那一瞬间可以使用。之后就消失了。
block执行过程中对象被释放
在block执行开始时self对象还未被释放,而执行过程中,
self被释放了,此时访问self时,就会发生错误。
对于这种场景,应该在block中对 对象使用__strong修饰,
使得在block期间对 对象持有,block执行结束后,解除其持有。
注:此方法只能保证在block执行期间对象不被释放,
如果对象在block执行执行之前已经被释放了,该方法也无效。
什么情况下不回发生循环引用
通用情况:在block本身不被self持有,而被别的对象持有,
同时不产生循环引用的时候,就不需要使用weakself了
UIView动画block,AFN的block
UIView的调用的是类方法,当前控制器不可能强引用一个类 ,
所以循环无法形成 --> 动画block不会造成循环引用的原因。
函数和block区别
函数里面只能访问全局变量,而Block代码块不光能访问全局变量,
还拥有当前栈内存和堆内存变量的可读性
(当然通过__block访问指示符修饰的局部变量还可以在block代码块里面进行修改)。
从内存的角度看,函数指针只不过是指向代码区的一段可执行代码,
而block实际上是程序运行过程中在栈内存动态创建的对象,
可以向其发送copy消息将block对象拷贝到堆内存,
以延长其生命周期。
如何区分block位置
要点一:
当block在函数内部,
且定义的时候就使用了函数内部的变量,
那么这个block是存储在栈上的。
要点二:
当block定义在函数体外面,
或者定义在函数体内部且当时函数执行的时候,
block体中并没有需要使用函数内部的局部变量时,
也就是block在函数执行的时候只是静静地待在一边定义了一下
而不使用函数体的内容,
那么block将会被编译器存储为全局block。
要点三:
全局block存储在堆中,
对全局block使用copy操作会返回原函数指针;
而对栈中的block使用copy操作,
会产生两个不同的block地址,
也就是两个匿名函数的入口地址。
要点四:
ARC机制优化会将stack的block,
转为heap的block进行调用。
delegate为什么用weak 它和block区别
weak是为了解决循环引用的问题
代理和block的共同特性是回调机制,不同的是,代理的方法比较多,比较分
散,使用block的代码比较集中统一;代理运行成本比较低,一对一的关系,
block需要将使用的数据从栈内存拷贝到堆内存中,对象的话就是加计数,代理
制是保存一个对象指针,直接回调;block写法简单,注重传输,但是需要注意
循环引用
总结:公共接口,方法比较多的时候用delegate进行解耦,异步和简单的回调用block
在block中对weakSelf再进行—strong的操作,然后以百万级的数量去调用某个方法,与直接用weakSelf去调用有什么区别
在block中调用self会引起循环引用,但是在block中需要对
weakSelf进行strong,保证代码在执行到block中,self不会被
释放,当block执行完后,会自动释放该strongSelf
进行大量调用会引起内存问题
block嵌套block还用——strong两遍吗
只要在最后把外层的block执行一次, 内层的block才会真正地被
加载进内存, 循环引用问题再次出现. 出现循环引用的原因其实也
不难理解, 因为strongPerson说白了也是一个强引用, 它与一般
强引用的区别在于, 它只会在被定义的block中对对象进行强引用,
在block过后就会把对象释放掉, 所以在第2层block中继续用
strongPerson出现循环引用跟一般造成循环引用的原因其实是一
样的, 解决方法也是如出一辙, 而且可以继续嵌套下去, 此处的警
告同样是结果没被使用:
加上修饰符strong时,当别处把变量释放掉,但调用该变量的
block如果仍然没有执行结束,那么系统就会等待block执行完成后
再释放,对该变量在block中的使用起到了保护作用。当block执行
结束后会自动释放掉。
block捕获进去静态变量,局部变量,全局变量以后,在block内部以什么形式存在
在执行Block语法的时候,Block语法表达式所使用的自动变量的值
是被保存进了Block的结构体实例中,也就是Block自身中
Block仅仅捕获了val的值,并没有捕获val的内存地址。
block 和 delegate使用优先级
block 和 delegate 都可以通知外面。block 更轻型,使用更
简单,能够直接访问上下文,这样类中不需要存储临时数据,使用
block 的代码通常会在同一个地方,这样读代码也连贯。
delegate 更重一些,需要实现接口,它的方法分离开来,很多时
候需要存储一些临时数据,另外相关的代码会被分离到各处,没有
block 好读。