18:block的原理是怎样的?本质是什么?
封装了函数调用以及调用环境的oc对象
block本质也是一个oc对象,它的内部也有isa指针,它是封装了函数调用(函数调用地址)以及函数调用环境(block执行需要的参数)的oc对象
原理:block 的底层是一个__main_block_impl_0结构体
https://www.arclin.cn/post/5ed61a9.html
19:__block的作用是什么?有什么使用注意点?
被__block修饰的变量 在block内部可以被改变,因为在block内部被__block修饰的变量会被包装成一个__Block_byref_xxx_0的对象,这个对象内部有一个指向我们声明的变量的指针 指向的地址和我们声明的变量是同一个地址,修改的时候也是通过这个对象拿到指针做修改。block内部对这个对象是强引用的。而如果被修饰的变量是实例对象 那么这个对象内部会添加一个copy函数和dispose函数,在被copy到堆上时,这个对象的copy函数会根据被修饰实例对象的关键字来确定对实例对象是强引用还是弱引用。再block释放的时候,会调用dispose函数 来断开对实例对象的引用关系。需要注意的是__block只能修饰实例对象 还有一些内存管理问题 比如说修饰实例对象且该实例对象拥有该Block 会造成循环引用。而且在MRC下使用__block修饰的实例对象 不会被__Block_byref_xxx_0的对象retain操作。对象可能会被提前释放。
20:block的属性修饰词为什么是copy?使用block有哪些使用注意?
block一旦没有进行copy操作,就不会在堆上,只会在栈上,那么我们控制不了block的生命周期。注意引起循环引用。
21:block在修改NSMutableArray,需不需要添加__block?
不需要,因为只是对数组内容进行修改,而不是对数组指针指向进行修改
21.1 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制 (mrc环境)
变量类型 捕获到block内部 访问方式
auto √ 值传递
局部变量
static √ 指针传递
全局变量 × 直接访问
block会不会捕获,先看是局部变量还是全局变量,如果是局部变量则会捕获,全局就不会捕获
在函数外面定义的叫全局变量,函数里面定义的叫局部变量,(宏是一种字符替换,不属于全局变量)
21.2:block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
1:__NSGlobalBlock__ ( _NSConcreteGlobalBlock ) //存储在内存的 数据区域。 注意是“ - - “
2:__NSStackBlock__ ( _NSConcreteStackBlock ) // 存储在内存的 栈区。
3:__NSMallocBlock__ ( _NSConcreteMallocBlock ) // 存储在内存的 堆区。
21.3:如何区分block类型 (MRC环境下)
1:没有访问auto变量 就是 __NSGlobalBlock__ 类型
2:有访问auto变量 就是 __NSStackBlock__ 类型 //注意arc 与mrc状态下可能是不同的,arc模式下会可能自动copy到堆上,这里是只mrc状态下,真实类型
3:__NSStackBlock__调用了copy 就是 ___NSMallocBlock__ 类型
查看block类型 : [block class]
21.4:每一种类型的block调用copy后的结果如下所示 (MRC环境下)
block 的类型 副本源的配置存储域 复制效果
__NSGlobalBlock__ 程序的数据区域 什么也不做
__NSMallocBlock__ 堆 引用计数增加
__NSStackBlock__ 栈 从栈复制到堆
21.5 block的copy
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况:
1:block作为函数返回值时
2:将block赋值给__strong 指针时
3:block作为Cocoa API中方法名含有usingBlock的方法参数时// 例如:NSArray的遍历,enumerateObjctsUsingBlock 方法
4:block作为GCD API的方法参数时 // dispatch_once(&oncToken,^{...}); dispatch_after(... ,^{...});
(栈内存上的block不会对person对象进行强引用(arc下的说法,mrc通俗说是保命,局部对象person不被销毁,block被销毁后,统一清理person),堆内存上的block会对person对象进行强引用。)
21.6 : block的写法
1:MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
2:ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
21.7 : __block修饰符 (声明:age是block外部局部变量(auto变量) )
1:__block 可以用于解决block内部无法修改auto变量值的问题(无法修改外部auto局部变量的值)
2:__block不能修饰全局变量,静态变量(static)
3:编译器会将__block变量包装成一个对象.(__main_block_impl_0结构体里存的是*age指针(与没有用__block修饰的age存储不一样,没有用__block修饰的age直接存的是值)指向__Block_byref_age_0结构体,然后将auto变量age封装到结构体对象里,然后让__forwarding指针,指向对象)
//block不能直接修改外部局部变量值,因为block编译后是__main_block_impl_0结构体,所在函数与age(auto局部变量)不在同一个函数,所以不能直接修改局部变量age的值。通俗的说block内部访问局部变量age是通过局部变量值的捕获进来的,传递的是值,而不是地址指针,所以不能去修改外部auto变量age的值,因为没有访问外部auto局部变量age的地址指针,如果想修改可以将age变成静态变量或者全部变量等,也可以通过__block来访问。
网友评论