Objective C类方法与C函数

在开发源代码项目时,我遇到了以下C函数声明和实现:

// FSNData.h NSString *stringForMimeType(MimeType type); @interface FSNData : NSObject // All the expected objective-c property and instance method declarations @end // FSNData.m #import "FSNData.h" // where 'type' is an enum // this does work as expected NSString *stringForMimeType(MimeType type) { switch (type) { case MimeType_image_jpeg: return @"image/jpeg"; case MimeType_image_png: return @"image/png"; default: NSLog(@"ERROR: FSNData: unknown MimeType: %d", type); // do not return "application/octet-stream"; instead, let the recipient guess // http://en.wikipedia.org/wiki/Internet_media_type return nil; } } @implementation // all properties and methods defined in FSData.h implemented as expected @end 

这个例子很容易被重写为类级方法,没有任何问题。 实际上,使用stringFormMimeType() sill无论如何都需要导入FSNData头文件。

看一下Apple文档 ,它只说明:

因为Objective-C基于ANSI C,所以您可以自由地将直接C代码与Objective-C代码混合。 此外,您的代码可以调用非Cocoa编程接口中定义的函数,例如/ usr / include中的BSD库接口。

没有提到C函数何时应该支持Objective-C方法。

我在这一点上可以看到的唯一好处是,调用上面的函数,而不是类方法,将跳过一些Objective-C运行时调用。 在典型的FSNData用例中,这不会给用户带来明显的性能提升(甚至对开发人员来说)*。

在类方法上支持C函数有什么好处(编码风格除外)?

* FSNData用作FSNetworking库的一部分,因此我怀疑在任何应用程序的生命周期中都会有成千上万的网络操作被执行。

简而言之,C(或C ++)实现非常有用:

  • 对于抽象
  • 对于可重用性
  • 制作大中型节目时
  • 在性能关键路径中
  • 对于“内部”实施

在类方法上支持C函数有什么好处(编码风格除外)?

  • ObjC消息传递引入了间接函数调用。 这些是优化器的防火墙。
  • C函数可以轻松地限制访问,而“私有”ObjC实现可以使用ObjC运行时查找,或者意外地被覆盖。
  • 如果未引用C函数,则可以从可执行文件中删除它们,或者可以将它们设置为私有。 如果您编写可重用的代码(并且您应该),这会对您的二进制大小和加载时间产生巨大影响 – 可能会删除未引用/使用的C函数,但会保留ObjC类型和方法(包括它们的所有内容)参考)。 这就是为什么当您仅使用ObjC静态库的一小部分时,您的应用程序的二进制大小可能会显着增长 – 库中的每个objc类都会被保留。 如果该库是C或C ++,那么您可以通过非常小的增长来实现,因为您只需要引用的内容。 使用C和C ++更容易certificate引用的内容或未引用的内容。
  • 无论是在编译期间还是在链接时优化阶段,都可以内联C函数。
  • 编译器和优化器能够使用C函数进行大量优化(例如,程序间优化),但是使用ObjC方法很少,因为它们总是间接的。
  • 避免ObjC消息调度开销(如您所述)
  • 与ObjC对象交互时可能需要额外的引用计数操作和自动释放池活动。

当然,你不会总是因为你不需要或使用的东西而付出代价 – 并且记住ObjC类方法也有一些优于C函数的方法。 因此,只需将C或C ++实现视为工具箱中的另一个工具。 我发现它们非常有用,因为复杂性和项目大小增加了,它们可以用来使你的程序更快。 做你最不可能在2015年后悔的事情;)

您已经触及了避免objc_msgSend调用的边际性能差异。 Objective-C类方法也可以覆盖子类,因此在C中实现一个方法可以防止它在子类中被覆盖。 相关地,由于运行时inheritance/多态,因此永远不能内联Objective-C方法,而编译器可能会内联C函数以增加性能。

当谈到避免使用objc_msgSend ,一位聪明人曾告诉我,“如果objc_msgSend的开销对objc_msgSend来说太大了,那么Objective-C可能就是这项工作的错误工具。”