Welcome to the iOS Exploration series.

  • IOS explores the alloc process
  • IOS explores memory alignment &malloc source code
  • IOS explores ISA initialization & pointing analysis
  • IOS exploration class structure analysis
  • IOS explores cache_T analysis
  • IOS explores the nature of methods and the method finding process
  • IOS explores dynamic method resolution and message forwarding mechanisms
  • IOS explores the dyLD loading process briefly
  • The loading process of iOS explore classes
  • IOS explores the loading process of classification and class extension
  • IOS explores isa interview question analysis
  • IOS Explore Runtime interview question analysis
  • IOS explores KVC principles and customization
  • IOS explores KVO principles and customizations
  • IOS explores the principle of multithreading
  • IOS explores multi-threaded GCD applications
  • IOS explores multithreaded GCD underlying analysis
  • IOS explores NSOperation for multithreading
  • IOS explores multi-threaded interview question analysis
  • IOS Explore the locks in iOS
  • IOS explores the full range of blocks to read

preface

I believe you all have a certain understanding of block, the protagonist of this article, and can often see it in daily development. This article will explain the concept of block, BLCOK circular reference, block bottom three aspects

First acquaintance with Block

1. The block definition

An anonymous function with automatic variables (local variables) is called a block

It’s called differently in different languages

Programming language The name of the Block
C Block
Smalltalk Block
Ruby Block
Python Lambda
C++ Lambda
JS Anonymous function

2. Block classification

  • Global block –__NSGlobalBlock__
void (^block)(void) = ^ {NSLog(@ "111");
};
NSLog(@ "% @", block); -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- < __NSGlobalBlock__ :0x10a870050> -------------------- The command output is -------------------Copy the code
  • Heap block –__NSMallocBlock__
int a = 0;
void (^block)(void) = ^ {NSLog(@"%d", a);
};
NSLog(@ "% @", block); -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- < __NSMallocBlock__ :0x600002dca2b0> -------------------- The command output is -------------------Copy the code
  • Stack block –__NSStackBlock__
int a = 0;
NSLog(@ "% @"The ^ {NSLog(@"%d", a); }); -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- < __NSStackBlock__ :0x7ffeec41e1b8> -------------------- The command output is -------------------Copy the code

Conclusion:

  • A block that does not use external variables is__NSGlobalBlock__type
  • The block that uses external variables is__NSMallocBlock__type
  • The block before the heap block copy is__NSStackBlock__type

In addition, there are three system level block types (available in the libclosure source code)

  • _NSConcreteAutoBlock
  • _NSConcreteFinalizingBlock
  • _NSConcreteWeakBlockVariable

Block loop reference

1. Analysis of circular references

A circular reference

Capturing 'self' strongly in this block is likely to lead to a retain cycle
Copy the code

Let’s look at the problem with circular references:

  • selfHold theblock
  • blockHold theself(self.name)

This creates a circular reference to self -> block -> self

Then we have to mention memory management.

  • Normal release: A sends the dealloc signal to Bdealloc

  • Circular reference: A and B refer to each other, the reference count cannot be 0, and dealloc will not be called

Here are a few ways to work around circular references

2. Circular reference solution

2.1 Dance between strong and weak
__weak typeof(self) weakSelf = self;
self.name = @"Felix";
self.block = ^{
    NSLog(@ "% @", weakSelf.name);
};
Copy the code

Use the mediator mode __weak Typeof (self) weakSelf = self to change the circular reference to weakSelf -> self -> block -> weakSelf

On the surface, it still looks like a “reference circle”, but weakSelf -> self layer is weak reference — reference counting is not handled and weak table is used to manage. So at this point self will normally call dealloc during page destructor

But it’s not the final solution. There are still problems

__weak typeof(self) weakSelf = self;
self.name = @"Felix";
self.block = ^{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@ "% @", weakSelf.name);
    });
};
Copy the code

In the case of such delay, if the block is immediately returned to the previous page for page release, the self pointed to by weakSelf is nil after 3 seconds, and the printing at this time can only print null

Hence the term “strong holding”

