参考文献
Block本质
Block由浅入深(5):三种类型的Block
Block由浅入深(3):Block捕获局部变量
iOS基础:block 内如何修改 block 外部变量
Block原理,为什么block能捕获变量 -- 原理篇
Block原理,为什么block能捕获变量 -- 实战篇
block在内部会作为一个指向结构体的指针 当调用block的时候其实就是根据
block对应的指针找到相应的函数 进而进行调用 并传入自身
block本质上是一个OC对象 它内部也有个isa指针
block是封装了函数调用以及函数调用环境的OC对象
block是封装函数及其上下文的OC对象


self是当调用block函数的参数 参数是局部变量 self指向调用者
block捕获局部变量的本质
block体内的变量与被block引用的外部局部变量是2个不同的变量 它们有不同
的作用域 有不同的存储空间 它们的值早在block实现时就已经确定好了,而不
是在block执行时才被确定的

block为什么不可以修改局部变量
局部变量:
__block_impl_0 实现中会增加一个成员变量,用来存储 block 外部变量的
值,仅仅是一次值传递,在 block 内部对其操作会报错。
全局变量:
全局变量在 __block_impl_0 实现中并没有出现,因为全局变量存储在静态
数据区,程序结束前并不会被销毁,block 可直接访问,因此在
__block_impl_0 结构体中没有体现。
局部静态变量:
__block_impl_0 结构体中会增加一个指针变量,在 _block_func_0 中通过
局部变量的地址可以对其进行访问修改,但其作用域就是当前所在函数的作用
域
block的类型
__NSGlobalBlock__ (_NSConcreteGlobalBlock)
__NSStackBlock__ (_NSConcreteStackBlock)
__NSMallocBlock__(__NSConcreteMallocBlock)


如何判断block是哪种类型
- 没有访问auto变量的block 是__NSGlobalBlock__ 放在数据段
- 访问了auto变量的block是___NSStackBlock__
- [NSStackBlock __copy] 操作就变成了__NSMallocBlock__
block作为返回值时或者赋值给一个strong/copy修饰的对象会自动调用copy
MRC下手动调用copy 也会到达堆区 MRC下赋值不会触发copy操作 所以 MRC 下不手动调copy就是_NSConcreteStackBlock
在ARC下 block作为返回值或者赋值给一个strong/copy修饰的对象会自动调用copy 所以 ARC下大多数都是_NSConcreteMallocBlock
__block修饰符作用
1.__block可以用于解决block内部无法修改auto变量值的问题
2.__block不能修饰全局变量 静态变量
3.编译器会将__block变量包装成一个对象
4.__block修改变量 age->__forwarding->age
5.__Block_byref_age_0结构体内部地址和外部变量age是同一个地址

_block的实现原理
_block将变量包装成对象 然后在把捕获到的变量封装到block的结构体里面
block内部存储的变量为结构体指针 也就可以通过指针找到内存地址进而修改
变量的值
_block修饰变量或者对象对象类型的变量都是直接strong 而对象的引用类型 取决于block捕获的对象类型的变量
block的属性修饰词为什么是copy
block一旦没有进行copy操作 就不会在堆上
block在堆上 程序员就可以对block做内存管理等操作 可以控制block的生命
周期
当block被copy到堆时 对__block修饰的变量做了什么
1.会调用block内部的copy函数
2.copy函数内部会调用__Block_object_assign函数
3._block_object_assign函数会对__block变量形成强引用
4.对__block修饰的变量assign函数对其强引用 对于外部对象assig函数根据
外部如何引用而引用


当block从堆上移除时 对__block修饰的变量做了什么
1.会调用block内部的dispose函数
2.dispose函数内部会调用__Block_object_dispose函数
3.__Block_object_dispose函数会自动释放引用的_block变量(release)


block对象类型的auto变量 __block变量的区别
1.当block在栈上时 对他们都不会产生强引用
2.当block拷贝到堆上时
- 都会通过copy函数来处理它们
- 对于_block修饰的变量assign函数对其强引用 对于外部变量assign函数
根据外部变量如何引用而引用
3.当block从堆上移除时
- 都会通过dispose函数来释放它们
4.__block的__forwarding指针
- 栈上__block的__forwarding指向本身
- 栈上__block复制到堆上后 栈上block的__forwarding指向堆上block
堆上的block的_forwarding指向本身

