目标:
- 熟悉Block底层原理
- 熟悉内存拷贝相关的知识
如何深入学习相关概念呢? 将OC代码转化成C++代码,查看其内部实现。 面试过程中,每道题都是有对应分数的,一定要尽可能答的漂亮。
什么是Block
常见面试题:
- 什么是Block?
- 你对Block的调用是怎么理解的?
- 截获变量是Block的一大特性,系统对于Block的截获是怎么实现的呢?
- 我们添加__block修饰符做什么事情呢?
- 什么时候需要对Block 进行一个copy操作,栈Block和堆Block是否了解呢? 6.Block的循环引用
1. 什么是Block?
Block是一个对象,封装了执行函数和上下文。
源码解析
+ (void)testBlock1{
int multiplier = 6;
int (^Block)(int) = ^int(int num) {
return num *multiplier;
};
Block(2);
}
使用:
clang -rewrite-objc file.m
查看编译后的文件内容
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __TestObject__testBlock1_block_impl_0 {
struct __block_impl impl;
struct __TestObject__testBlock1_block_desc_0* Desc;
int multiplier;
__TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __TestObject__testBlock1_block_func_0(struct __TestObject__testBlock1_block_impl_0 *__cself, int num) {
int multiplier = __cself->multiplier; // bound by copy
return num *multiplier;
}
static struct __TestObject__testBlock1_block_desc_0 {
size_t reserved;
size_t Block_size;
} __TestObject__testBlock1_block_desc_0_DATA = { 0, sizeof(struct __TestObject__testBlock1_block_impl_0)};
// _C 表示类方法,_I 实例方法 ,TstObject 表示类名,testBlock1表示函数名
static void _C_TestObject_testBlock1(Class self, SEL _cmd) {
int multiplier = 6;
int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0, &__TestObject__testBlock1_block_desc_0_DATA, multiplier));
((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);
}
Block调用即是函数的调用。 从Block中取出函数指针传递两个参数Block和需要的参数。
截获变量
- 对于基本数据类型的局部变量截获其值。
- 对于对象类型的局部变量连同所有权修饰符一起截获(强引用)。
- 以指针形式截获局部静态变量。
- 不截获全局变量、静态全局变量。
使用
- (void)method {
// 基本类型的局部变量
int var =1;
// 对象类型的局部变量
__unsafe_unretained id unsafe_obj = nil;
__strong id strong_obj = nil;
//静态局部变量
static int static_var = 3;
void(^Block)(void) = ^{
NSLog(@"局部变量<基本数据类型> var %d",var);
NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@",unsafe_obj);
NSLog(@"局部变量<__strong 对象类型> var %@",strong_obj);
NSLog(@"静态变量 %d",static_var);
NSLog(@"全局变量<基本数据类型> var %d",global_var);
NSLog(@"静态全局变量<基本数据类型> var %d",static_global_var);
// 使用对象类型的 会防止循环引用 从而进行 提示
// NSLog(@"TestObject 对象类型成员 num %d",self.num);
};
Block();
}
clang -rewrite-objc -fobjc-arc file.m
struct __TestObject__method_block_impl_0 {
struct __block_impl impl;
struct __TestObject__method_block_desc_0* Desc;
int var;
__unsafe_unretained id unsafe_obj;
__strong id strong_obj;
int *static_var;
__TestObject__method_block_impl_0(void *fp, struct __TestObject__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __TestObject__method_block_func_0(struct __TestObject__method_block_impl_0 *__cself) {
int var = __cself->var; // bound by copy
__unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
__strong id strong_obj = __cself->strong_obj; // bound by copy
int *static_var = __cself->static_var; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_0,var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_1,unsafe_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_2,strong_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_3,(*static_var));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_4,global_var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_5,static_global_var);
}
__block修饰符
使用场景: 一般情况下,对被截获变量进行赋值操作需要添加__bloc修饰符。
__block修饰的变量变成了对象。
+ (void)testBlock1{
__block int multiplier = 6;
int (^Block)(int) = ^int(int num) {
return num *multiplier;
};
Block(2);
}
// 编译成C++文件后
struct __Block_byref_multiplier_0 {
void *__isa;
__Block_byref_multiplier_0 *__forwarding;
int __flags;
int __size;
int multiplier;
};
struct __TestObject__testBlock1_block_impl_0 {
struct __block_impl impl;
struct __TestObject__testBlock1_block_desc_0* Desc;
__Block_byref_multiplier_0 *multiplier; // by ref
__TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, __Block_byref_multiplier_0 *_multiplier, int flags=0) : multiplier(_multiplier->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void _C_TestObject_testBlock1(Class self, SEL _cmd) {
__attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = {(void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6};
int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0, &__TestObject__testBlock1_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344));
((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);
}
使用 不等于 赋值
笔试题
// 问题:此处是否需要对array 使用__block进行修饰呢
NSMutableArray * array = [[NSMutableArray alloc] init];
void (^Block)(void) = ^{
[array addObject:@123];
};
Block();
NSLog(@"array = %@",array);
此处不需要,仅仅为使用。 下面这个呢: 局部变量不管对象类型是基本数据类型还是对象类型,进行赋值操作都需要添加__block修饰符。
对变量进行赋值时, 需要__block修饰符
Block内存管理
Block的Copy操作
不论在任何内存位置,都可以顺利的访问同一个__block变量。
循环引用
_array = [NSMutableArray arrayWithObject:@"block"];
_strBlk = ^NSString *(NSString *num) {
return [NSString stringWithFormat:@"helloc_%@",_array[0]];
};
_strBlk(@"hello");
// 解决方式
_array = [NSMutableArray arrayWithObject:@"block"];
__weak NSArray * weakArray = _array;
_strBlk = ^NSString *(NSString *num) {
return [NSString stringWithFormat:@"helloc_%@",weakArray[0]];
};
_strBlk(@"hello");
在MRC下,不会产生循环引用。 在ARC下,会产生循环引用,引起内存泄漏。
此时如果不调用或者是长时间不调用的话,环就会一直存在。