YYKit 源码解析Foundation (一) NSObject

940 阅读4分钟

YYKit 源码解析Foundation (一) NSObject

这篇文章将详细介绍YYKit的NSObject分类中各主要接口的实现细节, 包括NSInvocation, 可变参数, 类型编码等.

源码概要

- (id)performSelectorWithArgs:(SEL)sel, ... {
  NSMethodSignature *sig = [self methodSignatureWithSelector:sel];
  if (!sig) {
    [self doesNotRecognizeSelector:sel];
    return nil;
  }
  NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
  if (!inv) {
    [self doesNotRecognizeSelector:sel];
    return nil;
  }
  [inv setTarget:self];
  [inv setSelector:sel];
  va_list args;
  va_start(args, sel);
  [NSObject setInv:inv withSig:sig andArgs:args];
  va_end(args);
  [inv invoke];
  return [NSObject getReturnFromInv:inv withSig:sig];
}

主要逻辑如下:

  1. 通过sel获取到该对象的方法签名
  2. 通过方法签名生成NSInvocation对象
  3. NSInvocation对象设置消息的接收者及selector
  4. 通过解析方法参数的类型编码, 从可变参数中获取到相应类型的参数值, 然后将解析得到的参数及序号设置给NSInvocation对象
  5. 通过NSInvocation对象发送消息
  6. 获取消息接收者的返回值, 向调用方返回结果

要点

通过NSInvocation发送消息

NSInvocation封装了消息的接收者, selector, 参数, 返回值, 在发送消息时, 对参数的个数没有限制.

用法:

// 1. 获取方法签名
NSMethodSignature *sig = [self methodSignatureWithSelector:sel];
// 2. 通过方法签名生成NSInvocation对象
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
// 3. 设置消息的接收者
[inv setTarget:self];
// 4. 设置selector
[inv setSelector:sel];
// 5. 设置参数
int arg = va_arg(args, int);
[inv setArgument:&arg atIndex:index];
// 6. 发送消息
[inv invoke];
// 7. 如果有返回值, 就获取返回值
int ret;
[inv getReturnValue:&ret];

可变参数

VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数

用法:

// 1. 定义va_list变量, 该变量是指向可变参数的指针
va_list args;
// 2. 用va_start宏初始化变量args, 第二个参数是可变参数的前一个参数值, 可变参数只能是函数的最后一个参数
va_start(args, sel);
// 3. 通过va_arg返回可变参数, 第二个参数是 返回参数 的类型, 如果可变参数中包含多个参数, 则通过多次调用va_arg来获取对应的参数, 每次调用va_arg后, args指针会自动指向下一个参数
va_arg(args, int);
// 4. 用va_end宏结束可变参数的获取
va_end(args);

类型编码

参考文档

进程间通讯

参数修饰符 in out inout const byref bycopy oneway, 当在进程间通讯时, 方法的参数会用到这些修饰符

in : 参数由客户端设置,即客户端传入参数值

out : 参数由服务端设置,即服务端返回值

inout : 客户端输入端都可以设置,即双向都可传入值

oneway : 被oneway修饰了的方法不可以有返回值,也不可以有带out或inout的参数

用法:

