【问题标题】:ARC, between block and C callbackARC,在块和 C 回调之间
【发布时间】:2015-09-07 15:15:32
【问题描述】:

我有一个将回调函数作为参数的 c 函数,我将编写一个 Obj-C 包装器到标准 C API。我想用块替换 C 回调。

让我们想象一个 C API:

void audioStopFunction(void (*callback)(void *), void *udata);

Obj-C 包装器如下所示:

- (void)myAudioStopFunction:(dispatch_block_t)block
{
    void *udata = (__bridge void *)block;
    audioStopFunction(my_callback, udata);
}

void my_callback(void *udata)
{
    dispatch_block_t block = (__bridge_transfer dispatch_block_t)udata;
    block();
}

现在,我有一些问题:

  1. myAudioStopFunction 函数中,我是否需要复制如下块:

    void *udata = (__bridge void *)[块复制];

  2. my_callback 函数中,我应该使用__bridge 而不是__bridge_transfer?还有,我需要在block() 之后拨打Block_release 吗?

  3. 代码会导致内存泄漏吗?如果是,那么正确的方法是什么?

【问题讨论】:

  • 您是否仅从myAudioStopFunction 拨打audioStopFunction?换一种方式问:实现myAudioStopFunction的类是C回调的(单一)所有者吗?
  • 是的,它是 c 回调的所有者。 myAudioStopFunction在一个类中,这个类是一个singletion类。

标签: objective-c c


【解决方案1】:

由于 C 回调由包装类管理,因此最容易让类管理块的所有权。 (我将块称为 completionBlock,因为这似乎更符合 Cocoa 命名约定。)

@interface AudioCallbackWrapper
@property (nonatomic) dispatch_block_t completionBlock;
@end

static void my_callback(void *udata)
{
    dispatch_block_t block = (__bridge dispatch_block_t)udata;
    if (block != nil)
        block();
}

@implementation AudioCallbackWrapper
- (void)setCompletionBlock:(dispatch_block_t)completionBlock
{
    _completionBlock = [completionBlock copy];
    audioStopFunction(my_callback, (__bridge void *)_completionBlock);
}
- (void)dealloc
{
    // remove the C callback before completionBlock is released
    self.completionBlock = nil;
}
@end

由于块的生命周期由封闭的包装器管理,C 代码永远不必转移所有权。所以代码只包含__bridgevoid * 之间的转换。

代码会导致内存泄漏吗?如果是,那么正确的方法是什么?

每次回调触发时,您的原始代码都会释放块。块指针会在第一次回调后悬空。

【讨论】:

  • 鉴于这是一个单镜头对象,我认为您应该在初始化程序中传递完成块并将属性设为只读。此外,使用 ARC,您的 dealloc 方法是多余的。
  • @JeremyP 能够更改回调可能仍然很有价值。此外,即使使用 ARC,dealloc 也会执行必要的清理,并且绝对不是多余的:ARC 只会释放 completionBlock,但保留 C 回调参数。
  • @JeremyP 不过,将包装类设为单例可能是个好主意,因为 C 回调似乎是全局的。那么dealloc 确实是多余的。
  • 不应该用复制语义声明块吗:@property (nonatomic, copy) dispatch_block_t completionBlock;?
  • @NikolaiRuhe C 回调是全局的,但块不是。如果您将对象设为单例,则您永远无法更改回调,或者至少您必须确保在更改之前完成对 audioStopFunction 的先前调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 2012-07-27
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多