1.block的原理是什么?本质是什么?
block本质上也是一个OC对象,它内部也有一个isa指针,
block是封装了函数调用以及函数调用环境的OC对象


2.block的类型
block有三种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型




3.block的属性修饰词为什么是copy?使用block有哪些使用注意?
block没有copy是不会在堆上的,
使用注意:循环引用问题
另外
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
-block作为函数返回值时
-将block赋值给__strong指针时
-block最为Cocoa API中方法名含有usingBlock的方法参数时
-block作为GCD API的方法参数时
--------------------------
MRC下block属性的建议写法
@property (copy,nonatomic) void (^block)(void);
ARC下block属性的建议写法
@property (copy,nonatomic) void (^block)(void);
@property (strong,nonatomic) void (^block)(void);
MRC下手动调用copy也会到达堆区,MRC下赋值不会触发copy操作,所以MRC下不手动调用个copy就是_NSConcreteStackBlock
4.block在修饰NSMutableArray,需不需要添加__block?
不需要,因为block会把指针捕获到block中,NSMutableArray相应的操作只是使用指针,并没有改变指针,但是如果要把数组置为nil则会编辑报错 Variable is not assignable (missing __block type specifier)(变量不可赋值)
5.为了能够保证block内部能够正常访问外部的变量,block有个变量捕获机制


6.block改变外部变量值的两种方式?
1.传递内存地址到block中(捕获变量)
2.改变存储区的方式(__block)
将auto从栈copy到堆,
原因:栈中内存管理是有系统管理,出了作用域就会被清除,堆中才是可以由程序员管理,
7.block不允许修改外部变量的值?
原因:->
-block本质上是一个对象,block的花括号区域是对象内部的一个函数,变量进入花括号,实际就是已经进入另一个函数区域---改变了作用域
-在几个作用域之间进行切换呢时,如果不加上这样的限制,变量的可维护性将大大降低
-比如想在block内部声明了一个与外部同名的变量,此时是允许还是不允许呢?只有加上了这样的限制,这样的场景才能实现
-所以Apple在编译器层面做了限制,如果block内部试图修改auto变量(无修饰符),那么直接编译报错
-可以把编译器的这种行为理解为:对block内部捕获到的auto变量设置为只读属性----不允许直接修改
8.如何实现对外部变量的捕获?
-将变量设置为全局变量.原理:block内外可直接访问全局变量
-加static(放在静态存储区/全局初始化区),原理是block内部都外部的auto变量进行指针捕获
最优解:使用 __block关键词修饰
9.__block修饰符
-__block可以用于解决block内部无法修改auto变量值的问题
-__block不能修饰全局变量,静态变量(static)(__block attribute not allowed, only allowed on local variables)
-将auto变量封装为结构体(对象),在结构体内部新建一个同名的auto变量
-block内部截获该结构体的指针
-在block中使用自动变量时,使用指针指向的结构体中的自动变量
__block int var = 10;
void(^blk)(void) = ^{
var = 20;
};
blk();
转换后的代码:
struct __Block_byref_var_0 {
void *__isa;
__Block_byref_var_0 *__forwarding;
int __flags;
int __size;
int var; // 10 => 20 该结构体持有相当于原来自动变量的成员变量
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_var_0 *var; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

10.block在修饰NSMutableArray需不需要添加__block
-不需要
-当变量时一个指针的时候,block里只是复制了一份这个指针,两个指针指向同一个地址
-所以,在block里面对指针呢指向内容做的修改,在block外面也一样有效
11.block是如何捕获局部变量的?
-block捕获外界变量个时,在内部会自动给生成一个属性来保存
12.UIView动画中block回调里self要不要弱引用?
-不需要,它不会造成过循环引用,因为它是类方法
-之所以需要弱引用本身,是因为怕对象之间产生循环引用,当前控制器不可能强引用一个类,所以循环无法形成
13.block里面会不会存在self为空的情况(weak strong的原理)?
__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
__strong typeof(weakself) strongself = weakself;
[strongself.dataSource reloadDataWithCompletion:nil];
}];
-有时候weakSelf在block里执行reloadDataWithCompletion的时候还存在
-但在执行reloadDataWithCompletion前,可能会被释放掉
-为了保证self在block执行过程中一直存在,对他强引用strongself
14.__block与__weak的区别?
-__block不管时ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型
-__weak只能在ARC模式下使用,也只能修饰对象(NSStringg),不能修饰基本数据类型(int)
-__block对象可以在block中被重新赋值,__weak不可以
15.多层block嵌套如何使用weakSelf?
__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
__strong typeof(weakself) strongself = weakself;
__weak typeof(self) weakSelf2 = strongself;
[strongself.dataSource reloadDataWithCompletion:^(BOOL result) {
__strong typeof(self) strongSelf2 = weakSelf2;
}];
}];
16.Masonry对于block内部引用self会不会造成循环引用?
-不会
-这个block没有copy,是在栈上,使用完直接释放了.
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
17.解决block的循环引用的方式
第一种
__weak typeof(self) weakSelf = self;
self.block = ^(NSString *name) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.name = name;
};
self.block(@"name1");
第二种(注意,如果不执行block也会导致内存泄漏)
__block Person *p = self;
self.block = ^(NSString *name) {
p.name = name;
p = nil; //最后释放掉p变量
};
self.block(@"name1");
第三种
self.block = ^(Person *p, NSString *name) {
p.name = name;
};
self.block(self, @"name1");
18.对象类型的auto变量以及__block的内存管理
当block内部访问了对象类型的auto变量时
如果block是在栈上个,将不会对auto变量产生强引用(因为block会随时被释放)
如果block被拷贝到堆上
-会调用block内部的copy函数
-copy函数您内部会调用_Block_object_assign函数
-_Block_object_assign函数会根据auto变量的修饰符(__strong,__weak,__unsafe_unretaimed)做出相应的操作,形成强引用(retain)或者弱引用
如果block从堆上移除
-会调用block内部的dispose函数
-dispose函数您内部会调用_Block_object_dispose函数
-_Block_object_dispose函数会自动释放引用的auto变量(release)

-----------未完待续------------------