[iOS]Blockシリーズ探究二-捕獲変数
8733 ワード
前回の記事ではOCのblockが実はCの関数ポインタであることを知りましたが,この記事ではblockと局所変数の様々な化学反応を研究します.
一、変数のキャプチャ
主に、グローバル変数、静的グローバル変数、レイアウト変数、静的ローカル変数の3つの変数をキャプチャします.コードは次のとおりです.
上のコード、私たちはblockにパラメータcountを伝えて、今私たちはclangして、下のC/C++コードを得ます:
二、_ブロック説明子
blockは外部の局所変数の値を修正できないことはよく知られていますが、
clangをクリックして、次のコードを得ます.
2.1、
まず
この構造体は_block変数のパッケージは、OCのオブジェクトに相当します.
構造体の最後のメンバー変数は元のローカル変数に相当し,メンバー変数名と初期化関数からも分かる.具体的に見てみましょうblock変数の付与:
このコードの変換は次のとおりです.
三、まとめ
ブロックキャプチャ変数には、次のような状況があります.変数がグローバル変数と静的グローバル変数である場合、blockは追加の処理がなく、直接使用する. 変数がローカル変数の場合、blockを宣言する前の変数の値のみがキャプチャされ、block内部ではその変数の値は変更できません. 変数が静的局所変数である場合、その変数のポインタをキャプチャし、block内部でその変数の値を変更することができる. 変数は被_block修飾された局所変数は、構造体(OCオブジェクト)としてパッケージ化されたものであり、この構造体の最後のメンバー変数は__block修飾された局所変数であり、blockはその構造体のポインタをキャプチャし、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つのメンバー変数count
とstatic_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
で探究する.三、まとめ
ブロックキャプチャ変数には、次のような状況があります.