Blockの本質

4761 ワード

Objective Cオブジェクトメモリモデル


blockの背後にあるメモリモデルは実際には構造体であり、この構造体はblockの実際の実行コードを指す関数ポインタを格納する.
クラスの構造の表示
struct DemoClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_value;
};

NSObject_の表示IMPL
struct NSObject_IMPL {
    Class isa;
};

Classの表示
typedef struct objc_class *Class;

struct objc_の表示class
struct objc_class {
    Class isa ;
};

Cの中にBがあります.Bの中にAがあります.Aの中にAがあります.

blockの本質


コード変換
static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {

    NSInteger variable = 10;
    VoidBlock temp = ((void (*)())&__DemoClass__demoFunction_block_impl_0((void *)__DemoClass__demoFunction_block_func_0, &__DemoClass__demoFunction_block_desc_0_DATA, variable));

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

}

分析:
ブロックの構造体
  struct __DemoClass__demoFunction_block_impl_0 {
  struct __block_impl impl;
  struct __DemoClass__demoFunction_block_desc_0* Desc;
  NSInteger variable;
  __DemoClass__demoFunction_block_impl_0(void *fp, struct __DemoClass__demoFunction_block_desc_0 *desc, NSInteger _variable, int flags=0) : variable(_variable) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__block_impl(すなわちblockクラスの構造)
struct __block_impl {
  void *isa; // OC isa , , 
  int Flags; // 
  int Reserved; // 
  void *FuncPtr; //Block 
};


__DemoClass__demoFunction_block_desc_0(本構造体の説明情報)
static struct __DemoClass__demoFunction_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __DemoClass__demoFunction_block_desc_0_DATA = { 0, sizeof(struct __DemoClass__demoFunction_block_impl_0)};


コンストラクション関数(すなわち、構造体インスタンスを作成する際に必要な初期化作業を行うための初期化関数)
__DemoClass__demoFunction_block_impl_0(void *fp, struct __DemoClass__demoFunction_block_desc_0 *desc, NSInteger _variable, int flags=0) : variable(_variable) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }

参考:blockの本質

メモリ内のストレージ領域の分割

  • TEXTコード領域
  • DATAデータ領域
  • Stackスタック
  • HEAPスタックとスタックの違い
  • Blockのタイプ

  • NSConcreteStackBlockスタックに割り当てられ、役割ドメインが終了すると自動的に
  • が解放される.
  • NSConcreteGlobalBlockグローバル割り当て、グローバル変数のようにデータセグメントに格納され、メモリには
  • しかありません.
  • NSConcreteHeapBlockスタック上割当
  • Blockの宣言と使用

    int multiplier = 7;
    int (^myBlock)(int) = ^(int num) {
        return num * multiplier;
    };
    //   int (^)(int)  ,myBlock ,^(int num) {
    //    return num * multiplier;
    // };  
    
    printf("%d", myBlock(3));
    // prints "21"
    

    Blockの直接使用

    char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
    
    qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    
    char *left = *(char **)l;
    
    char *right = *(char **)r;
    
    return strncmp(left, right, 1);
    
    });
    
    // myCharacters is now { "Charles Condomine", "George", "TomJohn" }
    

    __ブロック変数


    blockでblock外の変数を使用する


    blockの不思議な点は、block外の変数がシームレスにblock内部で直接使用できることです.例えば、
    float price = 1.99; 
    float (^finalPrice)(int) = ^(int quantity) {
        // Notice local variable price is 
        // accessible in the block
        return quantity * price;
    };
    int orderQuantity = 10;
    NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));
    
     Ordering 10 units, final price is: $19.90
    

    しかし、block内部でローカル変数の値を変更することはできません.さらに注意しなければならないのはpriceのような局所変数の変化はblockに現れないことです!たとえば、上のコードに続いて、次のように書きます.
    price = .99;
    NSLog(@"Ordering %d units, final price is: $%2.2f", orderQuantity, finalPrice(orderQuantity));
     Ordering 10 units, final price is: $19.90
    

    これは悲しいですが、block内のpriceはreadonlyであり、blockを定義するときだけ付与されると理解できます(補足説明ですが、実際にはpriceがvalue typeなので、block内のpriceはblock内にコピーされていることを明らかにし、block外のpriceはどのように変化してもblock内のpriceとは関係ありません.reference typeであれば、外部の変化は実際にblock内に影響します).

    blockで変数を変更する


    blockで変数を変更するには、次の2つの方法が考えられます.1.blockで変更したい外部ローカルオブジェクトについては、これらの変数に__blockキーワード修飾を加えることで、blockで変更することができます.
     __block int multiplier = 7;
        int (^myBlock)(int) = ^(int num) {
            
            multiplier ++;
            return num * multiplier;
        };
        
        myBlock(2);
    
    

    2.インスタンス変数を使用すると、インスタンス内の変数がインスタンス全体にわたって横行するように変更できます.

    リファレンス


    公式ドキュメントblockの本質Objective-CにおけるBlock iOS開発-blockを浅から深く学ぶ