__weak typeof(self) weakSelf = self;
self.name = @"Felix";
self.block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@ "% @", strongSelf.name);
    });
};
Copy the code

Weakself -> Self -> Block -> StrongSelf. Strongself -> WeakSelf -> Block -> StrongSelf

It looks like another circular reference, but in fact strongSelf is a temporary variable that will be released when the block scope ends, breaking the circular reference and releasing (delaying the release by 3 seconds)

2.2 Other intermediate modes

If you have auto empty, you can also do manual empty.

__block ViewController *vc = self;
self.name = @"Felix";
self.block = ^{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@ "% @", vc.name);
        vc = nil;
    });
};
Copy the code

The code above also uses the mediator pattern to break the loop application — breaking the loop reference by using VC as the mediator instead of self

Vc -> self -> block -> vc

But as long as blocks are not called, there are still loop applications

There is another way of dealing with circular references — not referencing them

self.name = @"Felix";
self.block = ^(ViewController *vc) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@ "% @", vc.name);
        vc = nil;
    });
}
Copy the code

Copy the above code with the current VC as an argument to the block, no holding, and use self’s memory space, perfectly avoiding circular references

3. Supplementary notes for circular references

  • MasonryIs there a circular reference in?

Monsary uses a block that is passed as a parameter to the view that holds self. The view that set the layout holds the block, but the block does not hold the view. When the block is finished, it will release the reference count of self. So it doesn’t cause circular references

  • Are there circular references in [UIView animateWithDuration: Animations :]?

UIView animations are class methods that are not held by Self (i.e. self holds the view, but the view is not instantiated), so they are not referenced in a loop

  • Using Facebook’s open source framework can detect the presence of circular references
- (void)checkLeaks {
    FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
    [detector addCandidate:self];
    NSSet *retainCycles = [detector findRetainCycles];
    NSLog(@ "% @", retainCycles);
}
Copy the code

Third, block bottom layer

1. Block essence

int main(a){
    __block int a = 10;
    void(^block)(void) = ^{
        a++;
        printf("Felix - %d",a);
    };
    block();
    return 0;
}
Copy the code

Output the above code as a CPP file with Clang to see the underlying implementation

clang -rewrite-objc main.c -o main.cpp
Copy the code
int main(a){

    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);
    return 0;
}
Copy the code
  1. fromThe main functionYou can see that the block assignment is__main_block_impl_0Type, which is a constructor in C++

A global search for __main_block_IMPL_0 shows its definition

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; }};Copy the code

Block is essentially a __main_block_IMPL_0 struct object. That’s why you can print blocks with %@

  1. Let’s look at the functions needed in the destructor:fpConcrete block implementations are passed__main_block_func_0And then stored in the block structureimplIn the

This shows that the block declaration only saves the implementation of the block, and the specific function implementation needs to be called

  1. Re-clang compilation when a block is a heap block (enclosing variables)
int main(a){
    
    int a = 10;
    void(^block)(void) = ^ {printf("Felix %d ", a);
    };
    
    block();
    return 0;
}
Copy the code

There will be an extra argument a in the block constructor and an extra property A in the block structure

We then turn our attention to the __main_block_func_0 implementation

  • __cselfis__main_block_impl_0Pointer to the block itself
  • int a = __cself->anamelyint a = block->a
  • Since a is just a property, the heap block is just a copy of the value (same value, different memory address).
  • This is why captured external variables cannot be manipulated directly, such asa++complains
  1. When __block decorates an external variable
int main(a){
    
    __block int a = 10;
    void(^block)(void) = ^ {printf("Felix %d ", a);
    };
    
    block();
    return 0;
}
Copy the code

__block

2. The signature block

Take a closer look at the Libclosure source code

Block_layout (which is equivalent to clang’s __Block_byref_a_0)

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
      BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

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

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor; //
    // imported variables
};
Copy the code