+ (void)setInv:(NSInvocation *)inv withSig:(NSMethodSignature *)sig andArgs:(va_list)args {
  // 获取参数个数
    NSUInteger count = [sig numberOfArguments];
  // 第一个参数是self, 第二个参数是_cmd, 所以我们定义的方法参数是从第三个开始的
    for (int index = 2; index < count; index++) {
      // 获取参数的类型编码
        char *type = (char *)[sig getArgumentTypeAtIndex:index];
      // 当参数修饰符是const, in, out, inout, byref, bycopy, oneway时, 则略过该符号前缀.
      // 如 in char s; 该类型编码为 nc, 此时忽略符号n
        while (*type == 'r' ||
               *type == 'n' ||
               *type == 'N' ||
               *type == 'o' ||
               *type == 'O' ||
               *type == 'R' ||
               *type == 'V') {
            type++;
        }
        
        BOOL unsupportedType = NO;
        switch (*type) {
            case 'v':
            case 'B':
            case 'c':
            case 'C':
            case 's':
            case 'S':
            case 'i':
            case 'I':
            case 'l':
            case 'L':
            {
                int arg = va_arg(args, int);
                [inv setArgument:&arg atIndex:index];
            } break;
            case 'q':
            case 'Q':
            {
                long long arg = va_arg(args, long long);
                [inv setArgument:&arg atIndex:index];
            } break;
            case 'f':
            {
                double arg = va_arg(args, double);
                float argf = arg;
                [inv setArgument:&argf atIndex:index];
            } break;
            case 'd':
            {
                double arg = va_arg(args, double);
                [inv setArgument:&arg atIndex:index];
            } break;
            case 'D':
            {
                long double arg = va_arg(args, long double);
                [inv setArgument:&arg atIndex:index];
            } break;
            case '*':
            case '^':
            {
                void *arg = va_arg(args, void *);
                [inv setArgument:&arg atIndex:index];
            } break;
            case ':':
            {
                SEL arg = va_arg(args, SEL);
                [inv setArgument:&arg atIndex:index];
            } break;
            case '#':
            {
                Class arg = va_arg(args, Class);
                [inv setArgument:&arg atIndex:index];
            } break;
            case '@':
            {
                id arg = va_arg(args, Class);
                [inv setArgument:&arg atIndex:index];
            } break;
            case '{':
            {
                if (strcmp(type, @encode(CGPoint)) == 0) {
                    CGPoint arg = va_arg(args, CGPoint);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(CGSize))) {
                    CGSize arg = va_arg(args, CGSize);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(CGRect))) {
                    CGRect arg = va_arg(args, CGRect);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(CGVector))) {
                    CGVector arg = va_arg(args, CGVector);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(CGAffineTransform))) {
                    CGAffineTransform arg = va_arg(args, CGAffineTransform);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(CATransform3D))) {
                    CATransform3D arg = va_arg(args, CATransform3D);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(NSRange))) {
                    NSRange arg = va_arg(args, NSRange);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(UIOffset))) {
                    UIOffset arg = va_arg(args, UIOffset);
                    [inv setArgument:&arg atIndex:index];
                } else if (strcmp(type, @encode(UIEdgeInsets))) {
                    UIEdgeInsets arg = va_arg(args, UIEdgeInsets);
                    [inv setArgument:&arg atIndex:index];
                } else {
                    unsupportedType = YES;
                }
            } break;
            case '(':
            {
                unsupportedType = YES;
            } break;
            case '[':
            {
                unsupportedType = YES;
            } break;
            default:
            {
                unsupportedType = YES;
            } break;
        }
        if (unsupportedType) {
            NSUInteger size = 0;
            NSGetSizeAndAlignment(type, &size, NULL);
#define case_size(_size_) \
else if (size <= 4 * _size_) { \
    struct dummy { char tmp[4 * _size_]; }; \
    struct dummy arg = va_arg(args, struct dummy); \
    [inv setArgument:&arg atIndex:index]; \
}
            if (size == 0) { }
            case_size( 1) case_size( 2) case_size( 3) case_size( 4)
            case_size( 5) case_size( 6) case_size( 7) case_size( 8)
            case_size( 9) case_size(10) case_size(11) case_size(12)
            case_size(13) case_size(14) case_size(15) case_size(16)
            case_size(17) case_size(18) case_size(19) case_size(20)
            case_size(21) case_size(22) case_size(23) case_size(24)
            case_size(25) case_size(26) case_size(27) case_size(28)
            case_size(29) case_size(30) case_size(31) case_size(32)
            case_size(33) case_size(34) case_size(35) case_size(36)
            case_size(37) case_size(38) case_size(39) case_size(40)
            case_size(41) case_size(42) case_size(43) case_size(44)
            case_size(45) case_size(46) case_size(47) case_size(48)
            case_size(49) case_size(50) case_size(51) case_size(52)
            case_size(53) case_size(54) case_size(55) case_size(56)
            case_size(57) case_size(58) case_size(59) case_size(60)
            case_size(61) case_size(62) case_size(63) case_size(64)
            else {
                struct dummy { char tmp; };
                for (int i = 0; i < size; i++) {
                    va_arg(args, struct dummy);
                    NSLog(@"YYKit performSelectorWithArgs unsupported type:%s (%lu bytes)", [sig getArgumentTypeAtIndex:index],(unsigned long)size);
                }
            }
#undef case_size
        }
    }
}