SwiftとOCのループリファレンスの問題

3074 ワード

オブジェクト向けのプログラミング言語を使用して開発する過程で、メモリ管理に関する問題が多い.JAVA、C#などの言語ではGC(ゴミ回収)メカニズムを採用してメモリの使用を管理しているが、iOS開発に携わった最初のエンジニアはMRC(手動管理)メモリの段階を経験し、後期appleはARC(自動参照カウント)方式を発売してメモリの管理を簡素化した.ではARCはいったい何なのか.ARCはどのようにメモリ管理を行っているのか.
自動参照数(ARC)
  • オブジェクトを作成すると、オブジェクトの属性と動作を格納するためにメモリに空間が開かれ、オブジェクトには独自のライフサイクルがあります.システムはどのようにオブジェクトのライフサイクルが終わったと判断して回収しますか?
  • iOSシステムは参照カウントを採用しており、開いたメモリ領域にNSIntegerタイプの変数が存在し、オブジェクトが作成されると値が1になります.(通常)強い参照がその値を指すと+1になり、強い参照がnilに設定され、その値は-1になります(retainメッセージは参照カウント+1になり、releaseメッセージは参照カウント-1になります).1回のイベントループが終了した後、オブジェクトの参照カウントが0になった場合、システムはオブジェクトを回収します.1回のイベントループには何が起こりますか?
  • まず、自動リリースプール(autoReleasePool)について説明します.本質はNSMutableArrayです.1回のイベントループで自動リリースプールが作成され、イベントループで生成されたオブジェクトがautoReleasePoolに順次追加されます.イベントループが終了すると、自動リリースプールは格納されたオブジェクトにreleaseメッセージを一度に送信し、オブジェクトの参照数が-1になるようにします.この操作が完了すると、参照カウントが0のオブジェクトはシステムによって回収されます.
  • 総じて自動参照カウント(ARC)はiOSシステムがメモリ管理を行うための手段であり、オブジェクトの参照カウント値を監視することによってオブジェクトが回収すべきかどうかを決定する.
    1つのオブジェクトが回収されたかどうかを判断する根拠
    オブジェクトの参照カウントが0の場合、Objective-Cでは-(void)deallocが呼び出され、Swiftではdeinit{}が呼び出されます.この2つのメソッドが正常に呼び出されると、オブジェクトのメモリ管理が正しいことを示します.しかし、オブジェクトが正常に解放されない場合もあります.例えば、2つのオブジェクトが互いに強く参照して循環参照をもたらし、blockや閉パッケージを使用してselfとの循環参照をもたらすなど、このような強い参照サイクルをどのように解決すればいいのでしょうか.
    ループ強参照
    クラスのインスタンス間のループ強参照
     @interface Person()
     @property(strong)Car* car;
     @end
    
     @interface Car()
     @property(strong)Person* owner;
     @end   
    

    上記のコードでPersonとCarのインスタンスを作成した後、属性に値を割り当てると、両者が互いに強い参照になり、参照カウントが0にならず、ARCは両者をメモリの解放できない.では、このような相互強い引用をどう打破すればいいのだろうか.Objective-CとSwiftはweakキーワードのメカニズムを提供してこの問題を解決した.weak修飾プロパティを使用すると、値を割り当てるときに参照カウント+1は使用されず、強い参照がなければオブジェクトは正常に解放されます.WeakはSwiftに加えてunowned(主参照なし)が強いループ参照を解決することを提供している.
    Swiftでweakとunowned
    2つのインスタンスが互いに強く参照される場合:(1)インスタンスの値がnilであることが論理的に影響を及ぼさない場合、weak(2)インスタンスを選択する値の一方が存在しなければならず、unownedしか使用できない.例えば、クレジットカードと同様に、クレジットカードの所有者は実際に存在しなければならない.
    blockと閉パッケージに強いループ参照が表示されます
    #import "HZBlock.h"
    typedef void (^TestBlock)(NSString* message);
    @interface HZBlock()
    @property(nonatomic,copy)TestBlock testBlock;
    @property(nonatomic,copy)NSString* name;
    @end
    @implementation HZBlock
    
    -(void)testBlcok{
        self.testBlock = ^(NSString* message){
           //     
            NSString* nameNew = self.name;
            NSLog(@"%@",nameNew);
        };
    }  
    

    上記のコードでblockでselfを使用すると、コンパイラはここに循環参照(Capturing'self'strongly in this block is likely to lead to a retain cycle)が現れることを警告します.
    Objective-Cではselfをweak化してこの問題を解決できる
    // weak self
    __weak __typeof(self) weakSelf = self;
    //  block      self   ,       
    __typeof(&*weakSelf) strongSelf = weakSelf;    
    

    Swiftでキャプチャリストを定義して閉パケット内の参照ループを解決する
    class AutoRefManager: NSObject { 
    
    let name: String
    let text: String?
    
    lazy var asHTML: (Void) -> String = {
    //     
        [unowned self] in
       self.text!
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    }
    

    まとめ
    開発で同様のループリファレンスが発生した場合、メモリの漏洩の問題が発生するかどうかをよく考え、適切なソリューションを選択して問題を解決します.