__block修饰符

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量、静态变量(static)
  • 编译器会将__block变量包装成一个对象

示例1:__block修饰auto常量时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
__block int age = 10;
^{
NSLog(@"age is %d", age);
}();

//C++源码分析
//block对象信息
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};

//block描述信息,大小,copy, dispose操作
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(void);
void (*dispose)(void);
};

//被包装成__Block_byref_age_0结构体
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};

//block结构体内容
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *p;

//被包装成一个函数
__Block_byref_age_0 *age; // by ref

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

示例1:__block修饰对象时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//ARC环境下,编译器会对block进行copy操作。
Person *person = [[Person alloc] init];
Block block = ^{
NSLog(@"%p", person);
};
block();

//C++源码解析:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};

struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(void);
void (*dispose)(void);
};

struct __Block_byref_weakPerson_0 {
void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4

//内存管理相关
void (*__Block_byref_id_object_copy)(void*, void*); // 8
void (*__Block_byref_id_object_dispose)(void*); // 8

Person *__weak weakPerson;
};

struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;

__Block_byref_weakPerson_0 *weakPerson; // by ref

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

__block的内存管理

  • 当block在栈上时,并不会对__block变量产生强引用
  • 当block被copy到堆时
    • 会调用block内部的copy函数
    • copy函数内部会调用_Block_object_assign函数
    • _Block_object_assign函数会对__block变量形成强引用(retain)
  • 当block从堆中移除时
    • 会调用block内部的dispose函数
    • dispose函数内部会调用_Block_object_dispose函数
    • _Block_object_dispose函数会自动释放引用的__block变量(release)

__block的__forwarding指针

  • 在栈上的话,__forwarding指向自己
  • 在堆上的话,__forwarding指向复制到堆上的的__block变量结构体的指针

对象类型的auto变量、__block变量

  • 当block在栈上时,对它们都不会产生强引用
  • 当block拷贝到堆上时,都会通过copy函数来处理它们
    • __block变量(假设变量名叫做a)
    • _Block_object_assign((void*)&dst->a, (void*)src->a, 8/BLOCK_FIELD_IS_BYREF/);
  • 对象类型的auto变量(假设变量名叫做p)
    • _Block_object_assign((void*)&dst->p, (void*)src->p, 3/BLOCK_FIELD_IS_OBJECT/);
  • 当block从堆上移除时,都会通过dispose函数来释放它们
    • __block变量(假设变量名叫做a)
    • _Block_object_dispose((void*)src->a, 8/BLOCK_FIELD_IS_BYREF/);
  • 对象类型的auto变量(假设变量名叫做p)
    • _Block_object_dispose((void*)src->p, 3/BLOCK_FIELD_IS_OBJECT/);

被__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)

循环引用问题

解决循环引用问题 - ARC

用__weak、__unsafe_unretained解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//用__weak解决
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
};

//用__unsafe_unretained解决
__unsafe_unretained id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
};

注:
// __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
// __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变

用__block解决(必须要调用block)

1
2
3
4
5
6
__block id weakSelf = self;
self.block = ^{
NSLog(@"%p", weakSelf);
weakSelf = nil;
};
self.block()

参考