LLVM 是否会在可能的情况下自动将 Objective-C 方法转换为内联函数?
(即,为一个可以内联粘贴的代码块创建一个 Objective-C 方法是否同样高效?)
如果 LLVM 不执行此优化,为什么不呢?如果是这样,(a)我必须设置某些构建设置才能发生这种情况吗?(b) 如何判断一个 Objective-C 方法是否会被内联?
3 回答
No, because its impossible to know in the context of the Obj-C runtime if those kind of optimizations can be performed. The thing to remember is that Obj-C methods are invoked by a message send, these messages can come from more than just the [myObject doSomething]
syntax.
Consider [obj performSelector:NSSelectorFromString(@"hello")]
the fact that this can happen means that it would be impossible to ever inline any method.
There is also a chain of events that happens when a message is received by a class, these events can reroute, or even change the message that is being sent. This happens transparently underneath the message send.
No. It is an essential feature of Objective-C that message dispatch (remember that in Obj-C you send a message, you don't call a method) happens dynamically at runtime, not at compile time.
Because of this, a message dispatch in Obj-C will always be a little slower than a pure function call (even if the function is not inlined).
让我们假设编译器内联了一个方法:
@implementation AwesomeClass
- (void)doFoo OBJC_INLINE { // or some way to indicate "this is an inline method"
NSLog(@"doing foo!");
}
- (void)doBar {
[self doAwesomeStuff];
[self doFoo];
}
@end
这样-doBar
基本上就变成了:
- (void)doBar {
[self doAwesomeStuff];
{
NSLog(@"doing foo!");
}
}
太棒了,这似乎会更快,对吧?我们通过不调用来节省一打指令objc_msgSend
。因此,您将其打包并作为 .a 文件在线发布。
NSCleverCoder 出现并说“但我想做doFoo
更多”,所以他这样做了:
@interface SuperAwesomeClass : AwesomeClass @end
@implementation SuperAwesomeClass
- (void)doFoo {
NSLog(@"doing more foo!");
[super doFoo];
}
@end
当他尝试运行它时,它永远不会被调用,因为AwesomeClass
从未真正调用过该-doFoo
方法。
“但是,”你说,“这是一个人为的例子!”
不,这不对。在 Objective-C 中,在应用程序的开发或执行的任何时候这样做都是完全合法的。我可以在编写代码时做到这一点。哎呀,我什至可以在运行时通过使用objc_allocateClassPair
和class_addMethod
动态创建子类并添加方法覆盖来做到这一点。
我还可以调配方法实现。不喜欢现有的-doFoo
? 这很酷; 用你自己的替换它。等一下; 如果该方法是内联的,那么您的新实现将永远不会被调用,因为-doBar
实际上从未调用该-doFoo
方法。
我唯一能看到这是可能的情况是,是否有某种方法可以将方法注释为不可覆盖。但是没有办法做到这一点,所以这个问题没有实际意义。即便如此,这仍然是个坏主意;仅仅因为编译器不允许你这样做并不意味着你仍然不能在运行时解决它。再一次,你会遇到问题。