iOS之objc_direct

780 阅读2分钟

它为Objective-C方法添加了一个新的直接派发机制(direct dispatch mechanism)。

Direct Methods 直接方法

直接方法具有常规方法的外观,但是具有C函数的行为。 当直接方法被调用时,它直接调用它的底层实现,而不是通过objc_msgSend

@interface KryTestPerson : NSObject
- (void)directMethod __attribute__((objc_direct));
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    
    KryTestPerson * person = [[KryTestPerson alloc]init];
    [self printClassMethodName:person];
}

- (void)printClassMethodName:(KryTestPerson*)person{
    unsigned int count;
    Method *methods = class_copyMethodList([KryTestPerson class], &count);
    for (int i = 0; i < count; i++)
    {
        Method method = methods[i];
        SEL selector = method_getName(method);
        NSString *name = NSStringFromSelector(selector);
        NSLog(@"%@",name);
    }
}
打印结果为 init 

我们可以得知,__attribute__((objc_direct))修饰的方法并不会放在类的方法列表里面.

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

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

如果交换 __attribute__((objc_direct))修饰的方法会发生什么?

 - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
 
    SEL directMethodSEL = NSSelectorFromString(@"directMethod");
    
    Method directMethod = class_getInstanceMethod([KryTestPerson class], directMethodSEL);
    
    Method printHelloMethod = class_getInstanceMethod([ViewController class], @selector(printHello));
    
    method_exchangeImplementations(directMethod, printHelloMethod);
    
    KryTestPerson * person = [[KryTestPerson alloc]init];
    [person directMethod];
    
}

- (void)printHello{
    NSLog(@"printHello");
}

打印结果为 directMethod

并没有交换成功,并不会Crash.

direct属性作用的对象

image.png :关于__attribute__((objc_direct_memters))

  • 指定到@implementation上时,那些只在@implementation中定义(不包括在@interface中定义)的方法会成为direct方法
  • 指定到@interface上时,所有方法和属性都会成为direct

编译期检查

  • 不允许重写或被重写的方法指定direct属性

  • 不允许类声明未指定direct但类实现指定了direct

    • 允许类声明指定但类实现未指定
  • 不允许protocol指定的方法指定direct属性

  • 不允许对id类型的对象调用direct方法

  • 不允许对direct方法使用@selector(...)

实施此功能,主要好处是减少了代码大小。 据报道,未使用的Objective-C元数据在编译后的二进制代码中多5 - 10%的比例。

一些工程师可以遍历每个SDK框架,使用objc_direct注释私有方法,并使用objc_direct_members注释私有类,这是一种逐步瘦身SDK的轻量级方法。