arc下如何解决block循环引用的问题
三种方式 __weak __unsafe_unretained
_block
第一种方式 __weak
Person *person = [[Person alloc] init];
// __weak Person *weakPerson = person;
__weak typeof(person) weakPerson = person;
person.block = ^{
NSLog(@"age is %d", weakPerson.age);
};
第二种方式:__unsafe_unretained
__unsafe_unretained Person *person = [[Person alloc] init];
person.block = ^{
NSLog(@"age is %d", weakPerson.age);
};
第三种方式:__block
__block Person *person = [[Person alloc] init];
person.block = ^{
NSLog(@"age is %d", person.age);
person = nil;
};
person.block();
三种方式比较
1.__weak 不会产生强引用 指向的对象销毁时 会自动让指针置nil
2.__unsafe_unretained 不会产生强引用 不安全 指向的对象销毁时 指针
存储的地址值不变
3.__block 必须吧引用对象置为nil 并且要调用该block


MRC下解决block循环引用的问题
两种方式 __unsafe_unretained __block
第一种方式__unsafe_unretained
__unsafe_unretained Person *person = [[Person alloc] init];
person.block = ^{
NSLog(@"age is %d", weakPerson.age);
};
第二种方式:__block
__block Person *person = [[Person alloc] init];
person.block = ^{
NSLog(@"age is %d", person.age);
};
C语言变量
- 自动变量
- 函数参数
- 静态变量
- 静态全局变量
- 全局变量

在block中改变变量值的方式
1.传递内存地址指针到block中
2.改变存储区方式
应用程序内存分配
系统默认调用copy方法把block复制的情况
1.手动调用copy
2.block是函数的返回值
3.block被强引用 block被赋值给_strong或者id类型
4.调用系统API入参中含有usingblock的方法
copy函数把block从栈上拷贝到堆上 dispose函数是把堆上的函数在废弃的时候销毁掉 堆空间的block自己销毁之后也会对持有的对象进行release操作
_Block_object_assign函数调用时机及作用
1).当block进行copy操作的时候就会自动调用_main_block_desc_0内部的
_main_block_copy_0函数 _main_block_copy_0函数内部会自动调用
_block_object_assign函数
2)._Block_object_assign函数会自动根据__main_block_impl_0结构体的
捕获对象是什么类型的指针,对捕获对象产生强引用或者弱引用。可以理解为
_Block_object_assign函数内部会对捕获的对象进行引用计数器的操作,如
果__main_block_impl_0结构体内捕获的对象指针是__strong类型,则为强
引用,引用计数+1,如果__main_block_impl_0结构体内对象指针是__weak
类型,则为弱引用,引用计数不变
_Block_object_dispose函数调用时机及作用
1).当block从堆中移除时就会自动调用__main_block_desc_0中的
__main_block_dispose_0函数,__main_block_dispose_0函数内部会调用
_Block_object_dispose函数。
2)._Block_object_dispose会对捕获对象做释放操作,类似于release,也
就是断开对person对象的引用,而person究竟是否被释放还是取决于person
对象自己的引用计数。
总结Block的调用
1).一旦block中捕获的变量为对象类型,block结构体中的
__main_block_desc_0会出两个参数copy和dispose。因为访问的是个对象,
block希望拥有这个对象,就需要对对象进行引用,也就是进行内存管理的操作
比如说对对象进行retain操作,因此一旦block捕获的变量是对象类型就会自
动生成copy和dispose来对内部引用的对象进行内存管理。
2).当block内部访问了对象类型的auto变量时,如果block是在栈上,block
内部不会对person产生强引用。不论block结构体内部的变量是__strong修饰
还是__weak修饰,都不会对变量产生强引用。
3).如果block被拷贝到堆上。copy函数会调用_Block_object_assign函数,
根据auto变量的修饰符(__strong,__weak,unsafe_unretained)做出相
应的操作,形成强引用或者弱引用
4)如果block从堆中移除,dispose函数会调用_Block_object_dispose函数
自动释放引用的auto变量 , 也会调用对象类型的dispose , 对象的引用计数
减1
网友评论