In-depth exploration of iOS block

tags: block

Analyze the block in two parts:

  1. how to use
  2. Why use it so

block stack

classification:

According to the location of the block in the memory, it is divided into three types: NSGlobalBlock, NSStackBlock, and NSMallocBlock.

NSGlobalBlock: similar function, located in the text section; NSStackBlock: located in the stack memory, Block will be invalid after the function returns; NSMallocBlock: located in the heap memory. Need to be released by the developer.
Distinguish: Under non-ARC, the one without external variable reference is NSGlobalBlock, the one that references external variable is NSStackBlock, and copy of NSStakBlock is NSMallocBlock.
Memory management:

The block is generated at compile time, not at runtime. So if external variables are used during compilation, a snapshot of the variables will be placed on the stack.

  • NSGlobalBlock: The life cycle starts from the beginning of the application to the end of the application. It doesn't make any sense to retain/release/copy it, or return to itself.
  • NSStackBlock: disappear after the function returns. It doesn't make any sense to retain/release it, or return to itself. Copying it will copy the content to the heap memory (NSMallocBlock) and generate a new memory block.
  • NSMallocBlock: It needs to be released by the programmer. Retaining is the same as copying, except that the counter of the pointed object is increased by 1, and 1 is always displayed when printing, but the actual counter has increased. Decrease the release counter by one, but when the counter is printed, 1 is always displayed.

 

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了 这里
}

 

Principle of Modifying Variable Value

 

typedef int (^square)(int);

int i = 6;
square tempBlock1 = ^(int a){return a * i; };

printf("=====%d", tempBlock1(5));

If you want to change the value of i in tempBlock1 is not allowed, why? Check the underlying implementation of the code: clang -rewrite-objc block2.c, remove unnecessary code and keep the main code:

 

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) {

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. 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()
{

 
  1. int i = 6;

  2. square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));

  3.  
  4. printf("=====%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5));

  5.  
  6. return 0;

}

The code looks a lot, look at the main information:

  1. __block_impl: isa refers to an object, the pointer to the class to which it belongs is the type of the block (here, the stack object), flags records the identifier of the block, reserved: reserved fields for expansion, funcptr: the function body executed by the block, that is The realization of block. __main_block_impl_0: Contains __block_impl objects and __main_block_desc_0 (that is, records the size of __main_block_impl_0), and the image of external variables. Personal understanding actually __block_impl is the base class of __main_block_impl_0.
  2. Sequence: Create a tempBlock1 object and execute the function body of the block.
  3. Why can't the variable value be changed directly in the block, because only the formal parameter is passed. If it is changed, the i in tempBlock1 can only be changed, but the external variable i will not be changed, so this operation is simply prohibited. So how can it be linked to the outside world?

 

{
   
   
 
  1. __block int i = 6;

  2. square tempBlock1 = ^(int a){ i = i + 1; return a * i; };

  3.  
  4. i = 7;

  5.  
  6. printf("=====%d,i=%d", tempBlock1(5), i);

  7.  
  8. return 0;

}

Also check the source code and remove unnecessary code:

 

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) {

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. 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()
{

 
  1. __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 6};

  2. square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

  3.  
  4. (i.__forwarding->i) = 7;

  5.  
  6. printf("=====%d,i=%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5), (i.__forwarding->i));

  7.  
  8. return 0;

}

Just look at the different codes:

  1. One more __Block_byref_i_0: Generate __Block_byref_i_0 object i to store the value of external variables, so that the pointer of __Block_byref_i_0 object i is passed into the block, and the value of int variable i in __Block_byref_i_0 object i is directly changed in the stack function funptr, because block Both external and internal use the same object pointer, so changing the value inside and outside the block is linked, that is, the same.
  2. One more __main_block_copy_0: If the block is copied from the stack to the heap, this function will be called. The implementation inside is to point the i->forwarding of the __Block_byref_i_0 object i stack to the address in the __Block_byref_i_0 heap. In this way, the operation in the heap will also change the stack. It is also the role of forwarding (code implementation is below).
  3. __main_block_dispose_0: This function is called when the block is released.

For the specific source code, view the source code of these implementations in Apple's blockRuntime. Click here for the URL :

void _Block_object_assign(void destAddr, const void object, const int flags)
{
   
   
 
  1. //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);

  2. if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER)

  3. {

  4. if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK)

  5. {

  6. _Block_assign_weak(object, destAddr);

  7. }

  8. else

  9. {

  10. // do *not* retain or *copy* __block variables whatever they are

  11. _Block_assign((void *)object, destAddr);

  12. }

  13. }

  14. else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)

  15. {

  16. // copying a __block reference from the stack Block to the heap

  17. // flags will indicate if it holds a __weak reference and needs a special isa

  18. _Block_byref_assign_copy(destAddr, object, flags);

  19. }

  20. // (this test must be before next one)

  21. else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK)

  22. {

  23. // copying a Block declared variable from the stack Block to the heap

  24. _Block_assign(_Block_copy_internal(object, flags), destAddr);

  25. }

  26. // (this test must be after previous one)

  27. else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT)

  28. {

  29. //printf("retaining object at %p\n", object);

  30. _Block_retain_object(object);

  31. //printf("done retaining object at %p\n", object);

  32. _Block_assign((void *)object, destAddr);

  33. }

}

