• block 归纳总结 上


    block的类型

    block有三种类型:堆block,栈block,全局block。
    对于这三种类型的block遵循俩个原则:
    block如果没有使⽤外部变量,或者只使⽤静态变量和全局变量,那⼀定是全局blcok。
    block如果使⽤了外部变量,⽽且不是静态变量或全局变量,如果赋值给强引⽤的是堆block,
    如果赋值给弱引⽤的是栈blcok。

    为什么block要⽤copy关键字修饰

    因为block在创建的时候,它的内存是分配在栈上的,⽽不是在堆上。栈区的特点是:对象随时有
    可能被销毁,⼀旦被销毁,在调⽤的时候,就会造成系统的崩溃。所以我们要使⽤copy把它拷⻉
    到堆上。在ARC下, 对于block使⽤copy与strong其实都⼀样, 因为block的retain就是⽤copy来实现
    的, 所以在ARC下 block使⽤ copy 和 strong 都可以。

    block的源码解析

    #if TARGET_OS_WIN32 // win32 __sync_bool_compare_and_swap()函数
    #define _CRT_SECURE_NO_WARNINGS 1
    #include <windows.h>
    static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) 
    { 
        // fixme barrier is overkill -- see objc-os.h
        long original = InterlockedCompareExchange(dst, newl, oldl);
        return (original == oldl);
    }
    
    static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) 
    { 
        // fixme barrier is overkill -- see objc-os.h
        int original = InterlockedCompareExchange(dst, newi, oldi);
        return (original == oldi);
    }
    #else
    
    // __sync_bool_compare_and_swap 是 GCC 内建的原子操作函数, 执行CAS操作,比较 _Ptr 和 _Old。 如果相等就将 _New 放到 _Ptr 中,并且返回true,否则返回false
    #define OSAtomicCompareAndSwapLong(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
    #define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
    #endif
    
    
    /*******************************************************************************
    Internal Utilities
    ********************************************************************************/
    
    // 引用计数加 1,最多不超过 BLOCK_REFCOUNT_MASK
    // volatile的作用是:作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化
    static int32_t latching_incr_int(volatile int32_t *where) {
        while (1) {
            int32_t old_value = *where;
            // 如果 old_value 在第 1~15 位都已经变为 1 了,即引用计数已经满了,就返回 BLOCK_REFCOUNT_MASK
            if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
                return BLOCK_REFCOUNT_MASK;
            }
            // 比较 where 处的现在的值是否等于 old_value,如果等于,就将新值 oldValue + 2 放入 where
            // 否则继续下一轮循环
            // 这里加 2,是因为 flag 的第 0 位已经被占了,引用计数是第 1~15 位,所以加上 0b10,引用计数只是加 1
            if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
                return old_value+2;
            }
        }
    }
    
    // 当 block 不是处于 dealloc 时,引用计数加 1
    // 返回值是是否成功,只有在 block 处于 dealloc 时,才会失败
    static bool latching_incr_int_not_deallocating(volatile int32_t *where) {
        while (1) {
            int32_t old_value = *where;
            // 如果 block 正在 dealloc,返回 false
            if (old_value & BLOCK_DEALLOCATING) {
                // if deallocating we can't do this
                return false;
            }
            // 引用计数最多不超过 BLOCK_REFCOUNT_MASK
            if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
                // if latched, we're leaking this block, and we succeed
                return true;
            }
            // 引用计数加 1,这里 old_value+2 的原因和 latching_incr_int 一致
            // 如果失败,进行下一轮循环
            if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
                // otherwise, we must store a new retained value without the deallocating bit set
                return true;
            }
        }
    }
    
    
    // return should_deallocate?
    // 引用计数减 1,如果引用计数减到了 0,就将 block 置为 deallocating 状态
    // 返回值是 block 是否需要被 dealloc
    static bool latching_decr_int_should_deallocate(volatile int32_t *where) {
        while (1) {
            int32_t old_value = *where;
            // 如果引用计数还是满的,就不能 dealloc,#疑问:引用计数满了以后就不能减了么
            if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
                return false; // latched high
            }
            // 如果引用计数为 0,按照正常的逻辑,它应该已经被置为 deallocating 状态,不需要再被 dealloc,所以返回 false
            if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
                return false;   // underflow, latch low
            }
            int32_t new_value = old_value - 2; // 引用计数减
            bool result = false;
            // 如果 old_value 在 0~15 位的值是 0b10,即引用计数是 1,且不是 deallocating 状态
            if ((old_value & (BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING)) == 2) {
                // old_value 减 1 后,新值 new_value 在 0~15 位值是 0b01,即引用计数变为0,且 BLOCK_DEALLOCATING 位变为 1
                new_value = old_value - 1;
                // 需要被 dealloc
                result = true;
            }
            // 将新值 new_value 放入 where 中
            if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
                return result;
            }
        }
    }
    
    
    /**************************************************************************
    Framework callback functions and their default implementations.
    ***************************************************************************/
    #if !TARGET_OS_WIN32
    #pragma mark Framework Callback Routines
    #endif
    
    static void _Block_retain_object_default(const void *ptr __unused) { }
    
    static void _Block_release_object_default(const void *ptr __unused) { }
    
    static void _Block_destructInstance_default(const void *aBlock __unused) {}
    
    static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
    static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
    static void (*_Block_destructInstance) (const void *aBlock) = _Block_destructInstance_default;
    
    
    /**************************************************************************
    Callback registration from ObjC runtime and CoreFoundation
    ***************************************************************************/
    
    void _Block_use_RR2(const Block_callbacks_RR *callbacks) {
        _Block_retain_object = callbacks->retain;
        _Block_release_object = callbacks->release;
        _Block_destructInstance = callbacks->destructInstance;
    }
    
    /****************************************************************************
    Accessors for block descriptor fields
    *****************************************************************************/
    #if 0
    static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
    {
        return aBlock->descriptor;
    }
    #endif
    
    // 取得 block 中的 Block_descriptor_2,它藏在 descriptor 列表中
    // 调用者:_Block_call_copy_helper() / _Block_call_dispose_helper
    static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
    {
        // Block_descriptor_2 中存的是 copy/dispose 方法,如果没有指定有 copy / dispose 方法,则返回 NULL
        if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
        // 先取得 Block_descriptor_1 的地址
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        // 偏移 Block_descriptor_1 的大小,就是 Block_descriptor_2 的起始地址
        desc += sizeof(struct Block_descriptor_1);
        return (struct Block_descriptor_2 *)desc;
    }
    
    // 取得 block 中的 Block_descriptor_3,它藏在 descriptor 列表中
    // 调用者:_Block_extended_layout() / _Block_layout() / _Block_signature()
    static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
    {
        // Block_descriptor_3 中存的是 block 的签名,如果没有指定有签名,则直接返回 NULL
        if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
        // 先取得 Block_descriptor_1 的地址
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        // 先偏移 Block_descriptor_1 的大小
        desc += sizeof(struct Block_descriptor_1);
        // 如果还有 Block_descriptor_2,就再偏移 Block_descriptor_2 的大小,得到的就是 Block_descriptor_3 的地址
        if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
            desc += sizeof(struct Block_descriptor_2);
        }
        return (struct Block_descriptor_3 *)desc;
    }
    
    // 调用 block 的 copy helper 方法,即 Block_descriptor_2 中的 copy 方法
    static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
    {
        // 取得 block 中的 Block_descriptor_2
        struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
        // 如果没有 Block_descriptor_2,就直接返回
        if (!desc) return;
    
        // 调用 desc 中的 copy 方法,copy 方法中会调用 _Block_object_assign 函数
        (*desc->copy)(result, aBlock); // do fixup
    }
    
    // 调用 block 的 dispose helper 方法,即 Block_descriptor_2 中的 dispose 方法
    static void _Block_call_dispose_helper(struct Block_layout *aBlock)
    {
        // 取得 block 中的 Block_descriptor_2
        struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
        // 如果没有 Block_descriptor_2,就直接返回
        if (!desc) return;
    
        // 调用 desc 中的 dispose 方法,dispose 方法中会调用 _Block_object_dispose 函数
        (*desc->dispose)(aBlock);
    }
    
    /*******************************************************************************
    Internal Support routines for copying
    ********************************************************************************/
    
    #if !TARGET_OS_WIN32
    #pragma mark Copy/Release support
    #endif
    
    // Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.
    // 拷贝 block,
    // 如果原来就在堆上,就将引用计数加 1;
    // 如果原来在栈上,会拷贝到堆上,引用计数初始化为 1,并且会调用 copy helper 方法(如果存在的话);
    // 如果 block 在全局区,不用加引用计数,也不用拷贝,直接返回 block 本身
    // 参数 arg 就是 Block_layout 对象,
    // 返回值是拷贝后的 block 的地址
    // 运行?stack -》malloc
    void *_Block_copy(const void *arg) {
        struct Block_layout *aBlock;
    
        // 如果 arg 为 NULL,直接返回 NULL
    
        if (!arg) return NULL;
        
        // The following would be better done as a switch statement
        // 强转为 Block_layout 类型
        aBlock = (struct Block_layout *)arg;
        const char *signature = _Block_descriptor_3(aBlock)->signature;
        
        // 如果现在已经在堆上
        if (aBlock->flags & BLOCK_NEEDS_FREE) {
            // latches on high
            // 就只将引用计数加 1
            latching_incr_int(&aBlock->flags);
            return aBlock;
        }
        // 如果 block 在全局区,不用加引用计数,也不用拷贝,直接返回 block 本身
        else if (aBlock->flags & BLOCK_IS_GLOBAL) {
            return aBlock;
        }
        else {
            // Its a stack block.  Make a copy.
            // block 现在在栈上,现在需要将其拷贝到堆上
            // 在堆上重新开辟一块和 aBlock 相同大小的内存
            struct Block_layout *result =
                (struct Block_layout *)malloc(aBlock->descriptor->size);
            // 开辟失败,返回 NULL
            if (!result) return NULL;
            // 将 aBlock 内存上的数据全部复制新开辟的 result 上
            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
            // 将 flags 中的 BLOCK_REFCOUNT_MASK 和 BLOCK_DEALLOCATING 部分的位全部清为 0
            result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
            // 将 result 标记位在堆上,需要手动释放;并且引用计数初始化为 1
            result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
            // copy 方法中会调用做拷贝成员变量的工作
            _Block_call_copy_helper(result, aBlock);
            // Set isa last so memory analysis tools see a fully-initialized object.
            // isa 指向 _NSConcreteMallocBlock
            result->isa = _NSConcreteMallocBlock;
            return result;
        }
    }
    
    
    // Runtime entry points for maintaining the sharing knowledge of byref data blocks.
    
    // A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
    // Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
    // We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
    // Otherwise we need to copy it and update the stack forwarding pointer
    
    // 1. 如果 byref 原来在堆上,就将其拷贝到堆上,拷贝的包括 Block_byref、Block_byref_2、Block_byref_3,
    //    被 __weak 修饰的 byref 会被修改 isa 为 _NSConcreteWeakBlockVariable,
    //    原来 byref 的 forwarding 也会指向堆上的 byref;
    // 2. 如果 byref 已经在堆上,就只增加一个引用计数。
    // 参数 dest是一个二级指针,指向了目标指针,最终,目标指针会指向堆上的 byref
    static struct Block_byref *_Block_byref_copy(const void *arg) {
        // arg 强转为 Block_byref * 类型
        struct Block_byref *src = (struct Block_byref *)arg;
    
        // 引用计数等于 0
        if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
            // src points to stack
            // 为新的 byref 在堆中分配内存
            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
            // 新 byref 的 flags 中标记了它是在堆上,且引用计数为 2。
            // 为什么是 2 呢?注释说的是 non-GC one for caller, one for stack
            // one for caller 很好理解,那 one for stack 是为什么呢?
            // 看下面的代码中有一行 src->forwarding = copy。src 的 forwarding 也指向了 copy,相当于引用了 copy
            copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
            // 堆上 byref 的 forwarding 指向自己
            copy->forwarding = copy; // patch heap copy to point to itself
            // 原来栈上的 byref 的 forwarding 现在也指向堆上的 byref
            src->forwarding = copy;  // patch stack to point to heap copy
            // 拷贝 size
            copy->size = src->size;
    
            // 如果 src 有 copy/dispose helper
            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
                // 取得 src 和 copy 的 Block_byref_2
                struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
                struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
                // copy 的 copy/dispose helper 也与 src 保持一致
                // 因为是函数指针,估计也不是在栈上,所以不用担心被销毁
                copy2->byref_keep = src2->byref_keep;
                copy2->byref_destroy = src2->byref_destroy;
    
                // 如果 src 有扩展布局,也拷贝扩展布局
                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);
                    // 没有将 layout 字符串拷贝到堆上,是因为它是 const 常量,不在栈上
                    copy3->layout = src3->layout;
                }
                // 调用 copy helper,因为 src 和 copy 的 copy helper 是一样的,所以用谁的都行,调用的都是同一个函数
                (*src2->byref_keep)(copy, src);
            }
            else {
                // Bitwise copy.
                // This copy includes Block_byref_3, if any.
                // 如果 src 没有 copy/dispose helper
                // 将 Block_byref 后面的数据都拷贝到 copy 中,一定包括 Block_byref_3
                memmove(copy+1, src+1, src->size - sizeof(*src));
            }
        }
        // already copied to heap
        // src 已经在堆上,就只将引用计数加 1
        else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
            latching_incr_int(&src->forwarding->flags);
        }
        
        return src->forwarding;
    }
    
    // 对 byref 对象做 release 操作,
    // 堆上的 byref 需要 release,栈上的不需要 release,
    // release 就是引用计数减 1,如果引用计数减到了 0,就将 byref 对象销毁
    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 已经被堆拷贝,则取得是堆上的 byref,否则是栈上的,栈上的不需要 release,也没有引用计数
        byref = byref->forwarding;
        
        // byref 被拷贝到堆上,需要 release
        if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
            // 取得引用计数
            int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
            os_assert(refcount);
            // 引用计数减 1,如果引用计数减到了 0,会返回 true,表示 byref 需要被销毁
            if (latching_decr_int_should_deallocate(&byref->flags)) {
                // 如果 byref 有 dispose helper,就先调用它的 dispose helper
                if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                    struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
                    // dispose helper 藏在 Block_byref_2 里
                    (*byref2->byref_destroy)(byref);
                }
                free(byref);
            }
        }
    }
    
    
    /************************************************************
     *
     * API supporting SPI
     * _Block_copy, _Block_release, and (old) _Block_destroy
     *  API - Application Programming Interface
     * API 和 SPI 都是相对的概念,他们的差别只在语义上,API 直接被应用开发人员使用,SPI 被框架扩张人员使用
     ***********************************************************/
    
    #if !TARGET_OS_WIN32
    #pragma mark SPI/API
    #endif
    
    
    // API entry point to release a copied Block
    // 对 block 做 release 操作。
    // block 在堆上,才需要 release,在全局区和栈区都不需要 release.
    // 先将引用计数减 1,如果引用计数减到了 0,就将 block 销毁
    void _Block_release(const void *arg) {
        struct Block_layout *aBlock = (struct Block_layout *)arg;
        // 如果 block == nil
        if (!aBlock) return;
        // 如果 block 在全局区
        if (aBlock->flags & BLOCK_IS_GLOBAL) return;
        // block 不在堆上
        if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
    
        // 引用计数减 1,如果引用计数减到了 0,会返回 true,表示 block 需要被销毁
        if (latching_decr_int_should_deallocate(&aBlock->flags)) {
            // 调用 block 的 dispose helper,dispose helper 方法中会做诸如销毁 byref 等操作
            _Block_call_dispose_helper(aBlock);
            // _Block_destructInstance 啥也不干,函数体是空的
            _Block_destructInstance(aBlock);
            free(aBlock);
        }
    }
    
    // 尝试 retain block。当 block 不是处于 dealloc 时,引用计数加 1
    // 返回值是是否成功,只有在 block 处于 deallocating 时,才会失败
    bool _Block_tryRetain(const void *arg) {
        struct Block_layout *aBlock = (struct Block_layout *)arg;
        return latching_incr_int_not_deallocating(&aBlock->flags);
    }
    
    // 判断 block 是否处于 deallocating 状态
    bool _Block_isDeallocating(const void *arg) {
        struct Block_layout *aBlock = (struct Block_layout *)arg;
        return (aBlock->flags & BLOCK_DEALLOCATING) != 0;
    }
    
    
    /************************************************************
     *
     * SPI used by other layers
     *
     ***********************************************************/
    // 取得 block 的完整大小
    size_t Block_size(void *aBlock) {
        return ((struct Block_layout *)aBlock)->descriptor->size;
    }
    
    // 如果 block 的返回值在栈上,则返回 TRUE,反之返回 FALSE
    bool _Block_use_stret(void *aBlock) {
        struct Block_layout *layout = (struct Block_layout *)aBlock;
    
        // block 的 flag 有 BLOCK_HAS_SIGNATURE 和 BLOCK_USE_STRET,才会返回 TRUE
        int requiredFlags = BLOCK_HAS_SIGNATURE | BLOCK_USE_STRET;
        return (layout->flags & requiredFlags) == requiredFlags;
    }
    
    // Checks for a valid signature, not merely the BLOCK_HAS_SIGNATURE bit.
    // 判断 block 是否有签名,不判断 BLOCK_HAS_SIGNATURE,而是通过直接取签名字符串来确定存在与否
    bool _Block_has_signature(void *aBlock) {
        return _Block_signature(aBlock) ? true : false;
    }
    
    // 取得 block 的签名字符串,可能是 NULL
    const char * _Block_signature(void *aBlock)
    {
        struct Block_layout *layout = (struct Block_layout *)aBlock;
        struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
        // 如果没有 desc3,则一定没有签名,返回 NULL
        if (!desc3) return NULL;
    
        return desc3->signature;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451

    block 归纳总结

    1. 如果block已经在堆上计数+1,返回block。
    2. 如果block在全局flags & BLOCK_IS_GLOBAL直接返回block本身。
    3. 如果block在栈上,需要拷贝block到堆上(重新开辟一个内存地址)result上,最后result->isa = _NSConcreteMallocBlock;指向了_NSConcreteMallocBlock 堆类型。

    堆block,栈block,全局block对于这三种类型的block遵循俩个原则:

    1. block如果没有使⽤外部变量,或者只使⽤静态变量和全局变量,那⼀定是全局blcok。
    2. block如果使⽤了外部变量,⽽且不是静态变量或全局变量,如果赋值给强引⽤的是堆block,如果赋值给弱引⽤的是栈blcok。(所以栈blcok,可以接收外部参数)
  • 相关阅读:
    Elk-Metricbeat配置Tomcat的日志分析 (Metricbeat-part3)
    vue列表跳转详情,记录列表滚动不变
    mysql面试题7:MySQL事务原理是什么?MySQL事务的隔离级别有哪些?
    (动态)树分治 学习笔记
    【全方位带你配置yolo开发环境】快速上手yolov5
    【SAP ME 26】SAP ME创建开发组件(DC)mobile
    OpenStack-train版安装之环境准备
    [机缘参悟-77]:深度思考-职场中注意事项-管理者版
    Flask基础学习笔记
    前后端验证码交互完整流程
  • 原文地址:https://blog.csdn.net/lingjunjie/article/details/125460252