title: block深度剖析
date: 2016-04-18 23:46:43
tags: block
分两部分内容来剖析block:
- 怎么用
- 为什么这么用
block的堆栈
分类:
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段; NSStackBlock:位于栈内存,函数返回后Block将无效; NSMallocBlock:位于堆内存。需要开发者进行释放。
区分:非ARC下,无引用外部变量的即为NSGlobalBlock,引用外部变量的为NSStackBlock,NSStakBlock做copy即为NSMallocBlock。
内存管理:
block是编译时生成的,而不是运行时生成的。所以在编译时如果用到外部变量就会把变量建一份快照放到栈上。
- NSGlobalBlock:生命周期从应用程序开始到程序终止。对其做retain/release/copy都没有任何意义,还是返回本身。
- NSStackBlock:函数返回后即消失。对其做retain/release都没有任何意义,还是返回本身。对其做copy则会把内容复制到堆内存(NSMallocBlock),生成新的内存块。
- NSMallocBlock:需要程序员自己释放,对其做retain和copy一样,只是所指对象计数器增加1,打印时一直会显示1,但实际计数器已经增加了。对其做release计数器减一,不过打印计数器时,一直还是显示1。
typedef int (^square)(int);
(void) viewDidLoad
{
[super viewDidLoad];
square tempBlock1 = ^(int a){ return a * a; };
NSLog(@"tempBlock1:%@, return1:%d", tempBlock1, tempBlock1(5));
// log: tempBlock1:<__NSGlobalBlock__: 0x10e6d9240>, return1:25
int i = 2;
square temptBlock2 = ^(int a){ int itRet = i * a; return itRet; };
NSLog(@"temptBlock2:%@, return2:%d", temptBlock2, temptBlock2(5));
// log: temptBlock2:<__NSStackBlock__: 0x7fff51528a50>, return2:10
square temptBlock3 = [temptBlock2 copy];
NSLog(@"temptBlock3:%@, return3:%d", temptBlock3, temptBlock3(5));
// log: temptBlock3:<__NSMallocBlock__: 0x7fbdc0608180>, return3:10
temptBlock = [temptBlock3 copy];
NSLog(@"temptBlock:%@, return:%d", temptBlock, temptBlock(5));
// log: temptBlock:<__NSMallocBlock__: 0x7fbdc0608180>, return3:10 此时temptBlock的计数器实际上是2
[temptBlock release];
}
(IBAction)Test:(id)sender {
NSLog(@"=====temptBlock:%@, return:%d, blockRec:%ld", temptBlock, temptBlock(5), [temptBlock retainCount]);
// 如果viewDidLoad中temptBlock再做次release就crash了 这里
}
修改变量值原理
typedef int (^square)(int);
int i = 6;
square tempBlock1 = ^(int a){return a * i; };
printf("=====%d", tempBlock1(5));
如果想在tempBlock1中更改i值是不被允许的,为什么?查看代码的底层实现:clang -rewrite-objc block2.c,去掉不必要的代码,保留主要代码:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void fp, struct __main_block_desc_0 desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
int i = __cself->i; // bound by copy
return a * i; }
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main()
{
int i = 6;
square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
printf("=====%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5));
return 0;
}
代码看上去有些多,看主要信息:
- __block_impl:isa指名是一个对象,指向所属类的指针也就是保存了block的类型(这里为栈对象),flags记录block的标识,reserved:保留字段为了扩展,funcptr:block执行的函数体,也就是block的实现。__main_block_impl_0:包含__block_impl对象和__main_block_desc_0(就是记录__main_block_impl_0的大小),以及外部变量的映像。个人理解其实__block_impl就是__main_block_impl_0的基类。
- 顺序:创建tempBlock1对象,执行block的函数体。
- 为什么不能在block中直接更改变量值,因为传的只是形参,如果改变的话只能操作tempBlock1中的i的改变,但是外部的变量i是不会被改变的,所以干脆禁止这么操作。那么怎么改才能和外部的联动?
{
__block int i = 6;
square tempBlock1 = ^(int a){ i = i + 1; return a * i; };
i = 7;
printf("=====%d,i=%d", tempBlock1(5), i);
return 0;
}
同样查看源码,去掉不必要的代码:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void fp, struct __main_block_desc_0 desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
(i->__forwarding->i) = (i->__forwarding->i) + 1; return a * (i->__forwarding->i); }
static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src) {_Block_object_assign((void)&dst->i, (void)src->i, 8/BLOCK_FIELD_IS_BYREF/);}
static void __main_block_dispose_0(struct __main_block_impl_0src) {_Block_object_dispose((void)src->i, 8/BLOCK_FIELD_IS_BYREF/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (copy)(struct __main_block_impl_0, struct __main_block_impl_0*);
void (dispose)(struct __main_block_impl_0);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main()
{
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 6};
square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
(i.__forwarding->i) = 7;
printf("=====%d,i=%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5), (i.__forwarding->i));
return 0;
}
只看不同的代码:
- 多了个__Block_byref_i_0:生成__Block_byref_i_0对象i存储外部变量的值,这样把__Block_byref_i_0对象i的指针传入block中,在栈函数funptr中直接改变__Block_byref_i_0对象i中int变量i的值,因为block外部和内部都用的是同一个对象指针,所以在block内外改变其中的值是联动的即同一份。
- 多了个__main_block_copy_0:如果block从栈中复制到堆中时,会调用此函数,里面的实现就是把__Block_byref_i_0对象i栈的i->forwarding指向__Block_byref_i_0堆中的地址。这样堆中操作了,栈中也会联动改变。也是forwarding的作用(代码实现在下面)。
- __main_block_dispose_0:在block释放时会调用此函数。
具体源码查看苹果blockRuntime里面的这些实现的源码,网址点击这里:
void _Block_object_assign(void destAddr, const void object, const int flags)
{
//printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER)
{
if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK)
{
_Block_assign_weak(object, destAddr);
}
else
{
// do *not* retain or *copy* __block variables whatever they are
_Block_assign((void *)object, destAddr);
}
}
else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)
{
// copying a __block reference from the stack Block to the heap
// flags will indicate if it holds a __weak reference and needs a special isa
_Block_byref_assign_copy(destAddr, object, flags);
}
// (this test must be before next one)
else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK)
{
// copying a Block declared variable from the stack Block to the heap
_Block_assign(_Block_copy_internal(object, flags), destAddr);
}
// (this test must be after previous one)
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT)
{
//printf("retaining object at %p\n", object);
_Block_retain_object(object);
//printf("done retaining object at %p\n", object);
_Block_assign((void *)object, destAddr);
}
}
static void _Block_byref_assign_copy(void dest, const void arg, const int flags)
{
struct Block_byref **destp = (struct Block_byref **)dest;
struct Block_byref *src = (struct Block_byref *)arg;
if (src->forwarding->flags & BLOCK_IS_GC)
{
; // don't need to do any more work
}
else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0)
{
//printf("making copy\n");
// src points to stack
bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
// if its weak ask for an object (only matters under GC)
struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
if (isWeak) {
copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning
}
if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
copy->byref_keep = src->byref_keep;
copy->byref_destroy = src->byref_destroy;
(*src->byref_keep)(copy, src);
}
else {
// just bits. Blast 'em using _Block_memmove in case they're __strong
_Block_memmove(
(void *)©->byref_keep,
(void *)&src->byref_keep,
src->size - sizeof(struct Block_byref_header));
}
}
// already copied to heap
else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
// assign byref data block pointer into new Block
_Block_assign(src->forwarding, (void **)destp);
}
循环引用
现象
对如下3个Person类分别做[[Person alloc] init]和[Person release],查看Person类中dealloc是否会调用。
类A PersonA:
typedef int (^square)(int);
import "Person.h"
@interface Person ()
{
square tempBlock;
}
@property (nonatomic, assign) int i;
@end
@implementation Person
(id) init
{
self = [super init];
if (self)
{
_i = 8;
tempBlock = ^(int a){
return a * _i;
};
NSLog(@"tempBlock:%@", tempBlock);
}
return self;
}
(void) funTest
{
NSLog(@"tmpBlock:%d", tempBlock(5));
}
(void) dealloc
{
[super dealloc];
NSLog(@"dealloc run");
}
@end
类B:PersonB:
typedef int (^square)(int);
import "Person1.h"
@interface Person1 ()
{
square tempBlock;
}
@property (nonatomic, assign) int i;
@end
@implementation Person1
(id) init
{
self = [super init];
if (self)
{
_i = 8;
square tempBlock1 = ^(int a){
return a * _i;
};
tempBlock = [tempBlock1 copy];
NSLog(@"tempBlock:%@, temptBlock1:%@", tempBlock, tempBlock1);
}
return self;
}
(void) funTest
{
NSLog(@"tmpBlock:%d", tempBlock(5));
}
(void) dealloc
{
[super dealloc];
NSLog(@"dealloc run");
}
@end
类C:PersonC:
typedef int (^square)(int);
import "Person2.h"
@interface Person2 ()
{
square tempBlock;
}
@property (nonatomic, assign) int i;
@end
@implementation Person2
(id) init
{
self = [super init];
if (self)
{
_i = 8;
__block Person2* weakSelf = self;
square tempBlock1 = ^(int a){
return a * weakSelf.i;
};
tempBlock = [tempBlock1 copy];
NSLog(@"tempBlock:%@, temptBlock1:%@", tempBlock, tempBlock1);
}
return self;
}
(void) funTest
{
NSLog(@"tmpBlock:%d", tempBlock(5));
}
(void) dealloc
{
[super dealloc];
NSLog(@"dealloc run");
}
@end
发现:
- A和C的dealloc会调用,而B的dealloc不会调用
- A中的block为栈的block,B和C的tempBlock为堆的block(从栈中copy了)
- 说明栈的block不会强引用self,而堆的block会强引用self。如果加上__block Person2* weakSelf = self;时,再在block中访问weakSelf的变量就不会强引用weakSelf。
为什么?
本质实现探究
查看源码实现,就知道栈上的blockcopy到堆上时,对person对象进行了retain,而当person对象加上__block时,block内部就不会对person进行retain了。具体如下:
对person1代码进行clang查看源码如下:
struct __block_impl
{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct Person1_IMPL
{
struct NSObject_IMPL NSObject_IVARS;
square tempBlock;
int _i;
};
struct __Person1__init_block_impl_0
{
struct __block_impl impl;
struct __Person1__init_block_desc_0* Desc;
Person1 *self;
__Person1__init_block_impl_0(void fp, struct __Person1__init_block_desc_0 desc, Person1 *_self, int flags=0) : self(_self)
{
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __Person1__init_block_func_0(struct __Person1__init_block_impl_0 *__cself, int a)
{
Person1 *self = __cself->self; // bound by copy
return a * (*(int *)((char *)self + OBJC_IVAR_$_Person1$_i));
}
static void __Person1__init_block_copy_0(struct __Person1__init_block_impl_0dst, struct __Person1__init_block_impl_0src)
{
_Block_object_assign((void*)&dst->self, (void*)src->self, 3/
*BLOCK_FIELD_IS_OBJECT*/);
}
static void __Person1__init_block_dispose_0(struct __Person1__init_block_impl_0*src)
{
_Block_object_dispose((void)src->self, 3/BLOCK_FIELD_IS_OBJECT*/);
}
static struct __Person1__init_block_desc_0
{
size_t reserved;
size_t Block_size;
void (*copy)(struct __Person1__init_block_impl_0*, struct __Person1__init_block_impl_0*);
void (*dispose)(struct __Person1__init_block_impl_0*);
} __Person1__init_block_desc_0_DATA = { 0, sizeof(struct __Person1__init_block_impl_0), __Person1__init_block_copy_0, __Person1__init_block_dispose_0};
static id _I_Person1_init(Person1 * self, SEL _cmd)
{
self = ((Person1 *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person1"))}, sel_registerName("init"));
if (self)
{
(*(int *)((char *)self + OBJC_IVAR_$_Person1$_i)) = 8;
square tempBlock1 = ((int (*)(int))&__Person1__init_block_impl_0((void *)__Person1__init_block_func_0, &__Person1__init_block_desc_0_DATA, self, 570425344));
(*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)) = (square)((id (*)(id, SEL))(void *)objc_msgSend)((id)tempBlock1, sel_registerName("copy"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person1_617ecb_mi_0, (*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)), tempBlock1);
}
return self;
}
static void _I_Person1_funTest(Person1 * self, SEL _cmd)
{
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person1_617ecb_mi_1, ((int (*)(__block_impl *, int))((__block_impl *)(*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)))->FuncPtr)((__block_impl *)(*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)), 5));
}
static void _I_Person1_dealloc(Person1 * self, SEL _cmd)
{
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person1"))}, sel_registerName("dealloc"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person1_617ecb_mi_2);
}
static int _I_Person1_i(Person1 self, SEL _cmd) { return ((int )((char )self + OBJC_IVAR_$_Person1$_i)); }
static void _I_Person1_setI_(Person1 self, SEL _cmd, int i) { ((int )((char )self + OBJC_IVAR_$_Person1$_i)) = i; }
// @end
结合runtime.h查看上面代码的执行过程:
- 先执行_I_Person1_init这个函数,执行block的copy。查看runtime.h中block_copy会执行_Block_copy_internal
-
执行_Block_copy_internal(const void arg, const int flags)中
aBlock;struct Block_layout
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;// Its a stack block. Make a copy.
if (!isGC) {struct Block_layout *result = malloc(aBlock->descriptor->size); if (!result) return (void *)0; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed result->flags |= BLOCK_NEEDS_FREE | 1; result->isa = _NSConcreteMallocBlock; if (result->flags & BLOCK_HAS_COPY_DISPOSE) { //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock); (*aBlock->descriptor->copy)(result, aBlock); // do fixup } return result;
}
- 执行上面对栈上的block进行copy,在堆上生成新的内存块,flag为BLOCK_NEEDS_FREE和~(BLOCK_REFCOUNT_MASK)记得这个后面会用。isa声明为堆的block。然后执行(*aBlock->descriptor->copy)(result, aBlock)即上面的__Person1__init_block_copy_0函数
- 执行_Block_object_assign((void)&dst->self, (void)src->self, 3/
BLOCK_FIELD_IS_OBJECT/);查看runtime.h查看其实现。 -
执行的代码如下:
void _Block_object_assign(void destAddr, const void object, const int flags)
{
// (this test must be after previous one)
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {//printf("retaining object at %p\n", object); _Block_retain_object(object); //printf("done retaining object at %p\n", object); _Block_assign((void *)object, destAddr);
}
} - 对object即src->self即block持有的person进行了retain,而_block_assign把栈上持有的person对象的内存地址赋值到堆上面person的对象的地址。这样指向一个person对象就可以直接操作person对象的变量和值。
同样对person2进行源码编译,查看runtime.c,也详细分析下步骤 编译后的源码:
struct __block_impl
{
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static void __Block_byref_id_object_copy_131(void dst, void src){
// 4个指针加上2个int 48+24=40即__Block_byref_weakSelf_0中的person对象 131即为BLOCK_FIELD_IS_OBJECT|BLOCK_BYREF_CALLE
_Block_object_assign((char)dst + 40, (void ) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose((void ) ((char)src + 40), 131);
}
static NSConstantStringImpl __NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_0 __attribute ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"tempBlock:%@, temptBlock1:%@",28};
static NSConstantStringImpl __NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_1 __attribute ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"tmpBlock:%d",11};
static NSConstantStringImpl __NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_2 __attribute ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"dealloc run",11};
struct __Block_byref_weakSelf_0 {
void *__isa;
__Block_byref_weakSelf_0 *__forwarding;
int __flags;
int __size;
void (__Block_byref_id_object_copy)(void, void*);
void (__Block_byref_id_object_dispose)(void);
Person2 *weakSelf;
};
struct __Person2__init_block_impl_0 {
struct __block_impl impl;
struct __Person2__init_block_desc_0* Desc;
__Block_byref_weakSelf_0 *weakSelf; // by ref
__Person2__init_block_impl_0(void fp, struct __Person2__init_block_desc_0 desc, __Block_byref_weakSelf_0 *_weakSelf, int flags=0) : weakSelf(_weakSelf->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static int __Person2__init_block_func_0(struct __Person2__init_block_impl_0 *__cself, int a)
{
__Block_byref_weakSelf_0 *weakSelf = __cself->weakSelf; // bound by ref
return a * ((int (*)(id, SEL))(void *)objc_msgSend)((id)(weakSelf->__forwarding->weakSelf), sel_registerName("i"));
}
static void __Person2__init_block_copy_0(struct __Person2__init_block_impl_0dst, struct __Person2__init_block_impl_0src)
{
_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __Person2__init_block_dispose_0(struct __Person2__init_block_impl_0*src)
{
_Block_object_dispose((void*)src->weakSelf, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __Person2__init_block_desc_0
{
size_t reserved;
size_t Block_size;
void (copy)(struct __Person2__init_block_impl_0, struct __Person2__init_block_impl_0*);
void (dispose)(struct __Person2__init_block_impl_0);
} __Person2__init_block_desc_0_DATA = { 0, sizeof(struct __Person2__init_block_impl_0), __Person2__init_block_copy_0, __Person2__init_block_dispose_0};
static id _I_Person2_init(Person2 * self, SEL _cmd) {
self = ((Person2 *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person2"))}, sel_registerName("init"));
if (self)
{
//33554432即为BLOCK_HAS_COPY_DISPOSE
(*(int *)((char *)self + OBJC_IVAR_$_Person2$_i)) = 8;
__attribute__((__blocks__(byref))) __Block_byref_weakSelf_0 weakSelf = {(void*)0,(__Block_byref_weakSelf_0 *)&weakSelf, 33554432, sizeof(__Block_byref_weakSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self};
square tempBlock1 = ((int (*)(int))&__Person2__init_block_impl_0((void *)__Person2__init_block_func_0, &__Person2__init_block_desc_0_DATA, (__Block_byref_weakSelf_0 *)&weakSelf, 570425344));
(*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)) = (square)((id (*)(id, SEL))(void *)objc_msgSend)((id)tempBlock1, sel_registerName("copy"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_0, (*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)), tempBlock1);
}
return self;
}
static void _I_Person2_funTest(Person2 * self, SEL _cmd)
{
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_1, ((int (*)(__block_impl *, int))((__block_impl *)(*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)))->FuncPtr)((__block_impl *)(*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)), 5));
}
static void _I_Person2_dealloc(Person2 * self, SEL _cmd)
{
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person2"))}, sel_registerName("dealloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)(*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)), sel_registerName("release"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_2);
}
static int _I_Person2_i(Person2 self, SEL _cmd) { return ((int )((char )self + OBJC_IVAR_$_Person2$_i)); }
static void _I_Person2_setI_(Person2 self, SEL _cmd, int i) { ((int )((char )self + OBJC_IVAR_$_Person2$_i)) = i; }
执行步骤:
- 执行_I_Person2_init,生成__Block_byref_weakSelf_0中weakSelf,用weakSelf生成tempBlock对象。执行tempBlock的copy。
-
同样执行_Block_copy_internal(const void *arg, const int flags)中
struct Block_layout *aBlock;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;// Its a stack block. Make a copy.
if (!isGC) {struct Block_layout *result = malloc(aBlock->descriptor->size); if (!result) return (void *)0; memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first // reset refcount result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed result->flags |= BLOCK_NEEDS_FREE | 1; result->isa = _NSConcreteMallocBlock; if (result->flags & BLOCK_HAS_COPY_DISPOSE) { //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock); (*aBlock->descriptor->copy)(result, aBlock); // do fixup } return result;
}
- 执行上面对栈上的block进行copy,在堆上生成新的内存块,flag为BLOCK_NEEDS_FREE和~(BLOCK_REFCOUNT_MASK)记得这个后面会用。isa声明为堆的block。然后执行(*aBlock->descriptor->copy)(result, aBlock)即上面的__Person2__init_block_copy_0函数
- 执行
static void __Person2__init_block_copy_0(struct __Person2__init_block_impl_0dst, struct __Person2__init_block_impl_0src)
{
_Block_object_assign((void)&dst->weakSelf, (void)src->weakSelf, 8/BLOCK_FIELD_IS_BYREF/);
}
- 查看runtime.h执行
void _Block_object_assign(void destAddr, const void object, const int flags)
{
else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)
{
// copying a __block reference from the stack Block to the heap
// flags will indicate if it holds a __weak reference and needs a special isa
_Block_byref_assign_copy(destAddr, object, flags);
}
}
- 执行
static void _Block_byref_assign_copy(void dest, const void arg, const int flags)
{
struct Block_byref **destp = (struct Block_byref **)dest;
struct Block_byref *src = (struct Block_byref *)arg;
else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0)
{
//printf("making copy\n");
// src points to stack
bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
// if its weak ask for an object (only matters under GC)
struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
if (isWeak) {
copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning
}
if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
copy->byref_keep = src->byref_keep;
copy->byref_destroy = src->byref_destroy;
(*src->byref_keep)(copy, src);
}
}</code></pre>
其中最后(*src->byref_keep)(copy, src);即为:__Block_byref_id_object_copy_131,static void __Block_byref_id_object_copy_131(void *dst, void *src) {
// 4个指针加上2个int 48+24=40即__Block_byref_weakSelf_0中的person对象 131即为BLOCK_FIELD_IS_OBJECT|BLOCK_BYREF_CALLE
_Block_object_assign((char)dst + 40, (void ) ((char*)src + 40), 131);}
-
查看runTime.h
void _Block_object_assign(void destAddr, const void object, const int flags) {
//printf("_Block_object_assign(*%p, %p, %x)n", destAddr, object, flags);
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) { _Block_assign_weak(object, destAddr); } else { // do *not* retain or *copy* __block variables whatever they are _Block_assign((void *)object, destAddr); }
}
}中执行_Block_assign即只把最初栈中person的指针赋值到堆中。不会对person进行retain。也所以不会造成循环引用。