ios Blockタイプ

5457 ワード

Objective-CにはBlockの3種類があります.
NSStackBlock     
NSGlobalBlock    
NSMallocBlock    

MRC下
    @property (nonatomic, copy ) void(^block)();

    int value = 10;
    void(^blockA)() = ^{
        NSLog(@"value: %d",value);
    };
    NSLog(@"MRC  : %ld, block is: %@",[blockA retainCount], blockA);

    void(^blockB)() = ^{
        NSLog(@"blockB");
    };
    NSLog(@"MRC  : %ld, block is: %@",[blockB retainCount], blockB);

    _block = [blockA copy];
    NSLog(@"MRC  : %ld, block is: %@",[self.block retainCount],self.block);
    
    [_block retain];
    NSLog(@"MRC  : %ld, block is: %@",[self.block retainCount],self.block);

    [_block release];
    NSLog(@"MRC  : %ld, block is: %@",[self.block retainCount],self.block);

印刷結果:
    MRC  : 1, block is: <__nsstackblock__:>
    MRC  : 1, block is: <__nsglobalblock__:>
    MRC  : 1, block is: <__nsmallocblock__:>
    MRC  : 1, block is: <__nsmallocblock__:>
    MRC  : 1, block is: <__nsmallocblock__:>


blockAとblockBの違いは、外部変数が呼び出されているかどうかにすぎないことがわかります.この違いは、それらのタイプが異なり、格納場所が異なります.
  • NSGlobalBlock block内部に外部変数を参照していないBlockタイプはいずれもNSGlobalBlockタイプであり、グローバルデータ領域に格納され、システムによってメモリが管理され、retain、copy、release操作は無効である.
  • NSStackBlock block内部参照外部変数、retain、release操作が無効でスタック領域に格納され、変数役割ドメインが終了するとシステムによって自動的に破棄される.MRC環境では[[mutableAarry addObject:blockA],(arcではこの問題を心配する必要はありません.arcではインスタンス化されたblockがスタックにデフォルトでコピーされるためです)が、その役割ドメインの終了である関数スタックを出た後、mutableAarryから取り出されたblockAは回収され、野ポインタになりました.正しい方法は、blockA copyをスタックに入れてから配列を加えることです.copyをサポートし、copyの後に新しいNSMallocBlockタイプのオブジェクトを生成します.
  • NSMallocBlock前例のように_block[blockA copy]・操作後変数タイプがNSMallocBlockとなり、retain、releaseをサポートしている.retainCountは常に1であるが、メモリマネージャではカウントが増加・減少し、参照カウントがゼロの場合は解放される(retain、release操作検証が複数回可能).copy以降は新しいオブジェクトは生成されませんが、retainのように、Blockに対してretain操作を使用しないでください.

  • ARC下:
        @property (nonatomic, copy ) void(^block)();
    
        int value = 10;
        void(^blockA)() = ^{
            NSLog(@"value: %d",value);
        };
        NSLog(@"ARC  : %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockA)), blockA);
        
        void(^blockB)() = ^{
            NSLog(@"blockB");
        };
        NSLog(@"ARC  : %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockB)), blockB);
        
        _block = blockA;
        NSLog(@"ARC  : %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)_block)), _block);
    

    印刷結果:
        ARC  : 1, block is: <__nsmallocblock__:>
        ARC  : 1, block is: <__nsglobalblock__:>
        ARC  : 1, block is: <__nsmallocblock__:>
    

    同じBlock変数blockAはMRCではNSStackBlock型であり,ARCではNSMallocBlock型であることを見出した.次のARCのテストコードを見てみましょう.
       int value = 10;
        NSLog(@"%@",^{
            NSLog(@"value: %d",value);
        });
        
        void(^blockA)() = ^{
            NSLog(@"value: %d",value);
        };
        NSLog(@"ARC  : %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockA)), blockA);
    
        // 
        //<__nsstackblock__:>
        //ARC  : 1, block is: <__nsmallocblock__:>
    
    

    このことから,ブロック変数は付与時にシステムが自動的にスタック領域にコピーし,変数ブロックAがNSMallocBlockタイプであることが分かる.
    2つのblockプロパティを定義します.
    @property (nonatomic, /*copy strong assign retain*/assign) void(^block)();
    @property (nonatomic, copy) void (^nameAge)();
    
    

    clangでcppソースを表示
    static void(* _I_OneViewController_block(OneViewController * self, SEL _cmd) )(){ return (*(void (**)())((char *)self + OBJC_IVAR_$_OneViewController$_block)); }
    static void _I_OneViewController_setBlock_(OneViewController * self, SEL _cmd, void (*block)()) { (*(void (**)())((char *)self + OBJC_IVAR_$_OneViewController$_block)) = block; }
    
    static void(* _I_OneViewController_nameAge(OneViewController * self, SEL _cmd) )(){ return (*(void (**)())((char *)self + OBJC_IVAR_$_OneViewController$_nameAge)); }
    extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
    
    static void _I_OneViewController_setNameAge_(OneViewController * self, SEL _cmd, void (*nameAge)()) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct OneViewController, _nameAge), (id)nameAge, 0, 1); }
    

    2つの属性のsetter getterメソッドを見ることができ、assign修飾block属性のsetterメソッドは直接付与されている.これにより、文_block = blockA;、局所変数blockAは属性blockに付与され、基本データ型に等しい値付与であり、その役割ドメインが終了すると、属性blockが指すメモリは自動的に解放され、属性blockは野ポインタであることが理解できる.メソッドの外で属性blockを呼び出すと、潰れてしまいます.
    - (IBAction)tapBtnOne:(UIButton *)sender {
    
        NSLog(@"%@",_block);
    }
    

    呼び出しメソッドtapBtnOneは潰れます.
    しかし、我々が印刷した変数blockAのタイプはNSMallocBlockであり、そのメモリはスタック領域に割り当てられ、_block = blockA;後、属性blockは同じブロックAが指すメモリブロックを指し、本来はスタック領域に割り当てられ、役割ドメインが終了してもシステムによって自動的に解放されない.実はよく理解して、属性_block修飾修飾修飾修飾子はassignであり、いずれの操作も参照カウントが増加しないことを示す.すなわち_block = blockA;文の後、blockの参照カウントは増加せず、同じメモリを指しています.役割ドメインが終了すると、ARCの下でrelease操作文を自動的に補完して変数blockAを解放します.blockは野針になった.
    したがって、Blockプロパティのプロパティ制限子は、setterメソッドでスタック領域に新しいコピーがコピーされるため、一般的にcopyです.