block深度剖析



title: block深度剖析
date: 2016-04-18 23:46:43

tags: block

分两部分内容来剖析block:

  1. 怎么用
  2. 为什么这么用

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;
}

代码看上去有些多,看主要信息:

  1. 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的基类。
  2. 顺序:创建tempBlock1对象,执行block的函数体。
  3. 为什么不能在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;
}

只看不同的代码:
1. 多了个__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内外改变其中的值是联动的即同一份。
2. 多了个__main_block_copy_0:如果block从栈中复制到堆中时,会调用此函数,里面的实现就是把__Block_byref_i_0对象i栈的i->forwarding指向__Block_byref_i_0堆中的地址。这样堆中操作了,栈中也会联动改变。也是forwarding的作用(代码实现在下面)。
3. __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 *)&copy->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

发现:

  1. A和C的dealloc会调用,而B的dealloc不会调用
  2. A中的block为栈的block,B和C的tempBlock为堆的block(从栈中copy了)
  3. 说明栈的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查看上面代码的执行过程:

  1. 先执行_I_Person1_init这个函数,执行block的copy。查看runtime.h中block_copy会执行_Block_copy_internal
  2. 执行_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;
    }
  3. 执行上面对栈上的block进行copy,在堆上生成新的内存块,flag为BLOCK_NEEDS_FREE和~(BLOCK_REFCOUNT_MASK)记得这个后面会用。isa声明为堆的block。然后执行(*aBlock->descriptor->copy)(result, aBlock)即上面的__Person1__init_block_copy_0函数
  4. 执行_Block_object_assign((void)&dst->self, (void)src->self, 3/
    BLOCK_FIELD_IS_OBJECT/);查看runtime.h查看其实现。
  5. 执行的代码如下:
    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);
    }
    }
  6. 对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; }

执行步骤:

  1. 执行_I_Person2_init,生成__Block_byref_weakSelf_0中weakSelf,用weakSelf生成tempBlock对象。执行tempBlock的copy。
  2. 同样执行_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;
    }
  3. 执行上面对栈上的block进行copy,在堆上生成新的内存块,flag为BLOCK_NEEDS_FREE和~(BLOCK_REFCOUNT_MASK)记得这个后面会用。isa声明为堆的block。然后执行(*aBlock->descriptor->copy)(result, aBlock)即上面的__Person2__init_block_copy_0函数
  4. 执行
    static void Person2init_block_copy_0(struct Person2init_block_impl_0dst, struct Person2init_block_impl_0src)
    {
    _Block_object_assign((void)&dst->weakSelf, (void)src->weakSelf, 8/BLOCK_FIELD_IS_BYREF/);
    }
  5. 查看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);
    }
    }
  6. 执行
    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);
        }
    }

    其中最后(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);}

  7. 查看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。也所以不会造成循环引用。

时间: 2024-12-28 04:06:27

block深度剖析的相关文章

大众点评开源分布式监控平台 CAT 深度剖析

一.CAT介绍 CAT系统原型和理念来源于eBay的CAL的系统,CAT系统第一代设计者吴其敏在eBay工作长达十几年,对CAL系统有深刻的理解.CAT不仅增强了CAL系统核心模型,还添加了更丰富的报表.自2014年开源以来,CAT在携程.陆金所.猎聘网.找钢网等多家互联网公司生产环境应用. CAT是一个实时和接近全量的监控系统,它侧重于对Java应用的监控,基本接入了美团点评上海侧所有核心应用.目前在中间件(MVC.RPC.数据库.缓存等)框架中得到广泛应用,为美团点评各业务线提供系统的性能指

深度剖析百度百科单页高排名的内因

百度百科在seoer心中一直是个神话,单页高排名.如何能使自己的网站做到向百科这样的优,走进百科代码,深度剖析百度百科单页高排名的内因. 一.Title Description Keywords如何写   从这个图片中可以看出,百度百科提倡精简不臃肿的title书写方式,而不是现在大家追求的关键词堆叠,其次大家可能也发现了,description和keywords都已经不存在了,只有标题,显示在搜索引擎中的描述都是自动提取网页的前一部分.也就是说你可以完全忽略你的后两项. 二.LOGO如何用  

对Ruby VS Java误区的深度剖析

