2016-07-05 8 views
3

私はSwiftについてもっと学び、deferステートメントを最近訪れました。これは本当に興味深いようです。しかし、私はそれが目的であることを本当に理解していません。 C++からは、割り当て解除機能を使用して同じ機能を実装していましたが、SwiftはARCなので、同じことができます。Swiftのリソース割り当て解除のためにdeinitとdeferを使用した場合の違い

はの割り当てを解除する必要があるデータとFooDataBarData仕事の両方を言ってみましょう。

class FooData { 
    deinit { 
     print("FooData being deallocated") 
    } 
} 

class BarData { 
} 

func baz() -> Int { 
    var a = FooData() 
    var b = BarData() 
    defer { print("BarData being deallocated") } 

    /* sensitive operations that could throw at any time */ 

    return 0 
} 

baz() 
// BarData being deallocated 
// FooData being deallocated 

のでdeinitアプローチを超えるdeferアプローチの利点は何ですか?リソースのクリーンアップ以外に何かのためにdeferを使用することについて考えてみると、私の頭が痛くなります。

答えて

3

deferは戻ってくる前にクリーンアップを処理するための安全で簡単な方法としてAppleによって導入されましたが、deferはスコープでのみ動作します。この場合

func resizeImage(url: NSURL) -> UIImage? { 
    // ... 
    let dataSize: Int = ... 
    let destData = UnsafeMutablePointer<UInt8>.alloc(dataSize) 
    defer { 
     destData.dealloc(dataSize) 
    } 

    var destBuffer = vImage_Buffer(data: destData, ...) 

    // scale the image from sourceBuffer to destBuffer 
    var error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, ...) 
    guard error == kvImageNoError 
     else { return nil } 

    // create a CGImage from the destBuffer 
    guard let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, ...) 
     else { return nil } 
    // ... 
} 

を、それはしません。ですから、機能は、作成した変数内で定義されたいくつかのスコープを持っている場合は、たとえば、deinitからアクセスすることができない範囲内でのみ存在し、私はより良い説明しましょう変数destDataをグローバルとして定義し、作業が終了したら割り当てを解除する必要があります。deferこれが選択です。

私はdeinitと考えています。たとえば、NSNotificationCenterなどを使用してKey-Value Observerを実装した場合など、よりグローバルなスコープに使用できます。

私はこれがあなたに役立つことを願っています。

0

データベーストランザクションを考えてみましょう。あなたが完了したら、接続を閉じるようにしたいが、あなたは将来の接続を復元するために周りのオブジェクトを維持したい:

stuct foo { 

    let db = Database() 

    func useDatabase() throws { 
     let connection = db.openConnection() 
     defer { 
      // close conenction, but keep db around for future use 
      connection.close 
     } 

     try connection.thisCanThrow() 
    } 
} 

これはほんの一例ですが、それのような多くがあります。特に、オブジェクトの存続時間に縛られることなく、状態の制限された寿命をモデル化したい場合は、多くのケースが発生します。重くRAIIに依存してい++

C。スウィフトは確かに同じパラダイムに固執することができますが、deferでそれを超えることもできます。

+0

しかし、この場合、接続オブジェクトの接続の寿命を延ばすことができます。データベースは後で使用するために保管され、接続は終了します。 – hgiesel

+0

はい、できます。 RAIIは確かに有能ですが、これが示すように、他の方法があります。 – Alexander

+0

ここで、閉じられなければならないクラス/構造体があなたのものではないので、あなたは 'deinit'を変更できません。じゃあ何?あなたはラッパーオブジェクトを書く必要があります。面倒です。 – Alexander

2

プログラミングでは、一部の機能は常にペアで表示されます。たとえば、接続を開き、その接続を閉じる、ミューテックスをロックする、ミューテックスをロック解除する、カウンターをインクリメントする、カウンターを減らす、メモリーを割り当てる、メモリーを割り振る、など。

パターンは、通常、次のようになります。

lock() 
... do something ... 
unlock() 

真ん中の部分が複雑で長くすることができます。返品がある場合があります(たとえば、失敗した前提条件の場合、Swiftはこのパターンをguardと推奨しています)。場合によっては、すべての実行パスにunlock()を含めることを忘れることは非常に困難です。素敵な状況を解決する

一つの方法は、ヘルパー関数を使用している:

func doSomething() { 
    ... do something with returns ... 
} 

lock() 
doSomething() 
unlock() 

が、それは常に可能ではないですが、例えばネストされたオブジェクトが複数ある場合

Cで同じパターンがしばしばgotoで解決しました:

x = malloc(...); 
y = malloc(...); 
if (!precondition) { 
    goto end; 
} 

... some code ... 

end: 
free(y); 
free(x); 

現代の言語は、スウィフトに(あなたはまた、例えば、移動中deferを見つけることができます)deferを使用して実装されてより良いアプローチとなりました。

lock() 
defer { 
    unlock() 
} 
... some code ... 

このアプローチにはいくつかの利点があります。

  1. は、あなたが一緒に呼び出しを持つことができ、可読性が向上し、それは非常に難しい2番目の呼び出しを忘れすることができました。
  2. unlockが常に正しく呼び出されるため、すべての返品、前提条件のチェック、エラー処理によってコードは正しい状態になります。これは、あなたがdeinitから違いについて尋ねている場合は、それは同様の方法で動作するJava(および他の言語)で例外処理に

finallyに似ています。ただし、deferは機能で動作し、deinitはクラスに対してのみ機能します。

deinitを使用してdeferを再実装することもできますが、使用方法が複雑になり、動作が予測できなくなります。この方法は、方法が出ているように、その作業が実行されることを意味内

1

延期は延期を使用してdeinit

​​
1

で実装することは不可能であるものを、条件付きで呼び出すことができます。

override func viewDidLoad() { 
    super.viewDidLoad() 

    print("Step 1") 
    myFunc() 
    print("Step 5") 
} 

func myFunc() { 
    print("Step 2") 
    defer { print("Step 3") } 
    print("Step 4") 
} 

"ステップ1"、 "ステップ2"、 "ステップ4"、 "ステップ3"、 "ステップ5" - 、myFuncという()メソッドが終了するまで3が遅延されるので、図3及び図4を切り替えるステップつまりプログラムでスコープの外に出たときです。


についてdeinit、これは初期化解除する前に、コードを実行するために使用されます。デイニットコードは自動的に実行されます。インスタンスの割り当て解除が行われる直前に、自動的にDeinitializerが呼び出されます。あなたは自分自身でdeinitializerを呼び出すことはできません。

class Item { 
    init() { 
    print("init called") 
    } 
    deinit { 
    // Called whenever class stops existing. 
    print("deinit called") 
    } 
} 

// Create optional Item variable. 
var i: Item? = Item() 
// Set optional to nil to force deinit. 
i = nil 
関連する問題