static void _Block_byref_assign_copy(void dest, const void arg, const int flags)
{

 
  1. struct Block_byref **destp = (struct Block_byref **)dest;

  2. struct Block_byref *src = (struct Block_byref *)arg;

  3. if (src->forwarding->flags & BLOCK_IS_GC)

  4. {

  5. ; // don't need to do any more work

  6. }

  7. else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0)

  8. {

  9. //printf("making copy\n");

  10. // src points to stack

  11. bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));

  12. // if its weak ask for an object (only matters under GC)

  13. struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);

  14. copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack

  15. copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)

  16. src->forwarding = copy; // patch stack to point to heap copy

  17. copy->size = src->size;

  18. if (isWeak) {

  19. copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning

  20. }

  21. if (src->flags & BLOCK_HAS_COPY_DISPOSE) {

  22. // Trust copy helper to copy everything of interest

  23. // If more than one field shows up in a byref block this is wrong XXX

  24. copy->byref_keep = src->byref_keep;

  25. copy->byref_destroy = src->byref_destroy;

  26. (*src->byref_keep)(copy, src);

  27. }

  28. else {

  29. // just bits. Blast 'em using _Block_memmove in case they're __strong

  30. _Block_memmove(

  31. (void *)&copy->byref_keep,

  32. (void *)&src->byref_keep,

  33. src->size - sizeof(struct Block_byref_header));

  34. }

  35. }

  36. // already copied to heap

  37. else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {

  38. latching_incr_int(&src->forwarding->flags);

  39. }

  40. // assign byref data block pointer into new Block

  41. _Block_assign(src->forwarding, (void **)destp);

}

 

Circular reference

phenomenon

Do [[Person alloc] init] and [Person release] for the following three Person classes respectively, and check whether dealloc will be called in the Person class.

Type 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)
{

 
  1. _i = 8;

  2. tempBlock = ^(int a){

  3. return a * _i;

  4. };

  5.  
  6. NSLog(@"tempBlock:%@", tempBlock);

}

return self;
}

(void) funTest
{

NSLog(@"tmpBlock:%d", tempBlock(5));

}

(void) dealloc
{

 
  1. [super dealloc];

  2.  
  3. NSLog(@"dealloc run");

}

@end

Class 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)
{

 
  1. _i = 8;

  2. square tempBlock1 = ^(int a){

  3. return a * _i;

  4. };

  5.  
  6. tempBlock = [tempBlock1 copy];

  7.  
  8. NSLog(@"tempBlock:%@, temptBlock1:%@", tempBlock, tempBlock1);

}

return self;
}

(void) funTest
{

NSLog(@"tmpBlock:%d", tempBlock(5));

}

(void) dealloc
{

 
  1. [super dealloc];

  2.  
  3. NSLog(@"dealloc run");

}

@end

Class 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)
{

 
  1. _i = 8;

  2. __block Person2* weakSelf = self;

  3. square tempBlock1 = ^(int a){

  4. return a * weakSelf.i;

  5. };

  6.  
  7. tempBlock = [tempBlock1 copy];

  8.  
  9. NSLog(@"tempBlock:%@, temptBlock1:%@", tempBlock, tempBlock1);

}

return self;
}

(void) funTest
{

NSLog(@"tmpBlock:%d", tempBlock(5));

}

(void) dealloc
{

 
  1. [super dealloc];

  2.  
  3. NSLog(@"dealloc run");

}

@end

