Men的博客

欢迎光临!

0%

Block理解

本质:带有自动变量值的匿名函数
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 好读。