Relevance咨询公司的Stuart Halloway最近编写了一个关于"Ruby vs. Java之怪谈"的系列博客文章 .这个系列文章的灵感,源自他最近从一个从零起步.没有先前约束的Ruby项目转回一个成熟完备的Java 项目后的心得体会.在这个历时多日的项目过程中,Halloway对以下几个"误区"进行了探索: 误区之一:Ruby适合小型项目,而Java更适用于大型的.复杂的项目. 概括起来,Halloway主张,对于小型项目来说,诸如未知因素一类的问题可

DOCTYPE 标签的深度剖析以及使用选择

  <!DOCTYPE>的定义: <!DOCTYPE>声明位于文档中的最前面的位置,处于<html>标签之前.此标签可告知浏览器文档使用哪种HTML或XHTML规范. 该标签可声明三种DTD类型,分别表示严格版本.过渡版本以及基于框架的HTML版本.(假如文档中的标记不遵循doctype声明所指定的DTD,这个文档除了不能通过代码校验之外,还有可能无法在浏览器中正确显示.) <!DOCTYPE>的用法: <!DOCTYPE html PUBLIC &q

深度剖析Struts2远程代码执行漏洞

本文讲的是深度剖析Struts2远程代码执行漏洞, 三月初,安全研究人员发现世界上最流行的JavaWeb服务器框架之一– Apache Struts2存在远程代码执行的漏洞,Struts2官方已经确认该漏洞(S2-046,CVE编号为:CVE-2017-5638)风险等级为高危漏洞. 漏洞描述 该漏洞是由于上传功能的异常处理函数没有正确处理用户输入的错误信息,导致远程攻击者可通过修改HTTP请求头中的Content-Type值,构造发送恶意的数据包,利用该漏洞进而在受影响服务器上执行任意系统命令

深度剖析俄罗斯黑客组织APT29的后门

本文讲的是深度剖析俄罗斯黑客组织APT29的后门,POSTSPY最大程度的利用了内置于Windows系统中的特性来设置隐蔽的后门,这些特性被称为"living off the land"(意思是入侵者使用系统凭据和合法的软件管理工具访问系统,感染和收集有价值的数据).POSHSPY使用WMI来存储后门代码,使其对不熟悉WMI机制的人不可见.PowerShell只有在合法系统过程能被执行,如果是恶意代码执行,能通过加强日志分析或者内存就可以发现. POSHSPY中的特点,如罕见的后门信标

深度剖析Petya病毒:反社会人格的恶性病毒 只为破坏不为牟利

本文讲的是深度剖析Petya病毒:反社会人格的恶性病毒 只为破坏不为牟利, 一.概述 6月27日晚间,代号为"Petya"的勒索病毒肆虐全球,根据外国媒体报道,俄罗斯石油公司Rosneft.乌克兰国家储蓄银行和政府系统都受到了攻击,仅俄.乌两国就有80多家公司被该病毒感染,就连乌克兰副总理的电脑也不幸中招.其他受影响的国家包括英国.印度.荷兰.西班牙.丹麦等. 经过深度分析,火绒安全团队惊讶地发现,Petya和以往的勒索病毒有很大不同–病毒作者精心设计制作了传播.破坏的功能模块,勒索赎

《Photoshop混合模式深度剖析》—第2章构建参考图像

构建参考图像 Photoshop混合模式深度剖析 实现色彩混合或是其他效果的方法有很多,很多人更习惯于处理真实的照片,但我更喜欢通过抽象的示意图来解决问题.因此,我需要建立示例文件,并调整各种参数以实现预期的效果,这样就不会受到真实照片中复杂色彩的干扰.使用这种方法的关键是,注意操作和根据色彩及其他调整而生成的结果之间的关系.尽管可以设计许多其他文件和方案,但最好的测试方法通常是处理自己想要调整的图像.将参考文件放在触手可及之处,或者是在灵感来临时构建参考文件,这是更好地处理具体图像的一种方式.

深度剖析ConcurrentHashMap源码

概述 你可能会在一些技术类的书籍上看到下面这样一段关于HahsMap和Hashtable的表述: HashMap是非线程安全的,Hashtable是线程安全的. 不知道大家有什么反应,我当时只是记住了,知道面试的时候能回答上来就行了-至于为什么是线程安全的,内部怎么实现的,却不怎么了解. 今天我们将深入剖析一个比Hashtable性能更优的线程安全的Map类,它就是ConcurrentHashMap,本文基于Java 7的源码做剖析. ConcurrentHashMap的目的 多线程环境下,使用