0

KVO を使用して、UILabel とオブジェクト Data の間に小さなバインディング システムを作成しようとしています。UI が変更された場合はデータを変更する必要があり、データが変更された場合は UI を更新して新しい値を表示する必要があります。

私が抱えている最大の問題は、__bridge_retained void* または CFBridgingRetain() を使用してカスタム オブジェクトを void* (コンテキスト) にキャストする必要があることですが、どこで CFBridgingRelease() を呼び出す必要があるかわかりません。observeValueForKeyPath メソッドで呼び出すと、不正なアクセス エラーが発生します (コンテキストが指すオブジェクトへの参照カウントが 0 であるためだと思います)。

// viewDidLoad
// binding my label text with a custom data object
[self bindObject:_myLabel withPath:@"text" toObject:_user path:@"name"];

-(void) bindObject:(id)uiObj withPath:(NSString *)uiPath toObject:(id)dataObj path:(NSString *)dataPath
{
    // custom object storing the object I want to bind and his path
    PLSObjectPath* op = [[PLSObjectPath alloc] init];
    op.theObj = dataObj;
    op.thePath = dataPath;
    PLSObjectPath* ob = [[PLSObjectPath alloc] init];
    ob.theObj = uiObj;
    ob.thePath = uiPath;

    /* possible leak because I don't know where to call CFBridgingRelease */
    [uiObj addObserver:self forKeyPath:uiPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(op)];
    [dataObj addObserver:self forKeyPath:dataPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(ob)];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{   

    PLSObjectPath *obj = (__bridge PLSObjectPath*) context;
    PLSObjectPath* pairObj = [[PLSObjectPath alloc] init];
    pairObj.theObj = object;
    pairObj.thePath = keyPath;
    // avoid infinite loop
    [obj.theObj removeObserver:self forKeyPath:obj.thePath];
    [obj.theObj setValue:change[@"new"] forKeyPath:obj.thePath];
    [obj.theObj addObserver:self forKeyPath:obj.thePath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(pairObj)];
}
4

2 に答える 2

1

従来、これのユーザーは、さまざまなobserveValueForKeyPathメッセージを区別するために、静的な char * をコンテキスト パラメーターとして使用していました。そうは言っても、あなたが試みているように何かをすることは可能であるべきです。

私がお勧めするのは、カスタム オブジェクトから Core Foundation オブジェクトに切り替えることです。ここでは、メモリ管理を明示的に行うことができます。したがってPLSObjectPath、CFDictionary に変更することをお勧めします。最初に NSDictionary を作成し、次に適切なキャストを使用してそれを CF ドメインに「転送」し、その CFDictionary オブジェクトをコンテキスト用に渡すことができます (これは現在保持されている CF オブジェクトです)。それを CFDictionary にobserveValueForKeyPath再キャストし、適切に ARC で NSDictionary にキャストし、それを使用すると、ARC を正しく実行した場合に解放されます。これはすべてよく理解されたパラダイムです - オブジェクトを ARC の内外に移動します。

もう 1 つの方法は、静的な NSMutableDictionary を使用し、コンテキスト ポインターを使用してint値に移動することです。NSNumber に変換すると、辞書のキーになります。すべての KVO がメイン スレッドで発生する場合、ディクショナリを保護する必要はありませんが、そうでない場合は、1 つのスレッドでシリアル アクセスを強制するシリアル ディスパッチ キューの背後にあるディクショナリへのすべてのアクセスを配置する必要があります。

于 2013-11-13T13:35:23.130 に答える
0

メモリ リークは [obj.theObj removeObserver:self forKeyPath:obj.thePath] によるものです。オブザーバーを削除していますが、システムはコンテキストをオブジェクトとして扱わないため、解放されません。また、観測されたオブジェクト自体からコンテキストを取得する方法はありません。

ある時点で、すべての監視を停止して、ビューの割り当てを解除できるようにする必要があります。これは、viewDid(Will)Disappear: から発生するはずです。その時点で PLSObjectPath:s を解放できるようにするには、おそらく NSMutableArray のどこかに保存する必要があります。この目的でこれらを保存する場合は、__bridge_retained を使用する必要はありません。また、この場合、PLSObjectPath:s には、他のコンテキストを指す void* が含まれている可能性があります/含まれている必要があります。このようにして、PLSObjectの作成をobserveValueForKeyPath:ofObject:change:context:に保存します。

コメントだけですが、viewDidLoad からではなく、viewWill(Did)Appear: から KVO を開始する必要があります。これにより、KVO の開始/停止管理が向上し、ビューが画面に表示されていない間の不要な観察も削除されます。

于 2013-11-13T14:31:43.177 に答える