Block内存布局详解中,有哪些细节需要关注?
摘要:1 内存布局 按照LLVM工程源码中的Block-ABI-Apple.rst描述,Block的内存布局如下: struct Block { void *isa; int flags; int reserved; R(*invoke)(Blo
1 内存布局
按照LLVM工程源码中的Block-ABI-Apple.rst描述,Block的内存布局如下:
struct Block {
void *isa;
int flags;
int reserved;
R(*invoke)(Block *, ...);
struct Block_Descriptor {
unsigned long int reserved;
unsigned long int size;
void(*copy_helper)(void *dst, void *src);
void(*dispose_helper)(void *src);
} *descriptor;
// 被捕获的变量
...
}
isa表明Block也是一个OC对象,它的取值后面会说明。
flags是各种标志位,它的取值后面会说明。
reserved是保留字段,不赋值。
invoke是函数指针,指向Block要执行的函数。
descriptor是一个结构体指针,里面包含了Block的各种描述信息。
descriptor.reserved是保留字段,不会进行赋值。
descriptor.size是整个Block结构体的大小。
descriptor.copy_helper与Block的拷贝相关,这个成员只有满足特定条件才会存在,后面会有介绍。
descriptor.dispose_helper与Block的释放相关,这个成员只有满足特定条件才会存在,后面会有介绍。
descriptor后面就是Block捕获的各种变量。
下面用一个例子来直观感受一下,假设有下面的Block定义:
int bi = 4;
void(^blk)(int, int, int) = ^(int i, int j, int k) {
int result = i + j + k + bi;
NSLog(@"%ld", result);
};
使用lldb查看内存布局如下:
(lldb) po $x0
<__NSStackBlock__: 0x16ba0f280>
signature: "v20@?0i8i12i16"
invoke : 0x1043eff4c (~/Library/Developer/CoreSimulator/Devices/ABFDFFF1-D158-48E1-9C91-0C8642E93E82/data/Containers/Bundle/Application/FC608AF0-55EA-493E-A1D4-851CFD67F9F0/iOSTest.app/iOSTest`__22-[BlockHandler handle]_block_invoke)
可以看到上面的Block是一个__NSStackBlock,地址是0x16ba0f280。
下面看下地址0x16ba0f280对应的内存值:
(lldb) x/8g 0x16ba0f280
0x16ba0f280: 0x00000001f2d7bb28 0x00000000c0000000
0x16ba0f290: 0x00000001043eff4c 0x00000001043fc220
0x16ba0f2a0: 0x0000000000000004
按照上面所述的内存布局:
0x00000001f2d7bb28就是isa指针。
0x00000000c0000000就是reserved+flags,高4字节是reserved,低4字节是flags。
0x00000001043eff4c就是invoke指针。
0x00000001043fc220就是descriptor指针。
0x0000000000000004就是捕获的变量bi,它的值是4。
