【问题标题】:Handling Callbacks处理回调
【发布时间】:2008-11-25 10:03:58
【问题描述】:

我在 Objective-C 类中有一个方法。它有 2 个用 C 编写的回调函数。类指针,即self,作为void * 传递给这些函数。在 C 函数中,我创建了一个 class 类型的指针并分配了 void * 参数。 第一个回调函数执行成功。但是void * 指针在第二个回调函数中变成了nil。请注意,我没有在第一个回调中调整指针,但在第二个回调中我仍然得到 nil

有什么想法可能会出错吗?

例如:

kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
                                      matchingDict, RawDeviceAdded, NULL,
                                      &gRawAddedIter);

RawDeviceAdded(NULL, gRawAddedIter, self);

这很好用。但下面的函数接收selfnil

kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification,
                                      matchingDict, BulkTestDeviceAdded, NULL,
                                      &gBulkTestAddedIter);

BulkTestDeviceAdded(NULL, gBulkTestAddedIter, self);

【问题讨论】:

  • 如果您可以编辑以添加显示您正在尝试执行的操作的代码,这将更容易提供帮助。

标签: objective-c cocoa


【解决方案1】:

您的问题是否与 IOKit 回调例程有关?您给出的具体示例的问题是 IOServiceMatchingCallback 只接受 2 个参数,而不是 3 个。您需要 RawDeviceAdded() 和 BulkTestDeviceAdded() 回调函数来匹配 IOServiceMatchingCallback 原型并接受 self 作为第一个参数(refCon),而不是第 3 个。此外,您需要将 self 作为 IOServiceAddMatchingNotification() 的倒数第二个参数传入,以便通过回调将其传回给您。

在 Objective-C 代码中处理 C 回调的一种常用方法是拥有一个将回调转发到您的实例的静态函数。因此,您的示例回调代码如下所示:

static RawDeviceAdded(void* refcon, io_iterator_t iterator)
{
    [(MyClass*)refcon rawDeviceAdded:iterator];
}

@implementation MyClass
- (void)setupCallbacks
{
    // ... all preceding setup snipped
    kr = IOServiceAddMatchingNotification(gNotifyPort,kIOFirstMatchNotification, matchingDict,RawDeviceAdded,(void*)self,&gRawAddedIter );
    // call the callback method once to 'arm' the iterator
    [self rawDeviceAdded:gRawAddedIterator];
}
- (void)rawDeviceAdded:(io_iterator_t)iterator
{
    // take care of the iterator here, making sure to complete iteration to re-arm it
}
@end

【讨论】:

    【解决方案2】:

    通常,Objective-C 中的回调是通过传递一个委托对象和一个选择器来处理该委托的。例如,此方法将在记录消息后调用其委托的方法,同时传递自身和记录的消息。

    - (void)logMessage:(NSString *)message
              delegate:(id)delegate
        didLogSelector:(SEL)didLogSelector
    {
        NSLog(@"%@", message);
    
        if (delegate && didLogSelector && [delegate respondsToSelector:didLogSelector]) {
            (void) [delegate performSelector:didLogSelector
                                  withObject:self
                                  withObject:message];
        }
    }
    

    你可以用这样的代码来调用它:

    - (void)sayHello
    {
        [logger logMessage:@"Hello, world"
                  delegate:self
            didLogSelector:@selector(messageLogger:didLogMessage:)];
    }
    
    - (void)messageLogger:(id)logger
            didLogMessage:(NSString *)message
    {
        NSLog(@"Message logger %@ logged message '%@'", logger, message);
    }
    

    您也可以直接使用objc_msgSend(),尽管您需要充分了解Objective-C 运行时以选择使用哪个变体以及如何构造原型和调用它的函数指针。 (这是在 Objective-C 中实际实现消息发送的机制——编译器通常会生成调用以表示 [] 表达式。)

    【讨论】:

    • 抱歉 - 在发布代码之前,我认为问题是关于如何编写在 Objective-C 中实现回调系统的代码,而不是关于使用 C 回调。
    【解决方案3】:

    这就是 Objective-C 的选择器的用途: http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/NSInvocationOperation_Class

    API 不是很直观,但是一旦你理解它就很好了

    您可能还需要进行一些重构,现在可能有更好的方法,但是当我遇到这个问题时,我的解决方案是重构并使用 InvoationOperation。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-26
      • 2017-11-06
      • 2015-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多