2

node.js EventEmitterに基づいてカテゴリを作成しようとしています。これは、多数のブロックを取り、それらを配列に弱く格納し、ブロックを作成するインスタンスの割り当てが解除されていない場合に後で実行できます (その場合、それらは配列から削除されます)。これは、古い未使用のブロックで配列を埋め続けないようにするためです。

問題は、ブロックを作成するインスタンスの割り当てが解除されても、ブロックがクラスによってコピーされたように見えるため、決して解放されないことです。

したがって、実装は次のようになります。

使用法

[object on:@"change" do:^(id slf, NSArray *args) {
    NSLog(@"something changed");
}];

実装 (ここにあるWeakReference クラス、noaの好意による)

- (void)on:(NSString *)eventType do:(Callback)callback
{
    NSMutableArray *callbacks = self.emitterEvents[eventType];
    __weak Callback wcb = callback;
    // Wrap the callback in NSValue subclass in order to reference it weakly
    WeakReference *cbr = [WeakReference weakReferenceWithObject:wcb];
    callbacks[callbacks.count] = cbr;
}

- (void)emit:(NSString *)eventType withArgs:(NSArray *)objArgs
{
    NSInteger idx = 0;
    NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
    callbacks = (NSMutableArray *)callbacks;
    for (WeakReference *cbv in callbacks) {
        __weak id cb = [cbv nonretainedObjectValue];
        if (cb) {
            Callback callback = (Callback)cb;
            __weak id slf = self;
            callback(slf, objArgs);
        } else {
            [indices addIndex:idx];
        }
        idx++;
    }
    [callbacks removeObjectsAtIndexes:indices];
}

ブロックがその範囲を超えて使用されるとコピーされるという記事を読んだことがありますが、率直に言って、これらすべてのブロックのセマンティクスについて読むと、今、頭がくらくらします。

問題にアプローチするこの方法は可能ですか?

4

3 に答える 3

0

すでにコピーされているブロックをコピーすることは、それを保持することと同じであるため、メソッドの呼び出し元が最初にブロックをコピーしてからメソッドに渡すと、期待どおりに機能するはずです。ただし、これは、使用法セクションで説明したように、メソッドを単純に使用できないことを意味します。

あなたはこのようにそれを使用しています

typeofblock block = ^(id slf, NSArray *args) {
    NSLog(@"something changed");
};
self.block = [block copy]
[object on:@"change" do:self.block];

問題を実際に解決するには、ブロックの所有者を把握する必要があります。の呼び出し元on:do:、または呼び出されたオブジェクト?

ブロックの所有者が呼び出し元であることを意味する、呼び出し元の割り当てが解除されたときにブロックを削除したいようです。ただし、on:do:メソッドはブロックの所有者を認識せず、呼び出し元の割り当てが解除されたときにブロックを削除できません。

1 つの方法は、ブロックの所有者をメソッドに渡し、割り当てが解除されたときにブロックを削除することです。これは関連オブジェクトを使用して行うことができます。

- (void)on:(NSString *)eventType do:(Callback)callback sender:(id)sender
{
    // add the block to dict
    // somehow listen to dealloc of the sender and remove the block when it is called
}

もう1つの方法は、ブロックを削除する新しいメソッドを追加し、そのメソッドをdeallocまたは別の場所で呼び出してブロックを手動で削除することです。

あなたのアプローチは、オブザーバーが観測を登録解除する必要があるKVOに似ています。従うべき良い習慣だと思います。

于 2013-04-12T01:16:33.850 に答える