LuckyHu Blog

Home MyGame MyApp About

JRSwizzle应用

18 Dec 2014

JRSwizzle是什么?

JRSwizzle是github上的一个简单的开源项目,让你可以利用objective-c runtime运行时交换类方法,JRSwizzle的实现很简单,某些情况下其实不需要JRSwizzle的封装,因为比如在我的场景下只是针对objc2.0以上,ios系统。

JRSwizzle的其他工程应用我目前还没怎么见过,貌似objective-c的aop编程是采用的类似实现,在我经历过的ios项目中,一般的应用是改变一些系统类的方法,来防止release版本中得一些崩溃发生,比如NSArray下标越界,NSMutableArray加入一个nil的object,NSMutableDictionary使用setObject:forKey插入一个nil的object等等。以防止NSArray下标越界来说,可以这么处理:

首先实现一个NSArray的Category:

@interface NSArray (Safe)
-(id)safeObjectAtIndex:(NSUInteger)index;
@end

@implementation NSArray (Safe)
-(id)safeObjectAtIndex:(NSUInteger)index{
    if (index<self.count) {
        return [self safeObjectAtIndex:index];
    }
    return nil;
}
@end

然后调用交换方法:

[[[NSArray array] class] jr_swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeObjectAtIndex:) error:nil];

具体为什么这么写,google一下就知道了。

后来我发现这篇文章:http://blog.csdn.net/yiyaaixuexi/article/details/8970734,所以我下面写的很多都是废话了。

但是对于调用未实现的方法引起的crash,处理起来就比较麻烦了。对于[xx method];[xx performSelector:@selector(method)];等等各种写法的方法调用,其实最终都是通过objc_msgSend这个C函数来实现的。当执行某个instance的某个selector的时候,如果找不到该方法的实现,那么会调用NSObject的forwardTargetForSelector,去询问实现类是否把这个方法转发一个其他的实现,如果返回的object还是没有实现该方法,那么就会抛出一个unrecognized selector的exception导致应用程序的进程崩溃。

原理大概就是这样,show me the code. 首先实现一个NSObject的Category:

@interface NSObject (Safe)
-(id)safeForwardingTargetForSelector:(SEL)aSelector;
@end

@implementation NSObject (Safe)
	-(id)safeForwardingTargetForSelector:(SEL)aSelector{
		NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
		if ([self respondsToSelector:aSelector] || signature) {
			return [self safeForwardingTargetForSelector:aSelector];
		}

		FakeForwardTargetObject *tmp = [[FakeForwardTargetObject alloc] initWithSelector:aSelector];
		return tmp;
	}
@end

这里FakeForwardTargetObject的实现为:

@interface FakeForwardTargetObject : NSObject
-(instancetype)initWithSelector:(SEL)aSelector;
@end

#import <objc/runtime.h>
id fakeIMP(id sender,SEL sel,...){
	return nil;
}
@implementation FakeForwardTargetObject
-(instancetype)initWithSelector:(SEL)aSelector{
	self = [super init];
	if (self) {
		if(class_addMethod([self class], aSelector, (IMP)fakeIMP, NULL)){
			NSLog(@"add Fake Selector:[instance %@]",NSStringFromSelector(aSelector));
		}
	}
	return self;
}
@end

上面把过程分析了,但实际实现中有一个特别的处理,在判断是否相应某个方法的时候,同时也判断了该instance是否含有一个该selector的methodsignature。系统类或者是别人会使用苹果的消息转发来实现多继承,这里你不能无视这种行为。但这个目前只是一个经验判断,因为没有看到过objc_msgSend的源码.

这样,在给很多类方法加上保护以后,你的app应该会稳定很多,但这都是治标不治本的方式,只对完成kpi什么的有点用….本身oc这样设计的目的并不是用于这个目的,所以这么使用会带来潜在的风险,如果哪个版本的ios把这里的实现变了,它可能会兼容正规的消息发送方式,但对于这些旁门左道,可能就会引起大面积的错误.