Block的简介
Block 的官方定义是这样的:Block块是封装工作单元的对象,是可以在任何时间执行的代码段,其本质是可移植的匿名函数,可以作为方法和函数的参数传入,可以从方法和函数中返回。
在iOS4以后,越来越多的系统级的API在使用Block。苹果对于Block的使用主要集中在如下几个方面:
- 完成处理–Completion Handlers
- 通知处理–Notification Handlers
- 错误处理–Error Handlers
- 枚举–Enumeration
- 动画与形变–View Animation and Transitions
- 分类–Sorting
- 线程管理:GCD/NSOperation
第二部分:操作Block外部的变量
Q:访问Block之外的变量?
int age=10;
void (^Block)(void) = ^{
NSLog(@"age:%d",age);
};
Block();
age = 20;
Block();
输出值为 age:10
输出值为 age:10
原因:创建block的时候,已经把age的值存储在里面了。
注意此时觉得不能对block里面age的值修改,会报错
Q:下列代码输出值分别为多少?
auto int age = 10;
static int num = 25;
void (^Block)(void) = ^{
NSLog(@"age:%d,num:%d",age,num);
};
age = 20;
num = 11;
Block();
输出结果为:age:10,num:11
愿意:auto变量block访问方式是值传递,static变量block访问方式是指针传递
源码证明
int age = __cself->age; // bound by copy
int *num = __cself->num; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, (*num));
int age = 10;
static int num = 25;
block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, age, &num));
age = 20;
num = 11;
上述代码可查看 static修饰的变量,是根据指针访问的
Q:为什么block对auto和static变量捕获有差异?
auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;static变量一直保存在内存中,指针访问即可
Q:修改block之外的变量
在block中假如需要修改block之外定义的变量时,那么在定义变量时必须加上__block关键字这个比较常用
self.shadowView.alpha = 0.0f;
__block typeof(self) bself = self;
[UIView animateWithDuration:0.2f animations:^{
bself.shadowView.alpha = 1.0f;
} completion:^(BOOL finished){
}];
或者
改变 result的结果
__block BOOL result = NO;
[self.dataBaseQueue inDatabase:^(FMDatabase *db){
result = [db executeUpdate:sql];
}];
Q:block在修改NSMutableArray,需不需要添加__block?
不需要。
NSMutableArray *array = [NSMutableArray array];
void(^block)(void) = ^{
[array addObject:@123];
};
Block();
这里 对 array 只是一个使用,而不是赋值,所以不需要 _ _block 进行修饰
错误的例子
NSMutableArray *array = nil;
void(^block)(void) = ^{
array = [NSMutableArray array];
};
Block();
这里就需要在array的声明处添加__block修饰符,不然编译器会报错
总结下,对变量进行赋值的时候,下面这些不需要__block修饰符
Q:block能否修改变量值?
auto修饰变量,block无法修改,因为block使用的时候是内部创建了变量来保存外部的变量的值,block只有修改内部自己变量的权限,无法修改外部变量的权限。
static修饰变量,block可以修改,因为block把外部static修饰变量的指针存入,block直接修改指针指向变量值,即可修改外部变量值。
全局变量值,全局变量无论哪里都可以修改,当然block内部也可以修改。
Q:__block 修饰符作用?
- __block可以用于解决block内部无法修改auto变量值的问题
- __block不能修饰全局变量、静态变量(static)
- 编译器会将__block变量包装成一个对象
- __block修改变量:age->__forwarding->age
- __Block_byref_age_0结构体内部地址和外部变量age是同一地址

Q:block的属性修饰词为什么是copy?
block一旦没有进行copy操作,就不会在堆上
block在堆上,程序员就可以对block做内存管理等操作,可以控制block的生命周期
@property (copy, nonatomic) CMBCAddressUIBlock finishCompletion;
Block分类**
block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
__NSGlobalBlock __ ( _NSConcreteGlobalBlock )
__NSStackBlock __ ( _NSConcreteStackBlock )
__NSMallocBlock __ ( _NSConcreteMallocBlock )
各类型的block在内存中如何分配的?
- __NSGlobalBlock __ 在数据区
- __NSMallocBlock __ 在堆区(访问外部变量强引用在堆区)
- __NSStackBlock __ 在栈区(访问外部变量弱引用在栈区)
- 堆:动态分配内存,需要程序员自己申请,程序员自己管理
- 栈: 自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况

对每种类型block调用copy操作后是什么结果?
- __NSGlobalBlock __ 调用copy操作后,什么也不做
- __NSStackBlock __ 调用copy操作后,复制效果是:从栈复制到堆;副本存储位置是堆
- __NSStackBlock __ 调用copy操作后,复制效果是:引用计数增加;副本存储位置是堆
对象类型的auto变量**
Q:当block内部访问了对象类型的auto变量时,是否会强引用?
答案:分情况讨论,分为栈block和堆block
栈block
a) 如果block是在栈上,将不会对auto变量产生强引用
b) 栈上的block随时会被销毁,也没必要去强引用其他对象
堆block
1.如果block被拷贝到堆上:
a) 会调用block内部的copy函数
b) copy函数内部会调用_Block_object_assign函数
c) _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
2.如果block从堆上移除
a) 会调用block内部的dispose函数
b) dispose函数内部会调用_Block_object_dispose函数
c)

最低0.47元/天 解锁文章
104

被折叠的 条评论
为什么被折叠?



