一、block
简介
1、block
的三种类型
- (void)my_block {
// <__NSGlobalBlock__: 0x106d6b038>
void (^block)(int) = ^(int value) {
};
NSLog(@"%@",block);
// <__NSMallocBlock__: 0x6000001cd620>
int a = 11;
void (^block2)(int) = ^(int value) {
NSLog(@"%d",a);
};
NSLog(@"%@",block2);
// <__NSStackBlock__: 0x7ffee0161078>
NSLog(@"%@",^(void) {
NSLog(@"%d",a);
});
void (^__weak block3)(void) = ^(){
NSLog(@"%d",a);
};
NSLog(@"%@",block3);// <__NSStackBlock__: 0x7ffee0161048>
}
-
NSGlobalBlock
- 全局 block -
NSMallocBlock
- 堆 block -
NSStackBlock
- 栈 block
2、block
的循环引用
2.1、引起循环引用的原因
typedef void(^MyBlock)(void);
@interface ViewController ()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) MyBlock block;
@end
//
- (void)viewDidLoad {
[super viewDidLoad];
[self my_blockCircular];
}
- (void)my_blockCircular {
// 造成循环引用 self 和 block 互相持有
// self --持有--> block --持有--> self
self.block = ^() {
NSLog(@"%@",self.name);
};
}
如上代码,因self
和block
的互相持有造成了循环引用。 --> __weak
解决。
2.2、循环引用的解决方案
2.1.1、方案 1 - __weak
代码修改如下:
__weak typeof(self)weakSelf = self;
/**
self -> block -> weakSelf -> self,持有关系中加了中介 weakSelf
weakSelf 在弱引用表中,不会对 self 的引用计数加一处理
*/
self.block = ^ {
NSLog(@"%@",weakSelf.name);
};
但__weak
是如何解决的呢?如何验证__weak
是弱引用,不会持有对象对引用计数加一?
1)__weak
的原理
2)__weak
解决循环引用时的一个问题
示例代码如下,若block
中执行耗时异步任务,在dealloc
后才执行,self
已销毁:
self.block = ^ {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.name);
});
};
self.block();
/** 输出
dealloc 走了
(null) // 这里self已经销毁了才执行了block内的任务
*/
造成此问题原因:self
的生命周期没有得到合适的保障:
self
被wself
弱引用,引用计数不会变,所以生命周期和不被block
捕获时一样。
修改代码修改如下:
self.block = ^ {
__strong typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.block();
/** 输出
张三
dealloc 走了
*/
__strong
为何可以解决,如上代码,strongSelf
对weakSelf
进行了强持有,其作用域是block
内部,当block
任务执行完毕后,strongSelf
变量被系统释放,weakSelf
和self
也就释放了。
2.1.2、方案 2 - 手动释放
代码如下:
// 解决方案 2 -- 手动释放
__block ViewController *vc = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil;// 不手动置为 nil 循环引用则仍在
});
};
上述代码,vc
变量 在比那辆被捕获到 block
内,增加了一个强持有,是不会释放的,而我们通过手动将self
置为nil
,从而释放触发 dealloc
。
2.1.3、方案 3 - 临时变量
已知循环引用的打破方式有2中,即self
和 block
的任意一方对另一方弱引用便可打破,但block
是不能使用weak
修饰的,weak
修饰的属性刚创建就会被释放掉。那么只要不持有即可解决,我们仍是通过中介者模式,可以通过代理、临时变量
等方式处理,示例代码如下:
typedef void(^MyBlock2)(ViewController *vc);
// 解决方案 3 -- 临时变量
self.block2 = ^(ViewController *vc) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
self.block2(self);
vc
作为函数形参 作为临时变量被压栈,block
不会对self
持有.
2.1.4、方案 4 - proxy
二、block
本质
1、block
编译后结构分析
main.m
中添加代码,文件clang
编译后如下:
void (^block)(void) = ^{
printf("my_block_test");
};
block();
clang
编译:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 结构体构造函数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;// 函数式 编程
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("my_block_test");
}
static struct __my_func_block_desc_0 {
size_t reserved;
size_t Block_size;
} __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0)};
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
// 对上面2行代码简化,去掉类型强转:
void (*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA); // __main_block_impl_0 函数
block->FuncPtr(block);// block中 FuncPtr() 函数 的调用
/** block->FuncPtr(block)
这里FuncPtr(),也验证了为何block必须要调用 --> 函数的调用。
block作为参数又传给FuncPtr 即 函数__main_block_func_0:
类似objc_msgSenf(id self, SEL _cmd)隐藏参数,函数__main_block_func_0()可使用block结构体内的任何成员.
*/
block
本质是结构体,内部有其构造函数从而进行函数调用 --> block的代码块回调.
2、block
的源码
找到 block
的源码
打开debug
汇编,测试代码如下,block声明处打断点:
- (void)my_block_copy {
// block 最初编译读取的是个栈 block
// 运行时经过 _Block_copy --> 成为堆 block
// stack_block --> _Block_copy --> malloc_block
int a = 10;
void (^block)(void) = ^{
NSLog(@"%d",a);
};
block();
}
运行工程:
添加符号断点objc_retainBlock
,继续执行:
block
源码位置在libsystem_blocks.dylib
, libclosure-74 源码.
2.1、Block_layout
--> block 在底层真正的结构:struct Block_layout {}
struct Block_layout {
void *isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
BlockInvokeFunction invoke;
struct Block_descriptor_1 *descriptor; // 描述符号
// imported variables
};
2.1.1、flags值
- Block_layout->flags
用于描述块对象的标志 :
// Values for Block_layout->flags to describe block objects
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime 析构
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime 存储引用计数的值,是个可选参数
BLOCK_NEEDS_FREE = (1 << 24), // runtime 程序根据它来决定是否增减引用计数位的值
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler 是否复制处理
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler 是否标记为全局
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler 是否有签名
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
// 每个 block 都有
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
// 2、3 根据 flags 判断是否有
// 可选 并非每个block都有
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
// 可选 并非每个block都有
#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
搜索Block_descriptor_2
: -->Block_descriptor_2
和Block_descriptor_3
,2者存在与否的判断 代码如下:
/****************************************************************************
Accessors for block descriptor fields
*****************************************************************************/
#if 0
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
return aBlock->descriptor;
}
#endif
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
// 没copy 直接返回 null
if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
// 获取Block_descriptor_2 --> 通过Block_descriptor_1进行内存平移
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct Block_descriptor_1);
return (struct Block_descriptor_2 *)desc;
}
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
// 没签名 直接返回 null
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
// 获取Block_descriptor_2 --> 通过Block_descriptor_1进行内存平移
desc += sizeof(struct Block_descriptor_1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
// 有 2 则再加上2 进行内存平移
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
Block_descriptor_?
的获取:
- 获取
Block_descriptor_2
--> 通过Block_descriptor_1
进行内存平移得到; - 获取
Block_descriptor_2
--> 通过Block_descriptor_1
+Block_descriptor_2
(有2则加无则只1)进行内存平移得到.
2.1.2、Block_descriptor_2
和 Block_descriptor_3
的存在与否
1)有 Block_descriptor_3
1.1)全局block
的信息情况
void (^block)(void) = ^{
NSLog(@"hello");
};
block();
1.2)栈、堆 block
的信息情况
- (void)my_block_copy {
int a = 10;
void (^block)(void) = ^{
NSLog(@"%d",a);
};
block();
}
// BLOCK_HAS_SIGNATURE & flag 有值 —> 有 descriptor_3
运行时,栈block
通过_Block_copy
,copy出一份到堆,得到堆block
.详细过程基按文章下面copy
源码分析。
2)Block_descriptor_
的 1 & 2 & 3
均存在
- (void)my_block_copy {
__block int a = 10;
void (^block)(void) = ^{
a++;
NSLog(@"%d",a);
};
block();
}
3.2、关于 block
的签名
由上面对block
的结构分析,已知每个block
的签名是必然会有的。下面我们通过内存来读取签名:
(lldb) po 0x00006000039959e0
<__NSMallocBlock__: 0x6000039959e0>
signature: "v8@?0"
invoke : 0x100ce4f00 (/Users/domy/Library/Developer/CoreSimulator/Devices/329A1FCB-1ADA-46B4-AD1B-68A6ECA79F77/data/Containers/Bundle/Application/3337EFE6-31C2-4F3A-B963-26EC9C4DD7A8/Demo_Block_Copy.app/Demo_Block_Copy`__31-[ViewController my_block_copy]_block_invoke)
(lldb) x/4gx 0x00006000039959e0
0x6000039959e0: 0x00007fff89ea07a0 0x00000000c1000002
0x6000039959f0: 0x0000000100ce4f00 0x0000000100ce7020
(lldb) x/8gx 0x0000000100ce7020
0x100ce7020: 0x0000000000000000 0x0000000000000024
0x100ce7030: 0x0000000100ce624b 0x0000000100ce62e1
0x100ce7040: 0x00007fff87c51128 0x00000000000007c8
0x100ce7050: 0x0000000100ce6248 0x0000000000000002
(lldb) p/x 1 << 25
(int) $10 = 0x02000000
(lldb) p 0x02000000 & 0x00000000c1000002
(unsigned int) $11 = 0
(lldb) p/x 1 << 30
(int) $12 = 0x40000000
(lldb) p 0x40000000 & 0x00000000c1000002
(unsigned int) $13 = 1073741824
(lldb) po 0x0000000100ce624b // 没有2 得到3的签名 则 1平移 2*8=16
4308492875
(lldb) po (char *)0x0000000100ce624b
"v8@?0"
(lldb)
block
的签名:
(lldb) po [NSMethodSignature signatureWithObjCTypes:"v8@?0"]
<NSMethodSignature: 0x6000039deac0>
number of arguments = 1
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
3.3、block
的内存栈 --> 堆
变化 - _Block_copy
3.3.1、汇编分析copy
流程
重新运行工程,如下图位置,读取寄存器register read
:
当前block
还是栈类型,继续执行,进入到_Block_copy
,将断点打在下图位置(即: block完成copy
)处:
block
的 copy
流程:编译时是stack_block
--> 运行时经过_Block_copy
--> malloc_block
3.3.2、_Block_copy
源码分析
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
// 栈 -> 堆 拷贝过程
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock; // 全局 不需要copy 直接返回自己
}
else { // 栈
// Its a stack block. Make a copy.
// 开辟堆空间
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
if (!result) return NULL;
// 内存移动, 将 aBlock 整个 copy 到 result
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
// invoke调用
result->invoke = aBlock->invoke;
#endif
// reset refcount 重置引用计数
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
/**
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
// BLOCK_HAS_COPY_DISPOSE 是否标记 copy
struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
if (!desc) return;
(*desc->copy)(result, aBlock); // do fixup
}
*/
// Set isa last so memory analysis tools see a fully-initialized object.
// 最后设置 isa 为堆block MallocBlock
result->isa = _NSConcreteMallocBlock;
return result;
}
}
三、block
捕获外部变量 原理分析
1、block 捕获外部变量
初探
1.1、外部变量的调用
main.m
文件中代码如下:
static int A = 10;
int B = 20;
void my_func () {
int a = 123;
void (^block)(void) = ^{
A++;
B++;
printf("%d - %d - %d",a,A,B);
};
block();
}
clang
编译:
struct __my_func_block_impl_0 {
struct __block_impl impl;
struct __my_func_block_desc_0* Desc;
int a;// 编译时 自动生成创建了 a 变量
// a(_a) : a = _a --> _a即传来的a
__my_func_block_impl_0(void *fp, struct __my_func_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;// 编译时,栈block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __my_func_block_func_0(struct __my_func_block_impl_0 *__cself) {
// 赋值 int a --> 值copy --> 2个a变量值相同
int a = __cself->a; // bound by copy
// 这里也验证了,为何外部变量不可直接在block内部使用:
// 因为内部有声明新的值相同的变量 --> 若block内部直接用外部变量则会造成编译器的代码歧义,报错
A++;
B++;
printf("%d - %d - %d",a,A,B);
}
static struct __my_func_block_desc_0 {
size_t reserved;
size_t Block_size;
} __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0)};
void my_func () {
int a = 123;
// a 作为参数传给函数 __my_func_block_impl_0
void (*block)(void) = ((void (*)())&__my_func_block_impl_0((void *)__my_func_block_func_0, &__my_func_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
-
block
对全局or静态变量是不进行捕获操作的,它可直接对全局静态变量进行使用; -
block
捕获局部变量:编译时内部生成一个新的进行值copy的变量。
1.2、外部变量 修改+使用
__block
分析:
void my_func () {
__block int a = 123;
void (^block)(void) = ^{
a++;
printf("%d",a);
};
block();
}
clang
:
// a 封装成对象
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __my_func_block_impl_0 {
struct __block_impl impl;
struct __my_func_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__my_func_block_impl_0(void *fp, struct __my_func_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __my_func_block_func_0(struct __my_func_block_impl_0 *__cself) {
// 指针 copy
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
printf("%d",(a->__forwarding->a));
}
static void __my_func_block_copy_0(struct __my_func_block_impl_0*dst, struct __my_func_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __my_func_block_dispose_0(struct __my_func_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __my_func_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __my_func_block_impl_0*, struct __my_func_block_impl_0*);
void (*dispose)(struct __my_func_block_impl_0*);
} __my_func_block_desc_0_DATA = { 0, sizeof(struct __my_func_block_impl_0), __my_func_block_copy_0, __my_func_block_dispose_0};
void my_func () {
// a 封装成一个对象 --> __Block_byref_a_0 结构体
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
(void*)0,// isa
(__Block_byref_a_0 *)&a,// __forwarding
0,// flags
sizeof(__Block_byref_a_0),// size
123// a的值
};
// (__Block_byref_a_0 *)&a :传的 a 的地址
void (*block)(void) = ((void (*)())&__my_func_block_impl_0((void *)__my_func_block_func_0, &__my_func_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
编译时,__block
将变量a
封装成了一个对象__Block_byref_a_0
,函数执行时(block
内部修改操作),传入的是指针,so,修改操控的是block
外部传入的变量a
.
__block
做了什么:
- 将变量封装成相应结构体 -
__Block_byref_a_0 {}
; - 保存原始变量的指针和值;
- 传递指针地址给
block
.
2、block
捕获外界变量 原理分析
/*******************************************************
Entry points used by the compiler - the real API!
A Block can reference four different kinds of things that require help when the Block is copied to the heap.
1) C++ stack based objects
2) References to Objective-C objects
3) Other Blocks
4) __block variables
In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers. The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign. The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
The flags parameter of _Block_object_assign and _Block_object_dispose is set to
* BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
* BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
* BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.
When a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions. Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor. And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.
So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
__block id 128+3 (0x83)
__block (^Block) 128+7 (0x87)
__weak __block id 128+3+16 (0x93)
__weak __block (^Block) 128+7+16 (0x97)
********************************************************/
//
// When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
// to do the assignment.
// hold objects - 自动捕获到变量
// lgname
// __block 变量
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
// objc 指针地址 weakSelf (self)
// arc
_Block_retain_object(object);
// 持有
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
// block 被一个 block 捕获
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
/*******
// copy the onstack __block container to the heap
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__block ... x;
__weak __block ... x;
[^{ x; } copy];
********/
*dest = _Block_byref_copy(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
/*******
// copy the actual field held in the __block container
// Note this is MRC unretained __block only.
// ARC retained __block is handled by the copy helper directly.
__block id object;
__block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
/*******
// copy the actual field held in the __block container
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__weak __block id object;
__weak __block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
default:
break;
}
}
// When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
// to help dispose of the contents
void _Block_object_dispose(const void *object, const int flags) {
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
// get rid of the __block data structure held in a Block
_Block_byref_release(object);
break;
case BLOCK_FIELD_IS_BLOCK:
_Block_release(object);
break;
case BLOCK_FIELD_IS_OBJECT:
_Block_release_object(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
break;
default:
break;
}
}
block
可捕获:
- 基于c++堆栈的对象
- 引用 OC 对象
- 其他 block 块
- __block 变量