我正在尝试编写一个基于node.js EventEmitter的类别,它可以占用多个块,将它们弱地存储在一个数组中,如果创建块的实例不是,则稍后执行它们deallocated(在这种情况下,它们将从数组中删除)。这是为了不使用旧的未使用的块来填充数组。
问题是这些块似乎被类复制,因此即使创建块的实例被释放,也不会被释放。
所以实现看起来像这样;
[object on:@"change" do:^(id slf, NSArray *args) {
NSLog(@"something changed");
}];
- (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];
}
我在阅读范围之后阅读了blocks being copied的一些内容,但坦率地说,阅读所有这些块语义,现在让我头晕目眩。
这种解决问题的方式是否可行?
答案 0 :(得分:0)
复制已复制的块与保留它相同,因此如果方法的调用者首先复制块然后将其传递给方法,它应该按预期工作。但这意味着您不能简单地使用您在使用部分中描述的方法。
你已经像这样使用它了
typeofblock block = ^(id slf, NSArray *args) {
NSLog(@"something changed");
};
self.block = [block copy]
[object on:@"change" do:self.block];
要实际解决问题,你必须搞清楚拥有该块。 on:do:
的调用者,或者被调用的对象?
听起来我想在释放调用者时删除该块,这意味着该块的所有者是调用者。但是你的on:do:
方法不知道块的所有者,并且在释放调用者时无法删除块。
一种方法是将块的所有者传递给方法,并在解除分配时删除块。这可以使用关联对象来完成。
- (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
}
另一种方法是添加新方法来删除块,并调用dealloc
或其他地方的方法来手动删除块。
你的方法类似于KVO,它要求观察者注销观察,我认为这是一个很好的做法,你应该遵循。
答案 1 :(得分:0)
在Objective-C中,块是对象,但与其他对象不同,它们是在堆栈上创建的。如果要使用创建范围之外的块,则必须copy
它。
[object on:@"change" do:^(id slf, NSArray *args) {
NSLog(@"something changed");
}];
这里,您正在向堆栈上的块传递指针。一旦当前的堆栈帧超出范围,您的块就会消失。您可以将副本传递给块,使调用者成为块的所有者,也可以将块复制到接收器中。
如果您希望调用者拥有该块,则必须在调用者中保留对该块的强引用(例如,作为属性)。一旦调用者被解除分配,您将失去强引用,并且您的弱引用将设置为nil。
答案 2 :(得分:0)
感谢您的回答,我意识到我对如何管理块有点不满意。我用一种不同的方法解决了这个问题,灵感来自Mike Ash's KVO的实现与块和&自动解除引用,以及xlc关于在dealloc
中执行此操作的建议。
方法与此类似(如果您不想阅读整个gist):
on:event do:block with:caller
dealloc
添加一个块,从发射器中移除我不知道它是否安全使用,到目前为止我只在虚拟应用程序中的单个线程上测试过它。