为了安全起见,要为NSUserDefaults中的数据加密。但是在每个调用NSUserDefaults读写方法的位置,加入加密解密方法也太麻烦。所以想重写NSUserDefaults的读写方法,把加密解密方法内嵌其中,这样修改代码量会大大减小。
先贴上代码
#import <Foundation/Foundation.h> @interface NSUserDefaults (Swizzle) -(void)swizzleSetObject:(nullable id) value forKey:(nullable NSString *)defaultName; -(nullable id)swizzleObjectForKey:(NSString *)defaultName; @end
#import "NSUserDefaults+Swizzle.h" #import "WFTTripleDES.h" @implementation NSUserDefaults (Swizzle) -(void)swizzleSetObject:(nullable id) value forKey:(nullable NSString *)defaultName { if ([value isKindOfClass:[NSString class]]) { //加密后写入 [self swizzleSetObject:[WFTTripleDES TripleDES:value encryptOrDecrypt:kCCEncrypt key:[NSString stringWithFormat:@"2016012031009%@",defaultName] ] forKey:defaultName]; }else{ [self swizzleSetObject:value forKey:defaultName]; } } -(nullable id)swizzleObjectForKey:(NSString *)defaultName; { //如果键值为空直接返回 if (!defaultName) { return @""; } id getObject = [self swizzleObjectForKey:defaultName]; if ([getObject isKindOfClass:[NSString class]]) { //把结果解密 return [WFTTripleDES TripleDES:getObject encryptOrDecrypt:kCCDecrypt key:[NSString stringWithFormat:@"2016012031009%@",defaultName]]; }else{ return getObject; } } @end
Method originalMethod = class_getInstanceMethod([NSUserDefaults class], @selector(setObject:forKey:)); Method swizzledMethod = class_getInstanceMethod([NSUserDefaults class], @selector(swizzleSetObject:forKey:)); method_exchangeImplementations(originalMethod, swizzledMethod);
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,
然后调用method_exchangeImplementations方法交换后变成
所以调用setObject:forKey:方法时,其实调用的是swizzleSetObject:forKey:的实现。而调用swizzleSetObject:forKey:方法时,调用的系统的setObject:forKey:的实现。
如果在swizzleSetObject:forKey:实现方法中,调用setObject:forKey:方法,其实会回调swizzleSetObject:forKey:。