了解oc 运行时可分为以下三块:
1.概念 IMP SEL MEthod
2.消息发送流程
3.消息转发流程
概念篇:
SEL:oc在编译时会根据方法名和参数序列生成唯一的方法标识就是SEL
IMP: SEL 对应方法实现
MEthod:在 SEL 和 IMP之间建立映射
消息发送流程:
//第一个参数消息接收者,第二个参数 SEL
OBJC_EXPORT void objc_msgSend(void id self, SEL op, ... )
消息发送到对象时,会根据接收者对象所属类的isa指针在类的cache和methodList找到对应方法的实现,如果没找到就向其父类找,直到NSObject,如果还是没有找到就进入消息转发流程
消息转发流程:
1)动态方法解析:用来给一个类动态添加没有声明和实现的方法
//动态方法解析
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selName = NSStringFromSelector(sel);
if ([selName isEqualToString:@"eat"]) {
class_addMethod(self, sel, (IMP)eat, "V@:");
return YES;
}
return [super resolveInstanceMethod:sel];
objc_msgSend()
}
void eat(){
NSLog(@"-----------------i am eating----------------");
}
2)上一步没有处理运行时调用备用接收者方法:
/*备用接收者,上一步没有处理时runtime会调用该方法 */
//这种方法属于单纯的转发,无法对消息的参数和返回值进行处理
-(id)forwardingTargetForSelector:(SEL)aSelector{
NSString *selName = NSStringFromSelector(aSelector);
if ([selName isEqualToString:@"eat"]) {
return [[Children alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
3)前两步都没有处理,进入消息完整转发流程
/* 为了完整转发,需要重写以下方法 */
//消息转发机制为了创建NSInvocation需要使用这个方法获取信息,重写他为了指定方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString *selName = NSStringFromSelector(aSelector);
if ([selName isEqualToString:@"testMethod"]) {
return [NSMethodSignature signatureWithObjCTypes:"V@:"];
}
return [super methodSignatureForSelector:aSelector];
}
//对象需要创建一个NSInvocation对象,把消息调用的全部细节封装进去,包括selector,target,arguments等参数,还能够对返回结果进行处理
//NSInvocation对象可以做很多处理。比如修改实现方法,修改响应对象
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//改变响应方法
[anInvocation setSelector:@selector(newMethodTest)];
//改变响应对象
[anInvocation invokeWithTarget:[Children new]];
}
//方法参数类型
enum _NSObjCValueType {
NSObjCNoType = 0,
NSObjCVoidType = 'v',
NSObjCCharType = 'c',
NSObjCShortType = 's',
NSObjCLongType = 'l',
NSObjCLonglongType = 'q',
NSObjCFloatType = 'f',
NSObjCDoubleType = 'd',
NSObjCBoolType = 'B',
NSObjCSelectorType = ':',
NSObjCObjectType = '@',
NSObjCStructType = '{',
NSObjCPointerType = '^',
NSObjCStringType = '*',
NSObjCArrayType = '[',
NSObjCUnionType = '(',
NSObjCBitfield = 'b'
} NS_DEPRECATED(10_0, 10_5, 2_0, 2_0);