面试OC 底层

Objective-C Direct Methods

2019-12-29  本文已影响0人  iOS_小久

动态调度与目标C运行时

在Object-C中,程序由一组对象组成,这些对象通过传递消息来相互交互,而消息又反过来调用。方法或功能。这种消息传递行为由方括号语法表示:

[someObject aMethod:withAnArgument];

在编译Object-C代码时,将消息发送转换为调用一个名为objc_msgSend(字面上“向带有参数的对象发送消息”).

objc_msgSend(object, @selector(message), withAnArgument);

objc_msgSend负责确定响应此消息调用哪个底层实现,该流程称为方法调度.

在目标C中,每个类(Class)维护一个调度表来解析在运行时发送的消息。调度表中的每个条目都是一个方法(Method)键选择器(SEL)相应的实现(IMP),它是指向C函数的指针。当一个对象收到消息时,它会查询其类的调度表。如果可以找到选择器的实现,则调用关联的函数。否则,对象引用其超类的调度表。这继续在继承链上进行,直到找到匹配项或根类(NSObject)认为选择器不被识别。

如果您的代码中有一条热门路径,这是一种频繁调用的昂贵方法,那么您可以想象避免所有这些间接操作的好处。为此,一些开发人员使用C函数作为一种绕过动态分派的方法。

具有C函数的直接调度

就像我们看到的objc_msgSend,任何方法调用都可以通过传递隐式表示为等效函数。self作为第一个论点。

例如,考虑使用常规的动态分派方法的Object-C类声明。

@interface MyClass: NSObject
- (void)dynamicMethod;
@end

如果开发人员希望在MyClass不需要查看发送shebang的整个消息,他们就可以声明一个静态C函数,该函数的实例是MyClass作为一种争论。

static void directFunction(MyClass *__unsafe_unretained object);

下面是这些方法中的每一种方法如何转换到呼叫站点:

MyClass *object = [[[MyClass] alloc] init];

// Dynamic Dispatch
[object dynamicMethod];

// Direct Dispatch
directFunction(object);

直接法

A 直接法具有传统方法的外观和感觉,但具有C函数的行为。当一个直接方法被调用时,它直接调用它的底层实现,而不是遍历。objc_msgSend

使用这个新的LLVM修补程序,您现在可以对Object-C方法进行注释,以避免有选择地参与动态分派。

objc_directive、@Property(直接)和objc_Direct_Members

若要直接使用实例或类方法,可以将其标记为objc_direct`` clang属性...同样,通过使用direct属性属性

@interface MyClass: NSObject
@property(nonatomic) BOOL dynamicProperty;
@property(nonatomic, direct) BOOL directProperty;

- (void)dynamicMethod;
- (void)directMethod __attribute__((objc_direct));
@end

根据我们的统计,direct使.的总数@property属性为16:

  • gettersetter
  • readwritereadonly,
  • atomicnonatomic
  • weak, strong copy, retain,和unsafe_unretained
  • nullable, nonnullable,和null_resettable
  • class

@interface对于类别或类扩展,则使用objc_direct_members属性中包含的所有方法和属性声明都被认为是直接的,除非该类先前声明过。

控件不能对主类接口进行注释。objc_direct_members属性。

__attribute__((objc_direct_members))
@interface MyClass ()
@property (nonatomic) BOOL directExtensionProperty;
- (void)directExtensionMethod;
@end

注释@implementation带着objc_direct_members具有类似的效果,导致非先前声明的成员被认为是直接的,包括任何由属性合成产生的隐式方法。

__attribute__((objc_direct_members))
@implementation MyClass
- (BOOL)directProperty {…}
- (void)dynamicMethod {…}
- (void)directMethod {…}
- (void)directExtensionMethod {…}
- (void)directImplementationMethod {…}
@end

动态方法不能被直接方法在子类中重写,直接方法根本不能被重写。

协议不能声明直接方法需求,类不能用直接方法实现协议需求。

将这些注释应用到前面的示例中,我们可以看到调用站点上的直接方法和动态方法是如何难以区分的:

MyClass *object = [[[MyClass] alloc] init];

// Dynamic Dispatch
[object dynamicMethod];

// Direct Dispatch
[object directMethod];

对于我们中注重性能的开发人员来说,直接方法似乎是一个扣人心弦的特性。但有个转折:

在大多数情况下,直接使用方法可能不会有明显的性能优势。

隐藏动机

当目标-C方法被标记为直接时,它的实现具有隐藏的可见性。也就是说,直接方法只能在同一个模块中调用。(或者是学究,联动单元).它甚至不会出现在Object-C运行时。

隐藏的能见度有两个直接的优点:

没有外部可见性或从Object-C运行时动态调用它们的方法,直接方法实际上是私有方法。

如果您希望参与直接分派,但仍然希望使您的API在外部可访问,则可以将其包装在一个C函数中。

static inline void performDirectMethod(MyClass *__unsafe_unretained object) {
   [object directMethod];
}

另外,如果你想一起进阶,不妨添加一下交流群1012951431,选择加入一起交流,一起学习。期待你的加入!

02.png
上一篇下一篇

猜你喜欢

热点阅读