Where Block_layout is the basic block structure space, and some blocks have Block_descriptor_2 and Block_descriptor_3 structures, where flags record some information

  • Bit 1: Release flag. BLOCK_NEEDS_FREE is used for bit and operation, and is passed along with flags to indicate that the block can be released
  • Bit 16: The value to store the reference count, which is an optional argument
  • Bit 24: Indicates whether bit 16 is valid, which the program uses to decide whether to increase the value of the rocket Girl reference count bit
  • Bit 25: Whether you have a copy helper function
  • Bit 26: Whether you have a block destructor
  • Number 27: Indicates whether there is garbage collection
  • Bit 28: Whether the flag is a global block
  • Bit 30: Determines whether the current block has a signature, as opposed to BLOCK_USE_START, for dynamic invocation at Runtime

But part of the block has the Block_descriptor_2 and Block_descriptor_3 structure. Look at the explanation below

static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
    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)
{
    if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    desc += sizeof(struct Block_descriptor_1);
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
        desc += sizeof(struct Block_descriptor_2);
    }
    return (struct Block_descriptor_3 *)desc;
}
Copy the code
  • ifaBlock->flags & BLOCK_HAS_COPY_DISPOSETo meet,_Block_descriptor_2Exists, otherwise block does not_Block_descriptor_2This structure
    • _Block_descriptor_2Can be achieved byBlock_descriptor_1Memory offset derived
  • In the same way,aBlock->flags & BLOCK_HAS_SIGNATURETo meet,_Block_descriptor_3There are
    • _Block_descriptor_3Can be achieved byBlock_descriptor_2Memory offset derived

The absolute determinant of the existence of these two structures is the flags for Block_layout

// 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
};
Copy the code

Next, use assembly to look at the signatures in the block

  • __NSGlobalBlock__ signature (when _Block_copy enters)

  • __NSStackBlock__ signature (when _Block_copy enters)

  • __NSMallocBlock__ signature (when _Block_copy returns)

  • This block returns a null value
  • Block’s signature is@?, stands for unknown object

3. Block copy analysis

Next we’ll look at the process of converting down-stack blocks to heap blocks — _Block_copy

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;
    }
    else {
        // Its a stack block. Make a copy.
        struct Block_layout *result =
            (struct Block_layout *)malloc(aBlock->descriptor->size);
        if(! result)return NULL;
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
        // Resign the invoke pointer as it uses address authentication.
        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);
        // Set isa last so memory analysis tools see a fully-initialized object.
        result->isa = _NSConcreteMallocBlock;
        returnresult; }}Copy the code

The whole code is divided into three logical branches

  1. throughflagsIdentifying bit – Whether the value that stores the reference count is valid

The block reference count is not handled by the Runtime and is managed by itself

static int32_t latching_incr_int(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return BLOCK_REFCOUNT_MASK;
        }
        if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
            return old_value+2; }}}Copy the code

There might be a question here — why is the reference count +2 instead of +1?

Because the first position of flags already stores the release flag

  1. Is it a global block? – If it is, return block

  2. Stack block -> process of heap block

  • Through the firstmallocCreate a space in the heap area
  • throughmemmoveCopy data from stack to heap
  • invoke,flagsModify at the same time
  • Block’s ISA is marked as_NSConcreteMallocBlock

4.__block in-depth exploration

To better explore, we compiled clang in OC’s main file

xcrun -sdk iphonesimulator clang -rewrite-objc main.m
Copy the code
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
        __block NSString *name = [NSString stringWithFormat:@"Felix"];
        void (^fxBlock)(void) = ^ {// block_copy
            name = @"Feng Felix";
        };
        fxBlock();
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
Copy the code
4.1 Layer 1 Copy (Block)

The first layer of copying in blocks is already covered — _Block_copy copies blocks from the stack to the heap

4.2 Layer 2 Copy (Memory space for capturing variables)

__main_block_desc_0_DATA structure
__main_block_copy_0
__main_block_copy_0
_Block_object_assign

