zoukankan      html  css  js  c++  java
  • ios之Block研究

    Block的好处,我总结了下主要有2点:1.用于回调特别方便,2.可以延长对象的作用区域。但是,Block的内存管理这个模块一直不是很清楚,这个周末好好的看了下Block的原理,有些许心得。

    为了性能,默认Block都是分配在stack上面的,所以它的作用区域就是当前函数。

    #include <stdio.h>
    
    int main()
    {
        int i = 1024;
        void (^blk)(void) = ^ {
            printf("%d
    ", i);
        };
        blk();
        return 0;
    }

     在blk这个block里面是不能修改i的。Why?我们可以通过clang看看编译器处理后的这块代码

    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 void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int i = __cself->i; // bound by copy
    
            printf("%d
    ", i);
        }
    
    static struct __main_block_desc_0 {
      unsigned long reserved;
      unsigned long Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    int main()
    {
        int i = 1024;
        void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i);
        ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
        return 0;
    }
    struct __block_impl是Block的一个内部结构体,原型是
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };

    每个block都有个默认的构造函数

    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) 所以只能读取i,而不能修改i,当你试图修改它时,编译器就在预处理阶段直接报错。

    只要在i前加__Block变量就可以在Block里面修改i值了,此时由值类型变为引用类型

    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 void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int *i = __cself->i; // bound by copy
    
            printf("%d
    ", (*i));
        }
    
    static struct __main_block_desc_0 {
      unsigned long reserved;
      unsigned long Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    int main()
    {
        static int i = 1024;
        void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i);
        ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
        return 0;
    }

    上面的代码块是将int i的类型修改为__Block int i = 1024;后编译器生成代码块,可以看到__main_block_impl_0中的 i类型已经改变为int *,所以我们可以修改它的值。

    所以只要没对Block进行copy操作,它一直存在stack里面。不管是否有__block修饰符

    要想延长Block的作于域,我们可以对它进行copy操作,apple提供的接口是Block_Copy()方法

    /* 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)
    ", 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)...
    ", 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...
    ");
                (*aBlock->descriptor->copy)(result, aBlock); // do fixup
            }
            if (hasCTOR) {
                result->isa = _NSConcreteFinalizingBlock;
            }
            else {
                result->isa = _NSConcreteAutoBlock;
            }
            return result;
        }
    }
    View Code

    通过观察apple提供的block源码,我们可以看到copy方法将block从statck拷贝到heap里面,所以它的作用区域延长了

    待完成:1.block与oc的混合

        2.__block修饰符与oc的混合

    总结

    1.block默认都是分配在stack,当copy后,它移到heap里

    2.block中的变量默认是不能修改的,只有添加__Block修饰符后才能修改

    3.block中有oc对象时,会_Block_retain_object(object),直到block销毁后才会_Block_release_object(object);

    4.对block进行copy时

    • If you access an instance variable by reference, a strong reference is made to self;

    • If you access an instance variable by value, a strong reference is made to the variable.

    它会将self进行copy,此时改对象的dealloc方法不会执行(因为它的引用计数归0),解决此问题有2种方法:在block执行完成后面立即Block_Release(),或者将改变量声明为__Block类型(Why?)

    继续补充block的oc的混合

    //
    //  main.m
    //  block
    //
    //  Created by lijian on 13-8-9.
    //  Copyright (c) 2013年 YOUKU. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
        
        NSMutableString *str = [NSMutableString stringWithFormat:@"lijian"];
        
        void (^blk)(void) = ^ {
            NSLog(@"%@", str);
        };
        blk();
        
        return 0;
    }
    View Code

    编译器生成代码为

    //
    //  main.m
    //  block
    //
    //  Created by lijian on 13-8-9.
    //  Copyright (c) 2013年 lijian. All rights reserved.
    //
    
    #include <Foundation/Foundation.h>
    
    int main(int, const char **);
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      NSMutableString *str;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableString *_str, int flags=0) : str(_str) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      NSMutableString *str = __cself->str; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl_main_m_1, str);
        }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __main_block_desc_0 {
      unsigned long reserved;
      unsigned long 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 (int argc, const char * argv[]) {
        
        NSMutableString *str = ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main_m_0);
        
        void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344);
        ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
        
        return 0;
    }
    View Code

    blk的原型为

        void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, str, 570425344);

    其中570425344 = BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR;

    通过构造函数,我们看到仍然是值传递,所以blk中是能不修改str的。

    至于上面的__main_block_copy_0和__main_block_dispose_0 就是用于Block_Copy()和Block_Release();

    当我将str的类型修改为__block NSMutableString时,生成如下代码

    int main(int, const char **);
    struct __Block_byref_str_0 {
      void *__isa;
    __Block_byref_str_0 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     NSMutableString *str;
    };
    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);
    }
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_str_0 *str; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_str_0 *_str, int flags=0) : str(_str->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_str_0 *str = __cself->str; // bound by ref
    
            (str->__forwarding->str) = ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main2_m_1);
            NSLog((NSString *)&__NSConstantStringImpl_main2_m_2, (str->__forwarding->str));
        }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_0 {
      unsigned long reserved;
      unsigned long 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 (int argc, const char * argv[]) {
        
        __block __Block_byref_str_0 str = {(void*)0,(__Block_byref_str_0 *)&str, 33554432, sizeof(__Block_byref_str_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((id (*)(id, SEL, NSString *, ...))(void *)objc_msgSend)(objc_getClass("NSMutableString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl_main2_m_0)};
        
        void (*blk)(void) = (void (*)(void))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (struct __Block_byref_str_0 *)&str, 570425344);
        ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
        
        return 0;
    }
    View Code

    可以看到str的类型实际上是__Block_byref_str_0,其中33554432 = BLOCK_HAS_COPY_DISPOSE = (1 << 25)

    而blk的构造函数中传递的是__Block_byref_str_0类型的指针,所以我们能在blk中修改str

  • 相关阅读:
    linux下activemq安装与配置
    Linux设置开放一个端口
    使用codis-admin搭建codis集群
    elasticsearch7.0安装及配置优化
    ELK详细安装部署
    filebeat安装部署
    ElasticSearch-5.3.1集群环境搭建,安装ElasticSearch-head插件,安装错误解决
    手把手教你搭建一个 Elasticsearch 集群
    ES 集群管理(集群规划、集群搭建、集群管理)
    Elasticsearch如何关掉服务
  • 原文地址:https://www.cnblogs.com/wustlj/p/3252152.html
Copyright © 2011-2022 走看看