O、题:block的原理是怎样?本质是什么?
答:封装了函数调用以及调用环境的OC对象
O.a、题:__block的作用是什么?有什么使用注意点?
答:__block会将修饰的变量包装成一个
__Block_byref_xxx
对象,使用的时候要注意内存管理。
O.b、题:block的属性修饰词为什么是copy?使用block有哪些使用注意?
答:1. block一旦没有进行copy操作,就不会在堆上。 2. 使用注意:循环引用问题
O.c、题:block在修改NSMutableArray,需不需要添加__block?
答:不需要
block的本质是什么:
- block本质上也是一个OC对象,它内部也有个isa指针。
- block是封装了函数调用以及函数调用环境的OC对象
- block的底层结构图如下:
block的类型:
block有三种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承NSBlock类型。
继承关系:
__NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
-
__NSGlobalBlock__
(NSConcreteGlobalBlock) -
__NSStackBlock__
(NSConcreteStackBlock) -
__NSMallocBlock__
(NSConcreteMallocBlock)
三种类型的block在内存中的布局:
block布局位置三种类型的block的区别:
block的区别每种类型的block调用copy
后的结果:
copy
全局变量:数据段
局部变量:栈段
对象:堆段
class对象:数据段
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如一下情况:
- block作为函数返回值。
- 将block赋值给__strong指针。
- block作为Cocoa API中方法名含义usingBlock的方法参数
- block作为GCD API的方法参数
// 3
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
// 4
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
在MRC环境下,blcok属性建议的写法:
@property (nonatomic, copy) void (^block) void;
在ARC环境下,blcok属性建议的写法:
@property (nonatomic, copy) void (^block) void;
@property (nonatomic, strong) void (^block) void;
当block内部访问了对象类型的auto变量
- 如果block是在栈上,将不会对auto变量产生强引用
- 如果block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据auto变量的修饰符(__strong,__weak, __unsafe unretained)做出相应的操作,类似于retain(形成强引用,弱引用)
- 如果block从堆上移除
- 会调用block内部dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的auto变量,类似于release
__block修饰符
- __block可以用于解决block内部无法修改auto变量值的问题
- __block不能用来修饰全局变量、静态变量(static)
- 编译器会将__block修饰的变量包装成一个对象
__block修饰符的内存管理
- 当block在栈上面的时候,并不会对__block变量产生强引用
- 当block被copy到堆上面时:
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量产生强引用(retain)
- 当block从堆上移除时:
- 会调用block内部dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放__block变量产生(release)
内存管理(释放)
对象类型的auto变量,__block变量
- 当block在栈上的时候,都不会对他们产生强引用
- 当block拷贝到堆上的时候,都会通过copy函数来处理他们
- __block变量(假设变量名为a)
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
- 对象类型的auto变量(假设变量名叫做p)
_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
- 当block从堆上移除时,都会通过dispose函数来释放它们
- __block变量(假设变量名叫做a)
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
- 对象类型的auto变量(假设变量名叫做p)
_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
内存管理
__block修饰的对象类型
-
当__block变量在栈上时,不会对指向的对象产生强引用
-
当__block变量被copy到堆时
- 会调用__block变量内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
-
如果__block变量从堆上移除
- 会调用__block变量内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放指向的对象(release)
循环引用
循环引用- 解决循环引用问题(ARC)
- 用__weak、__unsafe_unretained解决
-
用__block解决(必须要调用block)
__weak : 不会产生强引用,指向的对象销毁时,会自动让指针置为nil。
__unsafe_unretained : 不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。
- 解决循环引用问题(MRC)
- 用__unsafe_unretained解决
-
用__block解决
网友评论