Find:

  1. Dealloc of A and C will be called, but dealloc of B will not be called
  2. The block in A is the stack block, and the tempBlocks in B and C are the heap block (copy from the stack)
  3. It means that the blocks of the stack will not strongly reference self, while the blocks of the heap will strongly reference self. If __block Person2* weakSelf = self; is added, then the variable of weakSelf will not be referenced strongly in the block.

why?

Essential realization

Looking at the source code implementation, you know that when the block on the stack is copied to the heap, the person object is retained, and when the person object is added with __block, the person is not retained within the block. The details are as follows:
Clang view source code for person1 code is as follows:

 

struct __block_impl 
{
   
   
 
  1. void *isa;

  2. int Flags;

  3. int Reserved;

  4. void *FuncPtr;

};

struct Person1_IMPL
{

 
  1. struct NSObject_IMPL NSObject_IVARS;

  2. square tempBlock;

  3. 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)
{

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. Desc = desc;

}
};

static int __Person1__init_block_func_0(struct __Person1__init_block_impl_0 *__cself, int a)
{

 
  1. Person1 *self = __cself->self; // bound by copy

  2. 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)
{

 
  1. _Block_object_assign((void*)&dst->self, (void*)src->self, 3/

  2. *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
{

 
  1. size_t reserved;

  2. size_t Block_size;

  3. void (*copy)(struct __Person1__init_block_impl_0*, struct __Person1__init_block_impl_0*);

  4. 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)
{

 
  1. self = ((Person1 *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person1"))}, sel_registerName("init"));

  2. if (self)

  3. {

  4. (*(int *)((char *)self + OBJC_IVAR_$_Person1$_i)) = 8;

  5. square tempBlock1 = ((int (*)(int))&__Person1__init_block_impl_0((void *)__Person1__init_block_func_0, &__Person1__init_block_desc_0_DATA, self, 570425344));

  6.  
  7. (*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)) = (square)((id (*)(id, SEL))(void *)objc_msgSend)((id)tempBlock1, sel_registerName("copy"));

  8.  
  9. NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person1_617ecb_mi_0, (*(square *)((char *)self + OBJC_IVAR_$_Person1$tempBlock)), tempBlock1);

  10. }

  11.  
  12. 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)
{

 
  1. ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person1"))}, sel_registerName("dealloc"));

  2.  
  3. 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

Combine runtime.h to view the execution process of the above code:

  1. First execute the function _I_Person1_init to execute the copy of the block. View block_copy in runtime.h will execute _Block_copy_internal
  2. Execute _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) {
     
    1. struct Block_layout *result = malloc(aBlock->descriptor->size);

    2. if (!result) return (void *)0;

    3. memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first

    4. // reset refcount

    5. result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed

    6. result->flags |= BLOCK_NEEDS_FREE | 1;

    7. result->isa = _NSConcreteMallocBlock;

    8. if (result->flags & BLOCK_HAS_COPY_DISPOSE) {

    9. //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);

    10. (*aBlock->descriptor->copy)(result, aBlock); // do fixup

    11. }

    12. return result;

    }

  3. Perform the above copy to the block on the stack and generate a new memory block on the heap. The flag is BLOCK_NEEDS_FREE and ~(BLOCK_REFCOUNT_MASK). Remember this will be used later. isa is declared as a block of the heap. Then execute (*aBlock->descriptor->copy)(result, aBlock) which is the above __Person1__init_block_copy_0 function
  4. Execute _Block_object_assign((void )&dst->self, (void )
    src- >self, 3/ BLOCK_FIELD_IS_OBJECT /); check runtime.h to see its implementation.
  5. The executed code is as follows:

    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) {
         
         
     
    1. //printf("retaining object at %p\n", object);

    2. _Block_retain_object(object);

    3. //printf("done retaining object at %p\n", object);

    4. _Block_assign((void *)object, destAddr);

    }
    }

  6. Object, src->self, the person held by the block is retained, and _block_assign assigns the memory address of the person object held on the stack to the address of the person object on the heap. In this way, pointing to a person object can directly manipulate the variables and values ​​of the person object.

Similarly, compile the source code of person2, check runtime.c, and analyze the compiled source code in the next step:

 

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) {

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. Desc = desc;

}
};

