iOS的Object-C是一门动态语言,在调用对象的方法时,是通过向对象发送消息的形式进行实现,在编译阶段并不会判断发送对象的方法是否存在,而是在代码运行的时候进行的判断。在正常情况下,调用一个对象的没有实现的方法,会导致程序发生崩溃消息,提示找不到该对象的实现方法。Object-C语言的动态特性基于Runtime系统实现,我们可以通过Runtime的一些公共的函数进行一些特殊操作。如给系统的类动态添加属性,调换类的实现方法等。我在这里简单介绍一些我们可以常用的Runtime方法。
1、动态添加属性
当我们需要给系统的类或者第三方库的类添加属性时,我们可以使用Runtime进行实现。主要通过两个方法进行关联属性的设置和获取,方法如下:
//设置对象的关联属性
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
//获取对象的关联属性
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
实例代码如下:
- (void)setWeight:(float)weight {
objc_setAssociatedObject(self, "weight", @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (float)getWeight {
return [objc_getAssociatedObject(self, "weight") floatValue];
}
2、动态方法解析
当我们的需求存在需要调用方法1,但是方法1又没有实现时,我们可以使用动态添加方法的方式进行实现。当消息发送的对象的方法没有实现时,系统会调用对象的+ (BOOL)resolveClassMethod:(SEL)sel方法或者+ (BOOL)resolveInstanceMethod:(SEL)sel方法进行处理,我们实现这个两个方法及可拦截方法的调用的第一步,在获取到我们需要动态添加的方法时,此时进行动态添加即可,当我们返回YES时,代表解决了方法不存在的文图,代码如下:
void functionMethod1(id obj, SEL _cmd) {
NSLog(@"Doing functionMethod1 %@",obj);
}
void classFunctionMethod1(id obj, SEL _cmd) {
NSLog(@"Doing classFunctionMethod1 %@",obj);
}
/*********************1、动态方法解析******************************/
//处理类方法
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(class_function1)) {
class_addMethod(object_getClass(self), sel, (IMP)classFunctionMethod1, "v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
//处理实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(function1)) {
class_addMethod([self class], sel, (IMP)functionMethod1, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
代码通过判断function1和class_function1方法来进行拦截,进把方法实现指向functionMethod1函数和classFunctionMethod1函数。此时会调用functionMethod1函数和classFunctionMethod1函数来替代function1和class_function1方法的实现。阻止了程序的崩溃。
3、重定向消息接收者
当我们调用某个对象的某个方法时,该方法又没有实现时,可以对该方法进行重新指定调用对象,也就是发送消息的对象主角。当方法消息发送给另外一个对象后,只要另外一个对象实现该方法,则程序会继续运行下去,以此避免因为不存在的方法而导致程序崩溃。代码如下:
/*********************2、重定向消息接收者******************************/
//处理类方法
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(class_function2)) {
return [ESCPersonModel class];
}
return [super forwardingTargetForSelector:aSelector];
}
//处理实例方法
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(function2)) {
return [ESCPersonModel new];
}
return [super forwardingTargetForSelector:aSelector];
}
4、重定向方法接收着和方法名
当我们通过第3步的重定向消息接受者没有对消息进行处理时,会进入最后一步,再次确认消息及消息接收者,此时方法消息及消息接收者被封装成NSInvocation对象,我们更改NSInvocation对象下的消息签名及消息接收对象即可避免程序崩溃的产生。代码如下:
/*********************3、重定向消息接收者和方法******************************/
//处理类方法
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"class_function3"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
}
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
if (sel_isEqual(sel, NSSelectorFromString(@"class_function3"))) {
anInvocation.selector = NSSelectorFromString(@"class_function2");
Class p = [ESCPersonModel class];
if([p respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:p];
}else {
[self doesNotRecognizeSelector:sel];
}
}
}
//处理实例方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"function3"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
if (sel_isEqual(sel, NSSelectorFromString(@"function3"))) {
anInvocation.selector = NSSelectorFromString(@"function2");
ESCPersonModel *p = [ESCPersonModel new];
if([p respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:p];
}else {
[self doesNotRecognizeSelector:sel];
}
}
}
demo地址:https://github.com/XMSECODE/ESCRuntimeDemo
runtime详解参考:https://www.jianshu.com/p/d4b55dae9a0d
网友评论