参考:
Apple Block 源码
Objective-C高级编程 iOS与OS X多线程和内存管理
Block与函数指针调用
//函数指针调用
static int count = 0;
int (*funcPtr)(int) = &func;
NSLog(@"C原因函数指针调用: %d", funcPtr(count));
//Block方式调用
int(^block)(int count) = ^(int count) {
return ++ count;
};
NSLog(@"Block方式调用: %d", block(count));
Block的类型和继承链
//Block的类型和继承链
//以下代码在MRC环境下
//定义在全局区
void(^globalBlock1)(void) = ^{
};
void test1() {
//未截获自动变量
void(^globalBlock2)(void) = ^{
};
//截获自动变量(MRC下验证,ARC会执行copy)
int a = 0;
void(^stackBlock)(void) = ^{
NSLog(@"a: %d", a);
};
//截获自动变量 调用copy
void(^mallocBlock)(void) = [^{
NSLog(@"a: %d", a);
} copy];
NSLog(@"globalBlock1: %@", globalBlock1);
NSLog(@"globalBlock2: %@", globalBlock2);
NSLog(@"stackBlock: %@", stackBlock);
NSLog(@"mallocBlock: %@", mallocBlock);
//继承链
NSLog(@"------------------------------");
NSLog(@"globalBlock1 superclass: %@", [globalBlock1 superclass]);
NSLog(@"stackBlock superclass: %@", [[stackBlock class] superclass]);
NSLog(@"mallocBlock superclass: %@", [[mallocBlock class] superclass]);
NSLog(@"------------------------------");
NSLog(@"globalBlock1 superclass superclass: %@", [[globalBlock1 superclass] superclass]);
NSLog(@"stackBlock superclass superclass: %@", [[[stackBlock class] superclass] superclass]);
NSLog(@"mallocBlock superclass superclass: %@", [[[mallocBlock class] superclass] superclass]);
NSLog(@"------------------------------");
NSLog(@"globalBlock1 superclass superclass superclass: %@", [[[globalBlock1 superclass] superclass] superclass]);
}
输出结果为:
Block的类型和继承链
Block截获变量
//Block截获变量
static int static_global_val = 0;//静态全局变量
int global_val = 0;//全局变量
void test2() {
// int val = 0;//不能在block内部修改的变量
static int static_val = 0;//静态变量
__block int block_val = 0;//__block修饰的变量
void(^block)(void) = ^{
static_global_val ++;
global_val ++;
static_val ++;
block_val ++;
NSLog(@"static_global_val: %d", static_global_val);
NSLog(@"global_val: %d", global_val);
NSLog(@"static_val: %d", static_val);
NSLog(@"block_val: %d", block_val);
};
block();
}
输入clang编译命令:clang -rewrite-objc main.m -o main.cpp
在main.cpp中关键代码如下:
static int static_global_val = 0;
int global_val = 0;
struct __Block_byref_block_val_0 {
void *__isa;
__Block_byref_block_val_0 *__forwarding;
int __flags;
int __size;
int block_val;
};
struct __test2_block_impl_0 {
struct __block_impl impl;
struct __test2_block_desc_0* Desc;
int *static_val;
__Block_byref_block_val_0 *block_val; // by ref
__test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, int *_static_val, __Block_byref_block_val_0 *_block_val, int flags=0) : static_val(_static_val), block_val(_block_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test2_block_func_0(struct __test2_block_impl_0 *__cself) {
__Block_byref_block_val_0 *block_val = __cself->block_val; // bound by ref
int *static_val = __cself->static_val; // bound by copy
static_global_val ++;
global_val ++;
(*static_val) ++;
(block_val->__forwarding->block_val) ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_16, static_global_val);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_17, global_val);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_18, (*static_val));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gg_dlj973yn6wg2tqfsg64kdyxr0000gn_T_main_b5be8e_mi_19, (block_val->__forwarding->block_val));
}
static void __test2_block_copy_0(struct __test2_block_impl_0*dst, struct __test2_block_impl_0*src) {_Block_object_assign((void*)&dst->block_val, (void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __test2_block_dispose_0(struct __test2_block_impl_0*src) {_Block_object_dispose((void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __test2_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test2_block_impl_0*, struct __test2_block_impl_0*);
void (*dispose)(struct __test2_block_impl_0*);
} __test2_block_desc_0_DATA = { 0, sizeof(struct __test2_block_impl_0), __test2_block_copy_0, __test2_block_dispose_0};
void test2() {
static int static_val = 0;
__attribute__((__blocks__(byref))) __Block_byref_block_val_0 block_val = {(void*)0,(__Block_byref_block_val_0 *)&block_val, 0, sizeof(__Block_byref_block_val_0), 0};
void(*block)(void) = ((void (*)())&__test2_block_impl_0((void *)__test2_block_func_0, &__test2_block_desc_0_DATA, &static_val, (__Block_byref_block_val_0 *)&block_val, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
test2();
}
return 0;
}
- static int static_global_val = 0;//静态全局变量
int global_val = 0;//全局变量
可以直接访问并修改 - static int static_val = 0;//静态变量
通过指针访问(*static_val) ++; - __block int block_val = 0;//__block修饰的变量
通过(block_val->__forwarding->block_val) ++;
__block说明符
Block生成了如下结构体:
struct __test2_block_impl_0 {
struct __block_impl impl;
struct __test2_block_desc_0* Desc;
int *static_val;
__Block_byref_block_val_0 *block_val; // by ref
__test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, int *_static_val, __Block_byref_block_val_0 *_block_val, int flags=0) : static_val(_static_val), block_val(_block_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__block修饰的变量编译后生成了如下结构体:
struct __Block_byref_block_val_0 {
void *__isa;
__Block_byref_block_val_0 *__forwarding;
int __flags;
int __size;
int block_val;
};
Block内部执行的操作为:
- __Block_byref_block_val_0 *block_val = __cself->block_val;
- (block_val->__forwarding->block_val) ++;
总结:
在给__block修饰的成员变量赋值时候,通过__Block_byref_block_val_0的指针__forwarding访问成员变量block_val;
Block存储域不同的区别
配置在全局变量上的Block,从变量作用域外也可以通过指针安全使用。但设置在栈上的Block,如果其所属的变量作用域结束,该Block就被废弃。由于__block变量也配置在栈上,同样地,如果其所属的变量作用域结束,则该__block变量也会被废弃。
Blocks提供了将Block和__blok变量从栈上复制到堆上的方法来解决这个问题。将配置在栈上的Block复制到堆上,这样即使Block语法记述的变量作用域结束,堆上的Block还可以继续存在。
复制到堆上的Block将_NSConcreteMallocBlock类对象写入Block用结构体实例的成员变量isa。
impl.isa = &_NSConcreteMallocBlock
而__block变量用结构体变量成员__forwarding可以实现无论__block变量配置在栈上还是堆上时都能正确地访问__block变量。
复制__block变量
验证复制__block变量
//验证复制__block变量
void test3() {
__block int a = 1;
NSLog(@"a=%d 地址:%p", a, &a);
void(^block)(void) = ^{
a ++;
NSLog(@"a=%d 地址:%p", a, &a);
};
// [block copy];
block();
NSLog(@"a=%d 地址:%p", a, &a);
}
输出结果:
不执行copy,在栈区
void test3() {
__block int a = 1;
NSLog(@"a=%d 地址:%p", a, &a);
void(^block)(void) = ^{
a ++;
NSLog(@"a=%d 地址:%p", a, &a);
};
[block copy];
block();
NSLog(@"a=%d 地址:%p", a, &a);
}
输出结果:
执行copy,copy后在堆区
栈上的Block复制到堆上在什么时候:
- 调用Block的copy实例方法时
- Block作为函数返回值返回时
- 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
- 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时
Block的引用和循环引用
被__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)
在一个Block中使用__block变量
在多个Block中使用__block变量
Block的废弃和__block变量的释放
复制__block变量
解决循环引用
- 通过__block修饰符
__block TwoViewController *weakSelf = self;
self.block = ^{
weakSelf.title = @"__block";
weakSelf = nil;
};
self.block();//不调用引起循环引用
- 通过__unsafe_unretained修饰符
__unsafe_unretained TwoViewController *weakSelf = self;
self.block = ^{
weakSelf.title = @"__unsafe_unretained";
};
self.block();
- 通过__weak修饰符(自动检查置nil安全方式)
__weak TwoViewController *weakSelf = self;
self.block = ^{
weakSelf.title = @"__weak";
};
self.block();
网友评论