Firebaseを使用してイベントを監視し、完了ハンドラー内に画像を設定しています
FirebaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.img = UIImage(named:"Some-image")!
} else {
self.img = UIImage(named: "some-other-image")!
}
})
しかし、私はこのエラーを受け取っています
クロージャは、変化する自己パラメータを暗黙的にキャプチャできません
このエラーの内容がわからないため、解決策を検索しても役に立たない
ショートバージョン
FirebaseRef.observeSingleEvent(of:with:)
への呼び出しを所有する型は、ほとんどの場合値型(struct
?)です。この場合、変化するコンテキストは@escaping
クロージャーでself
を明示的にキャプチャしない場合があります。
簡単な解決策は、所有タイプを参照に一度更新することです(class
)。
長いバージョン
Firebaseの observeSingleEvent(of:with:)
メソッド は次のように宣言されます
func observeSingleEvent(of eventType: FIRDataEventType, with block: @escaping (FIRDataSnapshot) -> Void)
block
クロージャーは@escaping
パラメーター属性でマークされます。これは、その関数の本体、さらにはself
のライフタイム(コンテキスト内)をエスケープする可能性があることを意味します。この知識を使用して、分析できる最小の例を構築します。
struct Foo {
private func bar(with block: @escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
これで、エラーメッセージがよりわかりやすくなり、Swift 3で実装された次の進化の提案に移ります。
[強調鉱山]:
inout
パラメーターを変更メソッドに含むself
をキャプチャすると、エスケープ可能なクロージャーリテラルキャプチャが明示的に行われない限り(したがって不変)。
さて、これは重要なポイントです。 valueタイプ(例:struct
)の場合、これはあなたの例のobserveSingleEvent(...)
への呼び出しを所有するタイプにも当てはまると思います。明示的なキャプチャは不可能です(参照型ではなく値型を使用しているため)。
この問題の最も簡単な解決策は、observeSingleEvent(...)
を所有する型を参照型にすることです。 class
ではなく、struct
:
class Foo {
init() {}
private func bar(with block: @escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
強い参照によってself
をキャプチャすることに注意してください。コンテキストに応じて(私はFirebaseを使用したことがないので、知りません)、self
を明示的に弱くキャプチャしたい場合があります。
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
クロージャ内の値の型(struct
)を変更する必要がある場合、これは同期的にのみ機能しますが、非同期呼び出しでは機能しません。
struct Banana {
var isPeeled = false
mutating func peel() {
var result = self
SomeService.synchronousClosure { foo in
result.isPeeled = foo.peelingSuccess
}
self = result
}
}
それ以外の場合は、可変(したがってvar
)コピーを提供することを除いて、値型で「変化する自己」をキャプチャすることはできません。
これが非同期コンテキストで機能しない理由は、コンパイラエラーなしでresult
を変更できますが、self
に変更結果を割り当てることはできません。それでも、エラーは発生しませんが、クロージャーがディスパッチされる前にメソッド(peel()
)が終了するため、self
は変更されません。
これを回避するには、コードの変更を試みて、非同期呼び出しが終了するのを待って非同期実行を同期実行に変更します。技術的には可能ですが、これはおそらく相互作用している非同期APIの目的を無効にしているので、アプローチを変更した方がよいでしょう。
struct
をclass
に変更することは技術的に適切なオプションですが、実際の問題には対処しません。この例では、現在class Banana
であるため、そのプロパティはwho-knows-whenを非同期に変更できます。理解するのが難しいので、問題が発生します。 モデル自体の外部でAPIハンドラーを記述し、実行のフェッチが完了してモデルオブジェクトを変更する方が適切です。コンテキストがなければ、適切な例を挙げるのは困難です。 (OPのコードでself.img
が変更されているため、これはモデルコードであると考えられます。)
私はこれの行の中で何かを考えています:
BananaNetworkRequestHandler
はリクエストを非同期的に実行し、結果のBananaPeelingResult
をBananaStore
に報告しますBananaStore
は、peelingResult.bananaID
を探して、その内部から適切なBanana
を取得しますbanana.bananaID == peelingResult.bananaID
でオブジェクトを見つけたら、banana.isPeeled = peelingResult.isPeeled
を設定し、必要な変更がアプリのアーキテクチャの変更を含む場合は特に、簡単な修正を見つけるための探求から、それは非常に簡単に関与する可能性があります。
(検索から)このページに誰かがつまずき、protocol
/protocol extension
を定義している場合、protocol
をclass boundとして宣言すると役立つ場合があります=。このような:
protocol MyProtocol: class
{
...
}
これを試すことができます!あなたを助けることを願っています。
struct Mutating {
var name = "Sen Wang"
mutating func changeName(com : @escaping () -> Void) {
var muating = self {
didSet {
print("didSet")
self = muating
}
}
execute {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 15, execute: {
muating.name = "Wang Sen"
com()
})
}
}
func execute(with closure: @escaping () -> ()) { closure() }
}
var m = Mutating()
print(m.name) /// Sen Wang
m.changeName {
print(m.name) /// Wang Sen
}