C语言 块被释放时回调

zysjyyx4  于 2023-03-07  发布在  其他
关注(0)|答案(3)|浏览(121)

我在Obj-C运行时从C调用可可。
我可以用这里的信息[1]创建块对象,并将它们作为参数传递给Cocoa方法,Cocoa方法根据需要保留它们,并在不再需要时释放它们。问题是,当块达到refcount 0并被释放时,我需要释放与块关联的其他资源,所以我需要一种方法来设置回调函数。
对于普通对象,我只需要子类化并覆盖dealloc()。我听说块也是对象--有没有可以子类化的Block类?或者有没有其他方法在释放和/或dealloc块时挂接函数?
谢谢。
[1][http://clang.llvm.org/docs/Block-ABI-Apple.html](http://clang.llvm.org/docs/Block-ABI-Apple.html)

sg2wtvxw

sg2wtvxw1#

你可以使用Obj-C Associated Objects API来关联一个对象示例和一个块示例。当块被释放时,关联的对象将被释放(如果它没有在其他地方被访问)。
使用关联对象的-dealloc方法执行任何所需的资源清理等。

brccelvz

brccelvz2#

扩大我的评论:
我假设你用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
}

您需要一个查找表来存储从块地址到辅助地址的Map,例如NSMapTable
高温加热

增编

正如我的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));
}
jtw3ybtb

jtw3ybtb3#

好吧,我是这么解决的。
首先,我创建了一个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

我为每个创建的块保存一个refcount,从1开始,以my_copy_callback递增,以my_dispose_callback递减,当refcount达到0时,与该块关联的资源被释放。
注:复制/释放助手不会在像NSString的enumerateLinesUsingBlock这样的同步方法上调用,因为这些方法在使用块时不会保留/释放块,因为它们假设块在调用期间保持可用。像dispatch_async()这样的异步方法调用helper。在同一个块上多次调用dispatch_async()应显示refcount递增两次,然后递减。

相关问题