我通过Obj-C运行时从C调用Cocoa。
我能够使用来自here [1]的信息创建块对象,并将它们作为参数传递给Cocoa方法,这些方法根据需要保留它们,并在不再需要它们时释放它们。问题是当块到达refcount 0并且被释放时我需要释放与块相关的其他资源,所以我需要一种方法来设置回调时间。
对于普通对象,我只是子类化并覆盖dealloc()。我听说块也是对象 - 是否有可以被子类化的Block类?或者还有其他方法可以在块的释放和/或释放上挂钩函数吗?
感谢。
答案 0 :(得分:1)
您可以使用Obj-C Associated Objects API将对象实例与块实例相关联。当块被释放时,关联的对象(如果在其他任何地方不被访问)将被释放。
使用关联对象的-dealloc
方法执行任何所需的资源清理等。
答案 1 :(得分:0)
扩展我的评论:
我假设您正在使用Clang编译器在C中创建块,如果您自己创建块描述结构,这个想法是相同的,但您可以使用正确的值直接创建结构。
如果您希望在处理某个块时调用清理功能(概述):
if (bObject->flags & BLOCK_HAS_COPY_DISPOSE)
{
// block already has a dispose helper
// save current dispose helper in a lookup table with key the bObject
bObject->descriptor->dispose_helper = function which:
a) uses the lookup table to call the original helper
b) removes the entry from the lookup table
c) calls your cleanup function
}
else
{
// block does not have a dispose helper
bObject->flags |= BLOCK_HAS_COPY_DISPOSE; // set is has helpers
bObject->descriptor->copy_helper = dummy copy function
bObject->descriptor->dispose_helper = dispose function which just calls your cleanup
}
您需要一个查找表来存储从块地址到辅助地址的映射,例如: NSMapTable
。
HTH
<强>附录强>
根据评论中的要求,我的quick'n'dirty测试代码,它只是遵循上面的伪代码。运行它,您应该看到第二个和第三个块被释放,第一个块不是静态文字,不需要处理。
void DummyBlockCopy(void * src,void * dst) { }
void BlockDispose(void *src)
{
printf("BlockDispose %p\n", src);
}
typedef void (*HelperFunction)(void *);
NSMapTable *disposeHelpers;
void BlockDisposeCallExisting(void *src)
{
HelperFunction helper = (__bridge void *)[disposeHelpers objectForKey:(__bridge id)(src)];
if (helper)
{
helper(src);
[disposeHelpers removeObjectForKey:(__bridge id)(src)];
}
printf("BlockDisposeCallExisting %p\n", src);
}
void block_trap_dispose(void *aBlock)
{
BlockObject *bObject = aBlock;
if (bObject->flags & BLOCK_HAS_COPY_DISPOSE)
{
[disposeHelpers setObject:(__bridge id)(void *)bObject->descriptor->dispose_helper forKey:(__bridge id)(aBlock)];
bObject->descriptor->dispose_helper = BlockDisposeCallExisting;
}
else
{
bObject->flags |= BLOCK_HAS_COPY_DISPOSE;
bObject->descriptor->copy_helper = DummyBlockCopy;
bObject->descriptor->dispose_helper = BlockDispose;
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
disposeHelpers = [NSMapTable.alloc initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
capacity:2];
void (^b1)(void) = ^{ printf("hello world\n"); };
printf("b1: %p\n", b1);
b1();
block_trap_dispose((__bridge void *)(b1));
int x = 10;
void (^b2)(void) = ^{ printf("x is %d\n", x); };
printf("b2: %p\n", b2);
b2();
block_trap_dispose((__bridge void *)(b2));
NSObject *anObject = NSObject.new;
void (^b3)(void) = ^{ printf("anObject: %p\n", anObject); };
printf("b3: %p\n", b3);
b3();
block_trap_dispose((__bridge void *)(b3));
}
答案 2 :(得分:0)
好的,这就是我解决它的方法。
首先我创建了一个block_literal
(定义为附加block_descriptor
。)
struct block_descriptor {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct block_literal)
copy_helper_t copy_helper; // IFF (1<<25)
dispose_helper_t dispose_helper; // IFF (1<<25)
};
struct block_literal {
struct block_literal *isa;
int flags;
int reserved;
void *invoke;
struct block_descriptor *descriptor;
struct block_descriptor d; // because they come in pairs
};
这是你应该如何设置字段:
block.isa = _NSConcreteStackBlock //stack block because global blocks are not copied/disposed
block.flags = 1<<25 //has copy & dispose helpers
block.reserved = 0
block.invoke = my_callback_function
block.descriptor = &block.d
block.d.reserved = 0
block.d.size = sizeof(block_literal)
block.d.copy_helper = my_copy_callback
block.d.dispose_helper = my_dispose_callback
我为每个创建的块保留一个引用计数,它从1开始,并在my_copy_callback
中递增,在my_dispose_callback
中递减。当refcount达到0时,与块关联的资源被释放。
注意:不会在NSString的enumerateLinesUsingBlock
之类的同步方法上调用copy / dispose helper,因为这些方法在使用时不保留/释放块,因为它们假设块在持续时间内保持可用状态。呼叫。 OTOH,像dispatch_async()
这样的异步方法会调用助手。在同一个块上多次调用dispatch_async()
应该显示refcount递增两次然后递减。