源码分析
需要搭建一个可以运行block底层代码的工程,参考objc - 编译Runtime源码objc4-680。
其中需要将main.m转为C++查看分析,命令如下:
main.m转化为C++的命令: xcrun -sdk macosx10.13 clang -S -rewrite-objc -fobjc-arc main.m
结论
1、block有普通变量:block_copy:把block从栈放到堆
2、block里有__block变量:block_copy,把block从栈放到堆,而且把__block的结构体从栈放到堆,同时forwading的指向改变
当block里有外部变量的时候
当 block 调用 copy 方法时,如果 block 在栈上,会被拷贝到堆上;
当 block 作为函数返回值返回时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;
当 block 被赋值给 __strong id 类型的对象或 (非weak)成员变量时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;
当 block 作为参数被传入方法名带有 usingBlock 的 Cocoa Framework 方法或 GCD 的 API 时。这些方法会在内部对传递进来的 block 调用 copy 或 _Block_copy 进行拷贝;
isa指针(NSMalloc、NSStack),OC里所有对象都有该指针,用于实现对象相关的功能。
flags,用于按bit位表示一些block的附加信息,本文后面介绍block copy的实现代码可以看到对该变量的使用。
reserved,保留变量。
invoke,函数指针,指向具体的block实现的函数调用地址。
descriptor, 表示该block的附加描述信息,主要是size大小,以及copy和dispose函数的指针。
variables,capture过来的变量,block能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。
案例分析
分析了当成员变量,和普通变量情况下,block内部的结构和是否还有值的情况。代码如下:
#import "ViewController.h"
typedef void(^cycleBlock)(void);
@interface ViewController () {
cycleBlock blk;
NSObject *object;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self weakCycleBlockFunction];
}
- (void)weakCycleBlockFunction {
NSObject *otherObject = [[NSObject alloc] init];
// otherObject :指针指向的地址,&otherObject :指针本身的地址
NSLog(@"object %p &object %p", otherObject, &otherObject);
/* 脑海里猜想block的结构 block对otherObject有强引用
struct blk {
__strong otherObject;
func()
}
func() {
NSLog(@"object %p ", blk->otherObject);
}
*/
blk = ^{
NSLog(@"object %p &object %p", otherObject, &otherObject);
};
blk();
otherObject = nil;
blk();
// 两次执行都有值
}
- (void)weakCycleBlockFunctionTwo {
NSObject *otherObject = [[NSObject alloc] init];
NSLog(@"object %p &object %p", otherObject, &otherObject);
/*
struct blk {
__weak otherObject;
func()
}
func() {
strong tmpObject = blk -> otherObject; // 最后一次block执行时,这时otherObject已经为nil了, 所以再强引用也没有用
NSLog(@"object %p", blk->otherObject);
}
*/
__weak typeof(otherObject)weakObject = otherObject;
blk = ^{
__strong typeof(weakObject)strongObject = weakObject;
NSLog(@"object %p &object %p", strongObject, &strongObject);
};
blk();
otherObject = nil;
blk();
// 最后一次的block执行没有值
}
- (void)weakCycleBlockFunctionThree {
NSObject *otherObject = [[NSObject alloc] init];
NSLog(@"object %p &object %p", otherObject, &otherObject);
/*
struct blk {
__weak otherObject;
func()
}
func() {
strong tmpObject = blk -> otherObject;
NSLog(@"object %p", blk->otherObject);
}
*/
__weak typeof(otherObject)weakObject = otherObject;
blk = ^{
__strong typeof(weakObject)strongObject = weakObject;
if (strongObject) {
NSLog(@"object %p &object %p", strongObject, &strongObject);
sleep(3);
NSLog(@"object %p &object %p", strongObject, &strongObject);
}
};
blk();
// 因为block的方法里面sleep了3秒,所以当otherObject = nil的时候,上面那行block还没有执行完(这时block对otherObject还有强引用)
otherObject = nil;
sleep(5);
// 前面方法执行完了,这时otherObject为nil,所以后面的block中打印不会执行
blk();
// 最后一个block不执行
}
- (void)strongCycleBlockFunction {
object = [[NSObject alloc] init];
NSLog(@"object %p &object %p", object, &object);
/*
struct blk {
__strong self;
func();
}
func() {
strong tmpObject = blk->self->otherObject;
NSLog(@"object %p ", blk->self->otherObject);
}
*/
//对于成员变量,block是把self引用计数+1,不是对成员变量object本身来增加引用计数的
//所以当otherObject为nil的时候,就是为nil了
blk = ^{
NSLog(@"object %p &object %p", object, &object);
};
blk();
object = nil;
blk();
// 最后一个block打印中object没有值
}
- (void)dealloc {
NSLog(@"dealloc");
}
@end