web-dev-qa-db-ja.com

キー値オブザーバーがまだ登録されているインスタンスが割り当て解除されました

UITableViewを持っています。

ここで別のセルを取得しました。各セルにはモデルがあります。 KVOとNotificationCenterを使用すると、セルはモデルの変更を監視します。 ViewControllerを離れると、次のエラーが発生します。

An instance 0x109564200 of class Model was deallocated while key value observers were still registered with it. 
Observation info was leaked, and may even become mistakenly attached to some other object. 
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x109429cc0> (
<NSKeyValueObservance 0x109429c50: Observer: 0x10942d1c0, Key path: name, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x10968fa00>
)

セルで、モデルプロパティが設定/変更されたときにこれを行います。

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];

次に、セルの割り当て解除で:

- (void)dealloc
{
    NSLog(@"DEALLOC CELL");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_model removeObserver:self forKeyPath:@"name"];
}

モデルでは、割り当てが解除されるタイミングもチェックします。

- (void)dealloc
{
    NSLog(@"DEALLOC MODEL");
}

すべてのセルはすべてのモデルの前に割り当て解除されますが、それでもこのエラーが発生します。また、エラーに示されているブレークポイントを設定する方法もわかりません。

15
Johannes

セルが再利用されているため、機能しません。そのため、セルが画面から消えると、割り当てが解除されず、プールが再利用されます。

セルに通知とKVOを登録しないでください。代わりにテーブルビューコントローラーで行う必要があります。モデルが変更された場合は、モデルを更新して表示セルを再読み込みする必要があります。

8
Greg

答えを見つけました。私はスレッドを削除できません、誰かが答えました:)多分それは誰かにとって役立つでしょう。

問題は、UITableViewが、以前に使用されたのと同じセルを、下に長い行に対してデキューすることです(十分にスクロールすると表示されます)。

セッターで私は今持っています:

// Before we set new model
if (_model) {
    [_model removeObserver:self forKeyPath:@"name"];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"modelIsInvalid" object:_model];
}

_model = model;

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];
6
Johannes

受け入れられた答え(正しい)に基づいて、セルの "prepareForReuse"メソッドでオブザーバーを削除することで解決できます。

このメソッドは、スクロールなどでセルを再利用する前に呼び出されるため、問題はありません。

- (void)prepareForReuse{
    [_model removeObserver:self forKeyPath:@"name"];
}
4
PakitoV

ビューコントローラーがdeallocメソッドを呼び出していない可能性があります。参照が誰かに保持されている可能性があり、deallocメソッドが呼び出されていない可能性があります。 viewDidUnload:またはviewWillDisappear:メソッドでオブザーバーを削除するか、保持のためにコントローラーを機器にトレースすることができます

0
Retro

セルと再利用可能なビューでこれを行うのに最適な場所はwillMove(toSuperiew)です

override func willMove(toSuperview newSuperview: UIView?) { if newSuperview == nil { // check for nil means this will be removed from superview self.collectionView?.removeObserver(self, forKeyPath: "contentSize") } }

0
Basheer_CAD