
iOS利用runtime技术实现容器的越界和nil保护
原因
我们经常会因为添加nil到NSDictionary,添加nil到NSArray,数组越界,而导致程序崩溃,为了防止这些因为代码疏忽而造成的崩溃,所以写了WQContainerSafe这个类。
类介绍
这文件一定要在工程中弄成mrc的。不然会有这样一个bug 在弹出键盘后home出程序就会crash,并且最后报[UIKeyboardLayoutStar release]这样一个错误。
重要的事情说三遍:
这文件一定要在工程中弄成mrc的。
这文件一定要在工程中弄成mrc的。
这文件一定要在工程中弄成mrc的。
WQContainerSafe这个类只有WQContainerSafe.m这一个文件。它并不会要求在其他代码的地方引用,调用它,只需要把该文件加到工程里面即可。原因就是:
+(void) load {
//code
}
load函数是在app装载的时候调用。(NSObject的load和initialize方法可以google一下这两个的区别)
- 一个类的+load方法在其父类的+load方法后调用
- 一个Category的+load方法在被其扩展的类的自有+load方法后调用
所以在WQContainerSafe.m文件里面我们实现了NSArray和NSMutableDictionary的load方法。然后利用runtime中method_exchangeImplementations方法来替换原有的objectAtIndex、insertObject:atIndex:、setObject:forKey:方法。在新的方法里面写上越界保护,nil保护。
我们拿NSMutableDictionary为例:
//在load方法中调用方法交换的函数。这样代码就为非侵入式的。默默的就自己贡献了力量。:-)
+(void)load{
[self swizze];
}
//交换方法,把[setObject:forKey:]的调用指向我们自己实现的函数[setSaveObject:forKey:]
+(void)swizze
{
Method old = class_getInstanceMethod(NSClassFromString(@"__NSDictionaryM"), @selector(setObject:forKey:));
Method new = class_getInstanceMethod(self, @selector(setSaveObject:forKey:));
if (!old || !new) {
return;
}
method_exchangeImplementations(old, new);
}
//实现安全的setObject:forKey:函数。添加了nil保护。这里之所以是[setSaveObject:forKey:]调用自己。
//是因为方法交换了以后,这里实际上是调用原生的[setObject:forKey:]
-(void)setSaveObject:(id)value forKey:(id)key
{
if (value!=nil) {
[self setSaveObject:value forKey:key];
}
else{
NSAssert(NO, @"WQContainerSafe: setObject:nil");
}
}
遇到的坑
- 用方法交换做NSArray的越界保护,要交换3个类的objectAtIndex:方法:
__NSArrayM (可变数组)__NSArrayI (一般数组)__NSArray0 (一般的空数组),而且不能用NSArrayI的objectiveAtIndex方法替换NSArrayM的。 - NSArray的
addObject方法最终会走到insertObject:atIndex:这个方法,所以只用替换后者就可以了。
TODO
- 现在只支持了
NSArray和NSDictionary,以后会添加其他容器的支持
Leave iOS利用runtime技术实现容器的越界和nil保护 to:
Read more #cn posts
Best Posts From decembersola
We have not curated any of decembersola's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.