ブロック探索

9270 ワード

私たちが使用するblockは、静的block(外部変数は一切使用していません)、スタックblock(外部一時変数を使用します)、スタックblock(外部メンバ変数または属性を使用します)の3種類に分けられます.
  • 静的block
  • 以下はテストのソースコードです.
    @interface MBlockObj : NSObject
    
    @end
    
    #import "MBlockObj.h"
    
    @implementation MBlockObj
    
    - (void)testMBlock {
        
        void (^blockM)(void) = ^{
            
            int i = 0;
            i ++;
            
        };
        blockM();
        
    }
    
    @end
    端末でclang-rewrite-objc MBlockObj.mコマンドを実行した後、余分なコードを削除して、下記の部分をもらいました.
    struct __MBlockObj__testMBlock_block_impl_0 {
      struct __block_impl impl;
      struct __MBlockObj__testMBlock_block_desc_0* Desc;
      __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) {
    
            int i = 0;
            i ++;
    
    }
    
    static struct __MBlockObj__testMBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)};
    
    static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {
    
        void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);
    
    }
    私たちのblockMが変更されているのが見えます.
    ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA));
    同前MBlockObj__utest MBロックblockimpl_0は二つのパラメータが必要です. __MBlockObj__utest MBロックblockfunc0は主に方法の実現内容であり、2番目のパラメータはブロックに関するいくつかの情報記述である.
    添付:テストコードにOCオブジェクトを使ってテストした結果も同様です.
    2.スタックblock
    テストコード
    - (void)testMBlock {
        
        int i = 0;
        
        void (^blockM)(void) = ^{
            
            printf("%d", i);
            
        };
        blockM();
        
    }
    clangから出るコードはこうです.
    struct __MBlockObj__testMBlock_block_impl_0 {
      struct __block_impl impl;
      struct __MBlockObj__testMBlock_block_desc_0* Desc;
      int i;
      __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
    {
      int i = __cself->i; // bound by copy
    
        printf("%d", i);
    
    }
    
    static struct __MBlockObj__testMBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)};
    
    static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {
    
        int i = 0;
    
        void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, i));
        ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);
    
    }
    一時変数iにアクセスすることにより、構造体が変化し、対応するメンバー変数が多くなり、テストコードが実行するジョブはiを印刷し、_u_uMBlockObj__utest MBロックblockfunc0のprintfプリントのiは、実は一回の値転送をしました.
    付:臨時変数をOCオブジェクトに置き換えると、NSArayなど、clangから出てくるコードが変わります.
    struct __MBlockObj__testMBlock_block_impl_0 {
      struct __block_impl impl;
      struct __MBlockObj__testMBlock_block_desc_0* Desc;
      NSArray *mArr;
      __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, NSArray *_mArr, int flags=0) : mArr(_mArr) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
    {
      NSArray *mArr = __cself->mArr; // bound by copy
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_6efbe8_mi_0, mArr);
        
    }
    static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src)
    {
        _Block_object_assign((void*)&dst->mArr, (void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    
    static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src)
    {
        _Block_object_dispose((void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    
    static struct __MBlockObj__testMBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*);
      void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*);
    } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0};
    
    static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {
    
        NSArray* mArr = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
    
        void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, mArr, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);
    
    }
    ここは主に多くなりました.MBlockObj__utest MBロックblockcopy_0と_uMBlockObj__utest MBロックblockディspose0の二つの方法
    前者の方法名にはCopyがありますが、copyのような操作が行われているとは思わないでください.この方法の具体的な実現は、_を呼び出しました.Block_objectassignメソッドBlock_objectassignメソッドは、伝わった実際のパラメータに基づいて、blockオブジェクトか、OCオブジェクトかを判断します.OCオブジェクトであれば、retainを行い、値を賦課します.
    後者の方法は,解析関数的役割であるが,実際のパラメータタイプも同様に判断し,OCオブジェクトであればreleaseを行う.
    3.ブロックブロック
    テストコード
    @interface MBlockObj : NSObject
    
    @property (nonatomic, strong) NSArray* mArr;
    
    @end
    
    #import "MBlockObj.h"
    
    @implementation MBlockObj
    
    - (void)testMBlock {
        
        _mArr = [NSArray array];
        
        void (^blockM)(void) = ^{
            
            NSLog(@"mArr %@", _mArr);
            
        };
        blockM();
        
    }
    
    @end
    clangからのコード
    struct __MBlockObj__testMBlock_block_impl_0 {
      struct __block_impl impl;
      struct __MBlockObj__testMBlock_block_desc_0* Desc;
      MBlockObj *self;
      __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, MBlockObj *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself)
    {
      MBlockObj *self = __cself->self; // bound by copy
    
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_58d1bf_mi_0, (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr)));
    
    }
    static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src)
    {
        _Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    
    static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src)
    {
        _Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    
    static struct __MBlockObj__testMBlock_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*);
      void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*);
    } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0};
    
    static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) {
    
        (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr)) = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
    
        void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, self, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM);
    
    }
    はい、上のコードがわかりました.メンバー変数にアクセスしても、SElfを通じてメンバー変数にアクセスします.上の説明があります.Block_objectassignは実際のパラメータがretain selfとなり、場合によっては循環参照を引き起こすことがあります.
    付1:_u ublockはどんな操作をしましたか?
    皆さんはテストコードを作って、clangしてください.blockで修飾された変数は構造体に変化します.具体的な役割は、この変数をスタックからスタックにコピーすることです.
    付2:なぜクラスの属性やメンバー変数が直接に変更されるのか、臨時変数は_u_u uとして宣言する必要があります.blockは修正できますか?
    添付1の問題の解釈において、臨時変数は声明でブロック後、スタックからスタックにコピーし、クラスの属性やメンバー変数自体がヒープ上にあるか、またはそれぞれの作用領域が異なると理解することができます.
    付3:これはblock実現過程のソースコードです. https://github.com/mackyle/blocksruntime
    付4:blockは、その保有する変数をリリースするための構文関数を呼び出します.
    blockは、その保有する変数を構文関数を呼び出して、blockがnilに設定されたり、releaseされたりするときです.したがって、一時的なblock(テストコードに見られるblock)であれば、メソッド呼び出しが終了したとき、blockとその保有変数をリリースします.blockは属性として、nilをアクティブにしない限り、その所在クラスで呼び出されます. deallocの時リリースします.