美文网首页
iOS - block - 序

iOS - block - 序

作者: felix6 | 来源:发表于2019-04-12 17:13 被阅读0次

    [toc]

    参考

    block

    https://blog.csdn.net/u014600626/article/details/78697535

    http://www.cocoachina.com/cms/wap.php?action=article&id=23147

    https://www.jianshu.com/p/d96d27819679

    block 简介

    block 是能够捕获外部变量的匿名函数, 在 iOS4 中引入, 是对C语言的扩充, C语言本身不存在这样的匿名函数。

    block 本质

    block 本质上是封装了 函数调用(函数指针) 以及 函数调用环境(捕获到的参数) 的 OC对象。既然是OC对象, 那就是一个内部有isa指针的结构体。

    block 属性策略

    MRC下block属性的建议写法

    @property (copy, nonatomic) void (^block)(void);
    

    ARC下block属性的建议写法

    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);
    

    代码存放在堆区时, 就需要特别注意, 因为堆区不像代码区不变化, 堆区是不断变化的(不断创建销毁)。因此当没有强指针指向时, 代码有可能会被销毁, 如果这时再访问此段代码则会程序崩溃。
    因此, 对于这种情况, 我们在定义一个block属性时应指定为strong, 或copy:

    @property (nonatomic, strong) void (^myBlock)(void); // 这样就有强指针指向它
    @property (nonatomic, copy)  void (^myBlock)(void); 
    

    block 相关源码

    Block.h
    BLOCK_EXPORT void *_Block_copy(const void *aBlock);
    
    BLOCK_EXPORT void _Block_release(const void *aBlock);
    
    #define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
    
    #define Block_release(...) _Block_release((const void *)(__VA_ARGS__))
    
    Block_private.h
    // (非完整)
    enum {
        /* See function implementation for a more complete description of these fields and combinations */
        BLOCK_FIELD_IS_OBJECT   =  3,  /* id, NSObject, __attribute__((NSObject)), block, ... */
        BLOCK_FIELD_IS_BLOCK    =  7,  /* a block variable */
        BLOCK_FIELD_IS_BYREF    =  8,  /* the on stack structure holding the __block variable */
        BLOCK_FIELD_IS_WEAK     = 16,  /* declared __weak, only used in byref copy helpers */
        BLOCK_BYREF_CALLER      = 128  /* called from __block (byref) copy/dispose support routines. */
    };
    
    /* Runtime entry point called by compiler when assigning objects inside copy helper routines */
    BLOCK_EXPORT void _Block_object_assign(void *destAddr, const void *object, const int flags);
        /* BLOCK_FIELD_IS_BYREF is only used from within block copy helpers */
    
    /* runtime entry point called by the compiler when disposing of objects inside dispose helper routine */
    BLOCK_EXPORT void _Block_object_dispose(const void *object, const int flags);
    
    runtime.c
    /* Copy, or bump refcount, of a block.  If really copying, call the copy helper if present. */
    static void *_Block_copy_internal(const void *arg, const int flags) {
        struct Block_layout *aBlock;
        const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
    
        //printf("_Block_copy_internal(%p, %x)\n", arg, flags); 
        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_GC) {
            // GC refcounting is expensive so do most refcounting here.
            if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) {
                // Tell collector to hang on this - it will bump the GC refcount version
                _Block_setHasRefcount(aBlock, true);
            }
            return aBlock;
        }
        else if (aBlock->flags & BLOCK_IS_GLOBAL) {
            return aBlock;
        }
    
        // 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;
        }
        else {
            // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne
            // This allows the copy helper routines to make non-refcounted block copies under GC
            unsigned long int flags = aBlock->flags;
            bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0;
            struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR);
            if (!result) return (void *)0;
            memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
            // reset refcount
            // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE.
            flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK);   // XXX not needed
            if (wantsOne)
                flags |= BLOCK_IS_GC | 1;
            else
                flags |= BLOCK_IS_GC;
            result->flags = flags;
            if (flags & BLOCK_HAS_COPY_DISPOSE) {
                //printf("calling block copy helper...\n");
                (*aBlock->descriptor->copy)(result, aBlock); // do fixup
            }
            if (hasCTOR) {
                result->isa = _NSConcreteFinalizingBlock;
            }
            else {
                result->isa = _NSConcreteAutoBlock;
            }
            return result;
        }
    }
    
    // API entry point to release a copied Block
    void _Block_release(void *arg) {
        struct Block_layout *aBlock = (struct Block_layout *)arg;
        int32_t newCount;
        if (!aBlock) return;
        newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;
        if (newCount > 0) return;
        // Hit zero
        if (aBlock->flags & BLOCK_IS_GC) {
            // Tell GC we no longer have our own refcounts.  GC will decr its refcount
            // and unless someone has done a CFRetain or marked it uncollectable it will
            // now be subject to GC reclamation.
            _Block_setHasRefcount(aBlock, false);
        }
        else if (aBlock->flags & BLOCK_NEEDS_FREE) {
            if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);
            _Block_deallocator(aBlock);
        }
        else if (aBlock->flags & BLOCK_IS_GLOBAL) {
            ;
        }
        else {
            printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock);
        }
    }
    

    block 相关 struct

    block 在 clang编译后的结构体:
    struct __block_impl {
        void *isa; // 指向block的具体类型
        int Flags;
        int Reserved;
        void *FuncPtr; // 函数指针 FunctionPointer, 指向block的实现 __funcName_block_func_0
    };
    
    block 在 Block_private.h 中的声明:
    struct Block_layout {
        void *isa;
        int flags;
        int reserved; 
        void (*invoke)(void *, ...);
        struct Block_descriptor *descriptor;
        /* Imported variables. */
    };
    
    struct Block_descriptor {
        unsigned long int reserved;
        unsigned long int size;
        void (*copy)(void *dst, void *src);
        void (*dispose)(void *);
    };
    

    相关文章

      网友评论

          本文标题:iOS - block - 序

          本文链接:https://www.haomeiwen.com/subject/syqhwqtx.html