问题缘由:
1、下面这段代码有问题么?如果没有,为什么?
- (void)blockDemo6
{
__weak ViewController *weakSelf = self;
void(^task)() = ^{
[weakSelf callFunction];
};
task();
}
- (void)callFunction{
[self callFunction2];
}
- (void)callFunction2{
}
答案:没有
原因:block本质上也是一个对象,对象对其外部变量 是拷贝和引用在block内部已经处理好,block内部调用外部函数,相当于直接调用 self 对象 调用其外部函数指针,这个时候,如果self 释放,函数将不执行
2、block套用block有问题么
- (void)blockDemo6
{
__weak ViewController *weakSelf = self;
void(^task)(void) = ^{
void(^task2)(void) = ^{
[weakSelf callFunction];
};
[weakSelf callFunction];
task2();
};
task();
}
- (void)callFunction{
}
答案:没问题 内部调用最外层的WeakSelf,不要造成内存问题,没有任何问题
block小结
block组成结构
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
Block_layout 为block组成结构
如图了解到:
void isa; block 对象也有指向对象本身的 isa指针
void (invoke)(void *, ...) block对象需要调用的函数指针
struct Block_descriptor *descriptor; block对象对外部变量的引用和拷贝
block 分类
NSConcreteGlobalBlock 不捕获外部变量的block
NSConcreteMallocBlock 执行过copy操作的block
NSConcreteStackBlock MRC 捕获外部变量没有执行过copy操作的block
code分析
void exampleA() {
char a = 'A';
^{
printf("%cn", a);
}();
}
这段代码在 MRC 和 ARC 的情况下都能正确执行。
因为 exampleA 的函数栈,在 block 执行完之前,并不会 pop,所以,无论函数中的 block 是在 stack 或是 heap 中,都能够被正确执行
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
这段代码只有在 ARC 的情况下才能正确执行。
在 MRC 的情况下,block 分配在栈上,在 exampleB_addBlockToArray 返回之后,函数栈被弹出,这个 block 的地址就不再合法了。
在 ARC 的情况下,block 将会被拷贝到堆中,可以合法使用。
外部变量值拷贝与值引用
code 分析:
//StrM 浅拷贝 地址拷贝 指向对象内容一样
- (void)blockDemo4
{
NSMutableArray *strM= [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];
NSLog(@"strM指针在栈区的地址%p strM内容在堆区的地址%p",&strM,strM);
void (^block)() = ^ {
// 只修改strM指向堆区的内容(字符串);没有修改strM指针所在的地址
//(存放字符串的堆区地址没变,只是&strM变成堆区地址);
[strM addObject:@"4"];
NSLog(@"strM指针拷贝到堆区的新地址%p strM内容在堆区的地址%p",&strM,strM);
};
block();
NSLog(@"%@",block);
}
//值拷贝
- (void)blockDemo11
{
int num = 10;
void(^task)() = ^{
// 栈区地址比堆区地址大
NSLog(@"堆区地址 ===> %p-%d",&num,num);
};
num = 20;
task();
}
//值引用
/* 指针地址一样
2019-01-17 12:13:59.728070+0800 TestIF[48445:7410873] 栈区地址 ===> 0x7ffee3a5e9d8-20
2019-01-17 12:13:59.728202+0800 TestIF[48445:7410873] 堆区地址 ===> 0x7ffee3a5e9d8-20
*/
- (void)blockDemo12
{
__block int num = 10;
void(^task)() = ^{
// 栈区地址比堆区地址大
NSLog(@"堆区地址 ===> %p-%d",&num,num);
};
num = 20;
NSLog(@"栈区地址 ===> %p-%d",&num,num);
task();
}
参考地址:
原理参考:https://blog.devtang.com/2013/07/28/a-look-inside-blocks/
Objective-C Blocks 小测验 http://www.futantan.com/2016/03/10/objective-c-blocks-quiz/
IOS工程中混合使用ARC与MRC https://www.jianshu.com/p/a528efd82f11
开发 - Block相关的面试题 https://www.jianshu.com/p/fd40a18369cf