【问题标题】:"Converting" a function pointer to a block in objective-C将函数指针“转换”到objective-C中的块
【发布时间】:2012-12-17 11:50:14
【问题描述】:

我正在做一些从 Mono C# 到 Obj-C 的互操作并遇到了这个问题。 C# 代码需要传递一个回调——它使用函数指针来完成。 我可以从 Obj-C 端获取函数指针并调用它,一切正常。 但是我现在需要将该函数指针作为回调提供给第三方 API,该 API 使用块作为回调。 我希望第三方调用 C# 函数 - 所以在某种程度上我试图将函数指针转换为块以便第三方可以运行它,或者制作某种桥梁 - 创建我自己的运行块该函数指针并将其提供给第三方。我似乎无法找到一种方法来做到这一点 - 我将如何生成一个包含要运行哪个函数的信息的块,然后将其提供给第三方。 也许我还有其他选择?

编辑:将函数放在全局变量中可能会起作用,但我希望能够拥有多个这样的函数,因为第三方 API 是异步的,我不希望它调用错误的回调。

我试过的代码:

typedef void (*DummyAction)(char * result);
typedef void (^DummyBlock)(char * result);

@interface FunctionToBlock : NSObject
{
    DummyAction function;
    DummyBlock block;
}

- (id) initWithFunction: (DummyAction) func;
- (DummyBlock) block;
@end

@implementation FunctionToBlock : NSObject
- (id) initWithFunction: (DummyAction) func {
    if (self = [super init]) {
        function = func;
        block = ^(char * result) {
            function(result);
        };
    }
    return self;
}

- (DummyBlock) block {
    return block;
}
@end

然后我运行它

void RegisterCallback( char * text, DummyAction callback)
{
    FunctionToBlock *funcToBlock = [[FunctionToBlock alloc] initWithFunction : callback];
    funcToBlock.block(text);
}

它会因 BAD_ACCESS 而失败。也许我做错了什么,因为我对 Obj-C 还不是很精通。如果直接运行,我可以确认回调是正常的,并且正在调用块,但它在函数(结果)行上失败。

【问题讨论】:

    标签: objective-c block function-pointers objective-c-blocks


    【解决方案1】:

    怎么样

    void (*myFunc)(int x); // ... your function pointer
    
    void (^myBlock)(int) = ^(int x) {
        myFunc(x);
    };
    

    那么myBlock就是一个block,捕获函数指针的值,在block执行的时候调用函数。


    添加:我的建议,基于您的代码,使用 @property(并假设您使用 ARC 编译):

    FunctionToBlock.h:

    typedef void (*DummyAction)(char * result);
    typedef void (^DummyBlock)(char * result);
    
    @interface FunctionToBlock : NSObject
    {
        DummyAction function; // Not really needed.
    }
    
    - (id) initWithFunction: (DummyAction) func;
    @property(copy, nonatomic) DummyBlock block;   // "copy" is important here!
    
    @end
    

    FunctionToBlock.m:

    #import "FunctionToBlock.h"
    
    @implementation FunctionToBlock : NSObject
    @synthesize block = _block; // Can be ommitted if you use Xcode 4.4 or later.
    
    - (id) initWithFunction: (DummyAction) func
    {
        if (self = [super init]) {
            function = func; // Not really needed.
            self.block = ^(char * result) {
                func(result); // Use "func", not "self->function", to avoid retain cycle.
            };
        }
        return self;
    }
    

    【讨论】:

    • 全局变量?那不是很好,因为那时我不能其中几个。我尝试将 myBlock 作为成员与 myFunc 一起放在一个类中,但是当我尝试从 myBlock 运行 myFunc 时,它可能不在其范围内,并且由于 BAD_ACCESS 而失败。我猜全局变量将是最后的手段,但我更喜欢非全局方法
    • @Amitloaf:我不想建议一个全局变量。 myFunc 可以是任何函数指针。分配块时在块中捕获该值。
    • 我添加了一些关于我如何尝试实施类似您建议的内容的更多信息。想知道如何解决它,谢谢!
    • @Amitloaf:阻止必须复制创建它们的范围内的“寿命”。试试block = [^(char * result) { function(result); } copy];,这似乎有效。或者,您可以将其声明为 @property(copy, nonatomic) DummyBlock block;
    • 效果很好!谢谢!那就是我所缺少的 :) 虽然我无法获得 @property 替代编译,但正常的副本有效!
    【解决方案2】:

    为什么不只是一个简单的功能

    typedef void (*DummyAction)(char * result);
    typedef void (^DummyBlock)(char * result);
    
    DummyBlock functionToBlock(DummyAction func) {
        return [[^(char * result) {
                     func(result);
                 } copy] autorelease];
    }
    

    【讨论】:

    • 嗯,你是对的。出于某种原因,我没有想到这一点。工作得很好而且很干净。谢谢!
    【解决方案3】:

    块在底层是一个指向本地数据结构的指针。一旦您离开声明它的范围,一个块就会变得无效。范围是 init 中的 if 语句;一旦你离开,这个块就无效了。

    您在这里以一种糟糕的方式违反了编码约定。首先,实例变量应该以下划线开头,这样每个人都可以看到你在做什么。最好在不声明实例变量的情况下使用属性。并且每个块属性都应该声明为“副本”。如果你这样做,一切都很好。

    【讨论】:

    • First, instance variables should start with an underscore, so that everyone sees what you are doing. 注意_inherited from C 后面的内容,注意_<upper case letter> 在任何范围内都是保留的,__<anything> 在任何范围内都是保留的。
    猜你喜欢
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2013-11-03
    • 2010-12-19
    • 1970-01-01
    相关资源
    最近更新 更多