Objective-Cのブロックデバイス

画像

Objective-Cには、ブロックのようなものがあります。これは、 クロージャーの概念の実装です。



ブロックを正しく使用する方法(コピーを呼び出すタイミング、保持ループを取り除く方法など)については多くの記事がありますが、ブロックデバイスは通常影響を受けません。 実際、このギャップを埋めましょう。



ツール



誰もが知っているわけではありませんが、clingには、コードをObjective-CからC ++に変換する-rewrite-objcオプションがあります。 Obective-Cに加えて、Objective-C ++もサポートされているため、CではなくC ++です。



次のように使用されます。

clang -rewrite-objc -ObjC main.m -o out.cpp
      
      





実際には、このオプションを使用してブロックが何になるかを理解し、コンパイラーの出力で受信したアセンブラーコードの分析を取り除くことが考えられます。



コードを変換する



したがって、次のコードを検討してください。

 #import <Foundation/Foundation.h> typedef int (^blk_t)(int intVar); int main(int argc, const char * argv[]) { __block NSString *blockString; NSString *string; blk_t blk = ^(int intVar) { blockString = @"Hello"; NSLog(@"%@", string); return intVar; }; blk(0); return 0; }
      
      







変換後、コードは次のようになります(重要でない部分は省略されます)。

 #ifndef BLOCK_IMPL #define BLOCK_IMPL struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // Runtime copy/destroy helper functions (from Block_private.h) #ifdef __OBJC_EXPORT_BLOCKS extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int); extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; #else __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int); __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int); __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32]; __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32]; #endif #endif #define __block #define __weak typedef int (*blk_t)(int intVar); struct __Block_byref_blockString_0 { void *__isa; __Block_byref_blockString_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); NSString *blockString; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSString *string; __Block_byref_blockString_0 *blockString; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_string, __Block_byref_blockString_0 *_blockString, int flags=0) : string(_string), blockString(_blockString->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static int __main_block_func_0(struct __main_block_impl_0 *__cself, int intVar) { __Block_byref_blockString_0 *blockString = __cself->blockString; // bound by ref NSString *string = __cself->string; // bound by copy (blockString->__forwarding->blockString) = (NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_0; NSLog((NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_1, string); return intVar; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->blockString, (void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->string, (void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} 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[]) { __attribute__((__blocks__(byref))) __Block_byref_blockString_0 blockString = {(void*)0,(__Block_byref_blockString_0 *)&blockString, 33554432, sizeof(__Block_byref_blockString_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131}; ; NSString *string; blk_t blk = (int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, string, (__Block_byref_blockString_0 *)&blockString, 570425344); ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 0); return 0; }
      
      