Let’s take a look at what _Block_object_assign does at the bottom.

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]; * * * * * * * * /

        _Block_retain_object(object);
        *dest = object;
        break;

      case BLOCK_FIELD_IS_BLOCK:
        /******* void (^object)(void) = ... ; [^{ object; } copy]; * * * * * * * * /

        *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; }}Copy the code

The captured variables are processed in different branches according to flags & BLOCK_ALL_COPY_DISPOSE_FLAGS

Enumerated values The numerical meaning
BLOCK_FIELD_IS_OBJECT 3 object
BLOCK_FIELD_IS_BLOCK 7 Block variables
BLOCK_FIELD_IS_BYREF 8 A structure decorated by __block
BLOCK_FIELD_IS_WEAK 16 An __weak variable
BLOCK_BYREF_CALLER 128 When processing block_byref internal object memory

An additional tag will be added, used in conjunction with the enumeration above

*dest = _Block_byref_copy(object); *dest = _Block_byref_copy(object);

static struct Block_byref* _Block_byref_copy(const void *arg) {
    // Save temporary variables
    struct Block_byref *src = (struct Block_byref *)arg;

    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack
        // Generate a Block_byref in the heap with the size of the original target
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        
        // Both the original and new regions point to the same object, giving the block the ability to modify
        copy->forwarding = copy; // patch heap copy to point to itself
        src->forwarding = copy;  // patch stack to point to heap copy
        
        copy->size = src->size;

        if (src->flags & BLOCK_BYREF_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
            struct Block_byref_2 *src2 = (struct Block_byref_2(*)src+ 1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2(*)copy+ 1);
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3(*)src2+ 1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3(*)copy2+ 1);
                copy3->layout = src3->layout;
            }

            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src)); }}// already copied to heap
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}
Copy the code
  • In the original targetnameGenerates a Block_byref in the heap area
  • copy->forwarding = copy; & src->forwarding = copy;Both the original and new regions point to the same object, giving blocks the ability to modify
  • (*src2->byref_keep)(copy, src)Start layer 3 copy
4.3 Layer 3 Copy (Copying Objects)

(*src2->byref_keep)(copy, SRC) this leads to the Block_byref structure, and byref_keep is the 5th attribute of Block_byref

struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};

Copy the code

The __block modifier actually calls the following constructor at the bottom level — the 5th bit is byref_keep, so __Block_byref_id_object_copy_131 is called at the second level

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _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);
}
Copy the code

The fifth function calls the _Block_object_assign function

(char*) DST + 40 In fact, the __Block_byref_name_0 Epiphany, just get the variable name object

struct __Block_byref_name_0 {
  void *__isa;
__Block_byref_name_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSString *name;
};
Copy the code

The _Block_object_assign case does the following for BLOCK_FIELD_IS_OBJECT:

case BLOCK_FIELD_IS_OBJECT:
    /******* id object = ... ; [^{ object; } copy]; * * * * * * * * /

    _Block_retain_object(object);
    *dest = object;
    break;
Copy the code
  • _Block_retain_objectIs an empty function because the peripheral variables captured by the block are automatically managed by ARC
  • Capture thenameTo copy
4.4 _Block_object_dispose

After looking at the three-tier copy, take a look at the release function _Block_object_dispose

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; }}static void _Block_byref_release(const void *arg) {
    struct Block_byref *byref = (struct Block_byref *)arg;

    // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
    byref = byref->forwarding;
    
    if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
        int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
        os_assert(refcount);
        if (latching_decr_int_should_deallocate(&byref->flags)) {
            if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                struct Block_byref_2 *byref2 = (struct Block_byref_2(*)byref+ 1);
                (*byref2->byref_destroy)(byref);
            }
            free(byref); }}}Copy the code
  • Do nothing if it is a free object (auto-free)
  • If it is__blockModifier, then point the pointer back to the original area and use itfreeThe release of

5. To summarize

  • The essence of a block is that__main_block_impl_0Structure object, so it can be used% @print
  • The block declaration simply stores the block implementation, which is required by the specific function implementationTo call
  • The structure of a block when it captures an external variableAutomatically generate a propertyTo save the variable
  • __blockThe modified property generates the response structure underneath, holds a pointer to the original variable, and passes onePointer to the addressTo block
  • There are three levels of copying in blocks: copying blocks, copying memory addresses of captured variables, and copying objects

Write in the back

Block and hook also need to be learned and understood

Small blocks also have a lot of basic knowledge to study, the more you learn to find yourself smaller, but in fact, it is not true, you just broaden your perspective, which is exactly how you will make progress