[iOS]Blockシリーズ探究二-捕獲変数

8733 ワード

前回の記事ではOCのblockが実はCの関数ポインタであることを知りましたが,この記事ではblockと局所変数の様々な化学反応を研究します.
一、変数のキャプチャ
主に、グローバル変数、静的グローバル変数、レイアウト変数、静的ローカル変数の3つの変数をキャプチャします.コードは次のとおりです.
/**      */
int global_count = 10;
/**        */
static int static_global_count = 10;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //   block
        void (^block)(void);
        //     
        int count = 10;
        //       
        static int static_count = 10;
        // block   
        block = ^{
            global_count = 11;
            static_global_count = 11;
            NSLog(@"count = %@", @(count));
            static_count = 11;
        };
        // block   
        block();
        //     
        global_count = 12;
        static_global_count = 12;
        count = 12;
        static_count = 12;
    }
    return 0;
}

上のコード、私たちはblockにパラメータcountを伝えて、今私たちはclangして、下のC/C++コードを得ます:
// __block_imp   
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

int global_count = 10;

static int static_global_count = 10;

// __main_block_impl_0   
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int count;
  int *static_count;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _count, int *_static_count, int flags=0) : count(_count), static_count(_static_count) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// __main_block_func_0  
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int count = __cself->count; // bound by copy
  int *static_count = __cself->static_count; // bound by copy

            global_count = 11;
            static_global_count = 11;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_23de66_mi_0, ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (int)(count)));
            (*static_count) = 11;
        }

// __main_block_desc_0   
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

// main  
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*block)(void);

        int count = 10;

        static int static_count = 10;

        block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, count, &static_count));

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

        global_count = 12;
        static_global_count = 12;
        count = 12;
        static_count = 12;
    }
    return 0;
}
__main_block_impl_0という構造体を重点的に見てみましょう.
// __main_block_impl_0   
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int count;
  int *static_count;
  
  /** 
          :                 count   
  int count = _count; 
  */
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _count, int *_static_count, int flags=0) : count(_count), static_count(_static_count) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
__main_block_impl_0構造体には2つのメンバー変数countstatic_countが多く見られる.blockが宣言されると、countの値は__main_block_impl_0構造体の内部に保存されている.ここでは値伝達、すなわち呼び出し前にcountに再付与され、__main_block_impl_0構造体の内部のcountメンバー変数の値には影響しないことに注意する.同様に、blockは、静的変数static_countのポインタが__main_block_impl_0構造体内部に保存され、参照伝達であるため、static_countに対するblock内部の修正はstatic_countの値に直接影響する.グローバル変数global_countおよび静的グローバル変数static_global_countは、役割ドメインがグローバルであるため、block内部の変更がそれらの値に直接影響する.
二、_ブロック説明子
blockは外部の局所変数の値を修正できないことはよく知られていますが、__blockは外部の局所変数の値を修正することができます.次に原因を探ってみましょう.まず、上OCコード:
//   block
void (^block)(void);
__block int count = 10;
// block   
block = ^{
    count = 11;
};
// block   
block();
//  count    
count = 12;

clangをクリックして、次のコードを得ます.
// __block_impl   
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

// __Block_byref_count_0   
struct __Block_byref_count_0 {
  void *__isa;
__Block_byref_count_0 *__forwarding;
 int __flags;
 int __size;
 int count;
};

// __main_block_impl_0   
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_count_0 *count; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_count_0 *_count, int flags=0) : count(_count->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// __main_block_func_0  
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_count_0 *count = __cself->count; // bound by ref
    (count->__forwarding->count) = 11;
}

// __main_block_copy_0  
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->count, (void*)src->count, 8/*BLOCK_FIELD_IS_BYREF*/);}

// __main_block_dispose_0  
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->count, 8/*BLOCK_FIELD_IS_BYREF*/);}

// __main_block_desc_0   
static struct __main_block_desc_0 {
  size_t reserved;
  size_t 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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*block)(void);
        __attribute__((__blocks__(byref))) __Block_byref_count_0 count = {(void*)0,(__Block_byref_count_0 *)&count, 0, sizeof(__Block_byref_count_0), 10};

        block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_count_0 *)&count, 570425344));

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        
        (count.__forwarding->count) = 12;
    }
    return 0;
}
__main_block_desc_0構造体はメンバー変数copyおよびdisposeを多くし,さらに__Block_byref_count_0構造体,__main_block_copy_0関数および__main_block_dispose_0関数のいくつかの関数および構造体も多くなることを見出した.
2.1、__Block_byref_count_0構造体
まず__Block_byref_count_0という構造体を見てみましょう.コードは以下の通りです.
// __Block_byref_count_0   
struct __Block_byref_count_0 {
  void *__isa;
__Block_byref_count_0 *__forwarding;
 int __flags;
 int __size;
 int count 
};

この構造体は_block変数のパッケージは、OCのオブジェクトに相当します.main()関数の構造体の初期化関数を見てみましょう.
 __attribute__((__blocks__(byref))) __Block_byref_count_0 count = {(void*)0,(__Block_byref_count_0 *)&count, 0, sizeof(__Block_byref_count_0), 10};

構造体の最後のメンバー変数は元のローカル変数に相当し,メンバー変数名と初期化関数からも分かる.具体的に見てみましょうblock変数の付与:
// block   
block = ^{
    count = 11;
};

このコードの変換は次のとおりです.
// __main_block_func_0  
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_count_0 *count = __cself->count; // bound by ref
    (count->__forwarding->count) = 11;
}
__main_block_impl_0構造体の例は、指向性_を有するblock変数の__Block_byref_count_0構造体の例のポインタ.__Block_byref_count_0構造体インスタンスのメンバー変数__forwardingは、インスタンス自体を指すポインタを有する.メンバー変数__forwardingを使用して、メンバー変数countにアクセスします.メンバー変数countは、インスタンス自体が持つ変数であり、ローカル変数に相当する.__forwarding変数、__main_block_copy_0関数と__main_block_dispose_0関数の存在意義は次の文章で説明します.Block で探究する.
三、まとめ
ブロックキャプチャ変数には、次のような状況があります.
  • 変数がグローバル変数と静的グローバル変数である場合、blockは追加の処理がなく、直接使用する.
  • 変数がローカル変数の場合、blockを宣言する前の変数の値のみがキャプチャされ、block内部ではその変数の値は変更できません.
  • 変数が静的局所変数である場合、その変数のポインタをキャプチャし、block内部でその変数の値を変更することができる.
  • 変数は被_block修飾された局所変数は、構造体(OCオブジェクト)としてパッケージ化されたものであり、この構造体の最後のメンバー変数は__block修飾された局所変数であり、blockはその構造体のポインタをキャプチャし、block内部でその変数の値を修正することができる.