【问题标题】:Invoke selector with scalar argument使用标量参数调用选择器
【发布时间】:2011-02-05 01:17:03
【问题描述】:

我目前使用objc_msgSend 在对象集合上调用此类选择器。有没有更好的方法来做到这一点?这是我的代码:

@protocol ADelegateProtocol {
   -(void) timeToEventOneDidChange:(NSInterval) event1;
   -(void) timeToEventTwoDidChange:(NSInterval) event1;
}

- (void) delegatesPerformSelector:(SEL) selector withTimeIntervalAsFristParameter:(NSTimeinterval) timeInterval {
    for (id<ADelegateProtocol> delegate in delegates) {
        if([delegate respondsToSelector:selector]) {
            objc_msgSend(delegate, selector, timeInterval);
        }
    }
}

选择器作为参数传入,timeInterval是非对象值。

注意:我不想使用 KVO。

【问题讨论】:

  • 请编辑问题以添加选择器作为参数传递。这有很大的不同。
  • 我认为如果不更改您要调用的方法,它不会真正变得更好,例如NSInvocation 比较繁琐。
  • 如果我遗漏了什么,请见谅,但此代码假定传递给此方法的任何选择器都将timeInterval 作为参数。如果您知道参数将是什么,为什么不知道选择器将是什么?例如,可能会传入一个选择器forBar:,它接受一个NSTimeInterval 作为参数,但是如果我传入一个接受字符串的fooBaz:respondsToSelector 仍然会返回true,但你会发送一个时间预期字符串并导致崩溃的间隔...
  • @Jasarien:我认为这些方法可能类似于setFooTimeStampsetBarTimeStamp 等。但最好是 Piotr 能澄清一下。
  • 这一切都很好,但是如果传入任何其他带有一个代表可能响应的参数的选择器,它不会阻止程序崩溃。传递setFooTimeStamp:setBarTimeStamp: 将是很好,但是为了争论,NSObject 或其任何子类将响应awakeAfterUsingCoder:,如果传入时间戳,它将崩溃。我想说的是这段代码在受控情况下可以正常工作,但并不安全。

标签: objective-c ios


【解决方案1】:

如果您要使用objc_msgSend(),您必须创建一个正确的类型转换函数指针才能这样做。依靠可变参数映射到非可变参数并不适用于所有情况。

即你想要:

void (*myMessage)(id, SEL, NSTimeInterval) = objc_msgSend;
myMessage(delegate, aSelector, aTimeInterval);

(输入 SO - 将语法视为近似值。:)

【讨论】:

  • 有趣。你能告诉我为什么上面的代码更好吗?我检查了两个版本,xcode 产生了非常相似的汇编代码。
  • 这是唯一的区别。你的版本:mov -0x14(%ebp),%eaxcall *%eax,原电话:call 0x13dcfe &lt;dyld_stub_objc_msgSend&gt;
  • 因为 ABI 的细节会随着时间的推移而改变(除了二进制兼容性问题),并且因为它可以在不同的架构之间变化。依赖可变参数就像非可变参数一样经常可以侥幸逃脱,但它不正确、不具有防御性或可移植性。
  • +1 我不得不搜索一下,但我明白你的意思。如果有人对此感兴趣,这里是一个很好的简报:stackoverflow.com/questions/2573805/…
【解决方案2】:

除了objc_msgSend(当然可以)之外,您可以使用NSInvocation。就我个人而言,我更喜欢objc_msgSend 方式,因为它是最省钱的方式。它也是更快的方式,但这在普通应用程序中应该无关紧要(在游戏中确实很重要)。

好吧,选择权在你,两种方式都可以,objc_msgSendNSInvocation 没有什么不好(除了 C 代码在 ObjC 方法中看起来错误)。

【讨论】:

    【解决方案3】:

    如果 delegates 是一个 NSArray:那么使用 NSArray 的 makeObjectsPerformSelector:(SEL)aSelector.
    如果需要将对象作为参数传递,可以使用makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject

    【讨论】:

    • 如果我正确理解了这个问题,timeInterval 是一个非对象值,因此不能轻易作为id 传递。
    • 你可以把它装在一个对象中,例如使用NSNumber
    • 这将失败,因为接收者不会派发 NSNumber,整个方法必须设计为接受 NSNumber。
    猜你喜欢
    • 2012-03-31
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-10
    • 2015-01-15
    相关资源
    最近更新 更多