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];
}
主要逻辑如下:
- 通过sel获取到该对象的方法签名
- 通过方法签名生成
NSInvocation对象 - 为
NSInvocation对象设置消息的接收者及selector - 通过解析方法参数的类型编码, 从可变参数中获取到相应类型的参数值, 然后将解析得到的参数及序号设置给
NSInvocation对象 - 通过
NSInvocation对象发送消息 - 获取消息接收者的返回值, 向调用方返回结果
要点
通过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
}
}
}