static int __Person2__init_block_func_0(struct __Person2__init_block_impl_0 *__cself, int a)
{

 
  1. __Block_byref_weakSelf_0 *weakSelf = __cself->weakSelf; // bound by ref

  2. 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) {

 
  1. self = ((Person2 *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person2"))}, sel_registerName("init"));

  2. if (self)

  3. {

  4. //33554432即为BLOCK_HAS_COPY_DISPOSE

  5. (*(int *)((char *)self + OBJC_IVAR_$_Person2$_i)) = 8;

  6. __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};

  7. 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));

  8. (*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)) = (square)((id (*)(id, SEL))(void *)objc_msgSend)((id)tempBlock1, sel_registerName("copy"));

  9. NSLog((NSString *)&__NSConstantStringImpl__var_folders_4x_l4ckkvk570s3grlbg_9fdk_40000gn_T_Person2_62cdd7_mi_0, (*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)), tempBlock1);

  10. }

  11. 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)
{

 
  1. ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person2"))}, sel_registerName("dealloc"));

  2. ((void (*)(id, SEL))(void *)objc_msgSend)((id)(*(square *)((char *)self + OBJC_IVAR_$_Person2$tempBlock)), sel_registerName("release"));

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

Steps:

  1. Execute _I_Person2_init to generate weakSelf in __Block_byref_weakSelf_0, and use weakSelf to generate tempBlock objects. Perform a copy of tempBlock.
  2. Also execute _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) {
         
         
     
    1. struct Block_layout *result = malloc(aBlock->descriptor->size);

    2. if (!result) return (void *)0;

    3. memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first

    4. // reset refcount

    5. result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed

    6. result->flags |= BLOCK_NEEDS_FREE | 1;

    7. result->isa = _NSConcreteMallocBlock;

    8. if (result->flags & BLOCK_HAS_COPY_DISPOSE) {

    9. //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);

    10. (*aBlock->descriptor->copy)(result, aBlock); // do fixup

    11. }

    12. return result;

    }

  3. Perform the above copy of the block on the stack to generate a new memory block on the heap. The flag is BLOCK_NEEDS_FREE and ~(BLOCK_REFCOUNT_MASK). Remember this will be used later. isa is declared as a block of the heap. Then execute (*aBlock->descriptor->copy)(result, aBlock) which is the above __Person2__init_block_copy_0 function
  4. carried out
    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/);
}

  1. View runtime.h execution
    void _Block_object_assign(void destAddr, const void object, const int flags)

{

 
  1. else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)

  2. {

  3. // copying a __block reference from the stack Block to the heap

  4. // flags will indicate if it holds a __weak reference and needs a special isa

  5. _Block_byref_assign_copy(destAddr, object, flags);

  6. }

}

  1. carried out
    static void _Block_byref_assign_copy(void dest, const void arg, const int flags)

{

 
  1. struct Block_byref **destp = (struct Block_byref **)dest;

  2. struct Block_byref *src = (struct Block_byref *)arg;

  3.  
  4. else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0)

  5. {

  6. //printf("making copy\n");

  7. // src points to stack

  8. bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));

  9. // if its weak ask for an object (only matters under GC)

  10. struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);

  11. copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack

  12. copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)

  13. src->forwarding = copy; // patch stack to point to heap copy

  14. copy->size = src->size;

  15. if (isWeak) {

  16. copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning

  17. }

  18. if (src->flags & BLOCK_HAS_COPY_DISPOSE) {

  19. // Trust copy helper to copy everything of interest

  20. // If more than one field shows up in a byref block this is wrong XXX

  21. copy->byref_keep = src->byref_keep;

  22. copy->byref_destroy = src->byref_destroy;

  23. (*src->byref_keep)(copy, src);

  24. }

  25. }</code></pre>

  26. 其中最后(*src->byref_keep)(copy, src);即为:__Block_byref_id_object_copy_131,static void __Block_byref_id_object_copy_131(void *dst, void *src) {

// 4 pointers plus 2 int 4 8+2 4=40, that is, the person object 131 in __Block_byref_weakSelf_0 is BLOCK_FIELD_IS_OBJECT|BLOCK_BYREF_CALLE
_Block_object_assign((char )dst + 40, (void ) ((char*)src + 40 ), 131);}

  1. View 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) {
         
         
     
    1. if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {

    2. _Block_assign_weak(object, destAddr);

    3. }

    4. else {

    5. // do *not* retain or *copy* __block variables whatever they are

    6. _Block_assign((void *)object, destAddr);

    7. }

    }
    }

    Execute _Block_assign in the middle, that is, only assign the pointer of the person in the initial stack to the heap. The person will not be retained. So it will not cause circular references.

Guess you like

Origin blog.csdn.net/wangletiancsdn/article/details/104380807