、構造体__main_block_impl_0 * SRC){_Block_object_assign((ボイド*)&dst-> blockString、(ボイド*)src-> blockString、 #ifndef BLOCK_IMPL #define BLOCK_IMPL struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // Runtime copy/destroy helper functions (from Block_private.h) #ifdef __OBJC_EXPORT_BLOCKS extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int); extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; #else __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int); __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int); __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32]; __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32]; #endif #endif #define __block #define __weak typedef int (*blk_t)(int intVar); struct __Block_byref_blockString_0 { void *__isa; __Block_byref_blockString_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); NSString *blockString; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSString *string; __Block_byref_blockString_0 *blockString; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_string, __Block_byref_blockString_0 *_blockString, int flags=0) : string(_string), blockString(_blockString->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static int __main_block_func_0(struct __main_block_impl_0 *__cself, int intVar) { __Block_byref_blockString_0 *blockString = __cself->blockString; // bound by ref NSString *string = __cself->string; // bound by copy (blockString->__forwarding->blockString) = (NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_0; NSLog((NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_1, string); return intVar; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->blockString, (void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->string, (void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} 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[]) { __attribute__((__blocks__(byref))) __Block_byref_blockString_0 blockString = {(void*)0,(__Block_byref_blockString_0 *)&blockString, 33554432, sizeof(__Block_byref_blockString_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131}; ; NSString *string; blk_t blk = (int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, string, (__Block_byref_blockString_0 *)&blockString, 570425344); ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 0); return 0; }



Block_object_assign((ボイド*)&DST #ifndef BLOCK_IMPL #define BLOCK_IMPL struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; // Runtime copy/destroy helper functions (from Block_private.h) #ifdef __OBJC_EXPORT_BLOCKS extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int); extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int); extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32]; extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32]; #else __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int); __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int); __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32]; __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32]; #endif #endif #define __block #define __weak typedef int (*blk_t)(int intVar); struct __Block_byref_blockString_0 { void *__isa; __Block_byref_blockString_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); NSString *blockString; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSString *string; __Block_byref_blockString_0 *blockString; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_string, __Block_byref_blockString_0 *_blockString, int flags=0) : string(_string), blockString(_blockString->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static int __main_block_func_0(struct __main_block_impl_0 *__cself, int intVar) { __Block_byref_blockString_0 *blockString = __cself->blockString; // bound by ref NSString *string = __cself->string; // bound by copy (blockString->__forwarding->blockString) = (NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_0; NSLog((NSString *)&__NSConstantStringImpl__var_folders_v1_v8wjvjd96vl0nhqsjtxs_c2h0000gn_T_main_1cea37_mi_1, string); return intVar; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->blockString, (void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->string, (void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->blockString, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->string, 3/*BLOCK_FIELD_IS_OBJECT*/);} 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[]) { __attribute__((__blocks__(byref))) __Block_byref_blockString_0 blockString = {(void*)0,(__Block_byref_blockString_0 *)&blockString, 33554432, sizeof(__Block_byref_blockString_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131}; ; NSString *string; blk_t blk = (int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, string, (__Block_byref_blockString_0 *)&blockString, 570425344); ((int (*)(__block_impl *, int))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, 0); return 0; }







ブロックとは



一見、C ++コードはわかりにくいように見えますが、順番に見てみましょう。



ブロック作成:

 blk_t blk = (int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, string, (__Block_byref_blockString_0 *)&blockString, 570425344);
      
      





この行は、デフォルトのコンストラクターを呼び出して構造を作成し(はい、C ++ではそのようなことがあります)、ブロックを格納する変数に割り当てます。 したがって、ブロックは構造体であることがわかります。



この構造を見てみましょう。

 struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSString *string; __Block_byref_blockString_0 *blockString; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_string, __Block_byref_blockString_0 *_blockString, int flags=0) : string(_string), blockString(_blockString->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
      
      





ご覧のとおり、__ block_impl構造はブロックの基礎です。 そして、すべてのObjective-Cプログラマーに馴染みのあるisaフィールドが含まれています。

念のため、各Objective-Cオブジェクトにはisa属性(このオブジェクトのクラスオブジェクトへのポインター)が含まれています。



Objective-Cのオブジェクトのベースは次のようになります。

 typedef struct objc_class *Class; struct objc_object { Class isa ; };
      
      





したがって、予期しない結論に達します-ブロックはクラスです!

ただし、ここで予期しないことは、MRCブロックの数日中に定期的にコピーを送信する必要があった場合です。

 - (void)someMethod { return [^() { ... } copy]; }
      
      





__main_block_impl_0構造体のコンストラクターを見ると、ブロック構造体のフィールドが作成時にどのように初期化されるかがわかります。

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *_string, __Block_byref_blockString_0 *_blockString, int flags=0) : string(_string), blockString(_blockString->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }
      
      





つまり、この場合、ブロックは_NSConcreteStackBlock型のオブジェクトであることがわかります。

FuncPtr-ブロックの本体であるC関数への参照を格納します

フラグ-ゼロ

desc-__main_block_impl_0構造体のサイズとブロックをコピーするための関数に関する情報が含まれています



変数



ブロックの標準フィールドに加えて、__ main_block_impl_0構造体は、ブロックによってキャプチャされた変数を格納します

  NSString *string; __Block_byref_blockString_0 *blockString;
      
      







ご覧のとおり、__ block修飾子でキャプチャされた変数は__Block_byref_blockString_0構造にラップされています

 struct __Block_byref_blockString_0 { void *__isa; __Block_byref_blockString_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); NSString *blockString; };
      
      





初期化は次のようになります。

 __attribute__((__blocks__(byref))) __Block_byref_blockString_0 blockString = {(void*)0,(__Block_byref_blockString_0 *)&blockString, 33554432, sizeof(__Block_byref_blockString_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131}; ;
      
      





つまり、元のオブジェクトがblockStringフィールドに格納されていることがわかります。 そして、__ forwardingフィールドはそれ自体を指します。

ちなみに、__ main_block_impl_0では、blockStringフィールドはof_blockString-> __ forwardingの値で初期化されます。

実際には、複数のブロックがスタックまたはヒープにある1つの変数を参照できるように、__ forwardingフィールドが作成されます。



おわりに



したがって、ブロックは特別なクラス(NSMallocBlock、NSGlobalBlock、NSStackBlock)のインスタンスであり、ブロックの本体であるC関数へのリンク(フィールドFuncPtr)と、オプションでブロックによってキャプチャされる変数を含むと言うことができます。



ブロックがクラスであるという事実は、その上であらゆる種類の興味深いメソッドを呼び出すことができることを意味します(カテゴリを通じて独立して追加されるものを含む)。 既存のもののリストはここにあります



すべてのブロック(グローバル、スタック、Malloc)はベースNSBlockから継承され、その説明は次のようになります。

 @interface NSBlock : NSObject <NSCopying> { } - (id)copy; - (id)copyWithZone:(struct _NSZone { }*)arg1; - (void)invoke; - (void)performAfterDelay:(double)arg1; @end
      
      





ブロックはNSObjectの後継であるため、誰でもお気に入りの記述メソッドを呼び出すことができます。これは、ブロックが保存されている場所を理解するのに役立ちます。 例えば

 NSLog(@"%@", [^{} description]);
      
      





<__ NSGlobalBlock__:0x1000010c0>を出力します。 私たちが示唆しているのは、使用しているブロックがグローバルメモリに保存されているということです。

説明の代わりに、クラスメソッドを使用できます。



ボーナス



楽しみのために、繰り返しメソッドをブロックに追加しましょう。



 #import <Foundation/Foundation.h> @interface NSBlock : NSObject <NSCopying> - (void)invoke; @end @interface NSBlock (Ext) - (void)repeat:(NSUInteger)count; @end @implementation NSBlock (Ext) - (void)repeat:(NSUInteger)count { for (int i = 0; i < count; i++) { [(NSBlock *)self invoke]; } } @end int main(int argc, const char * argv[]) { [^{ NSLog(@"Hello"); } repeat:3]; return 0; }
      
      





追記



メモリ管理に関する詳細についての情報を意図的に省略しました(ブロックのコピーなど)。

誰かが興味を持っているなら、書いてください。 このトピックをカバーしようとします。



All Articles