サーチ…


前書き

このトピックでは、Swiftランタイムがアプリケーションデータ構造にメモリを割り当てる方法とタイミング、およびそのメモリを再利用するタイミングを概説します。デフォルトでは、メモリバッキングクラスインスタンスは参照カウントによって管理されます。構造体は常にコピーを通過します。ビルトインメモリ管理スキームをオプトアウトするには、[ Unmanaged ] [1]構造を使用することができます。 [1]:https://developer.apple.com/reference/swift/unmanaged

備考

弱いキーワードを使用する場合:

参照を保持しているオブジェクトの存続期間中に参照オブジェクトが割り当て解除される可能性がある場合は、 weakキーワードを使用する必要があります。

unowned-keywordを使用するタイミング:

参照されたオブジェクトが参照を保持するオブジェクトの存続期間中に割り当て解除されることが予想されない場合、 unownedキーワードを使用する必要があります。

落とし穴

頻繁に起こるエラーは、機能が終了した後で生活する必要があるオブジェクトへの参照を作成することを忘れてしまうことです(ロケーションマネージャやモーションマネージャなど)。

例:

class A : CLLocationManagerDelegate
{
    init()
    {
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.startLocationUpdates()
    }
}

この例は、イニシャライザが戻った後にロケーションマネージャが割り当て解除されるため、正常に動作しません。適切な解決策は、インスタンス変数としての強力な参照を作成することです。

class A : CLLocationManagerDelegate
{
    let locationManager:CLLocationManager

    init()
    {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.startLocationUpdates()
    }
}

リファレンスサイクルと弱い参照

参照サイクル (または保持サイクル )は、 オブジェクトグラフの サイクルを示すため、その名前が付けられています。

サイクルを保持する

各矢印は、あるオブジェクトが別のオブジェクトを保持していることを示します(強い参照)。サイクルが壊れていない限り、これらのオブジェクトのメモリは決して解放されません

保持サイクルは、クラスの2つのインスタンスが互いに参照するときに作成されます。

class A { var b: B? = nil }
class B { var a: A? = nil }

let a = A()
let b = B()

a.b = b  // a retains b
b.a = a  // b retains a -- a reference cycle

両方のインスタンスは、プログラムが終了するまで存続します。これは保持サイクルです。

弱い参照

保持サイクルを回避するには、保持サイクルをunowned参照を作成するときにキーワードweakまたはunowned使用します。

class B { weak var a: A? = nil }

弱いまたは所有されていない参照は、インスタンスの参照カウントを増加させません。これらの参考文献は、保持サイクルに寄与しない。 weak参照 、それが参照するオブジェクトの割り当てが解除されるnilなります。

a.b = b  // a retains b
b.a = a  // b holds a weak reference to a -- not a reference cycle

クロージャを操作するときには、キャプチャリストにweakunowned使用することもできます

手動メモリ管理

C APIとのインタフェース時に、Swiftリファレンスカウンタをオフにしたい場合があります。これを行うには、アンマネージオブジェクトを使用します。

C関数に型打ちポインタを渡す必要がある場合は、 Unmanaged構造体のtoOpaqueメソッドを使用して未加工のポインタを取得し、 fromOpaqueを使用して元のインスタンスを復元します。

setupDisplayLink() {
  let pointerToSelf: UnsafeRawPointer = Unmanaged.passUnretained(self).toOpaque()
  CVDisplayLinkSetOutputCallback(self.displayLink, self.redraw, pointerToSelf)
}

func redraw(pointerToSelf: UnsafeRawPointer, /* args omitted */) {
  let recoveredSelf = Unmanaged<Self>.fromOpaque(pointerToSelf).takeUnretainedValue()
  recoveredSelf.doRedraw()
}

passUnretainedとcounterpartsを使用する場合、 unownedれていunowned参照と同様に、すべての予防措置を取る必要があることに注意してください。

レガシーObjective-C APIと対話するには、特定のオブジェクトの参照カウントに手動で影響を与えたい場合があります。そのため、 Unmanagedはそれぞれのメソッドがretainreleaseます。それにもかかわらず、結果を返す前に保持を実行するpassRetainedtakeRetainedValueを使用する方が望ましいです。

func preferredFilenameExtension(for uti: String) -> String! {
  let result = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension)
  guard result != nil else { return nil }

  return result!.takeRetainedValue() as String
}

これらのソリューションは常に最後の手段であるべきであり、言語固有のAPIが常に優先されるべきです。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow