运行时是一个好东西,他可以帮助我们解决很多的问题.在平时的开发中可能用的不是很多.但是做为APP开发人员遇到的崩溃情况肯定很多了.大致分为两类 一类是由于代码不严谨导致的数组越界 一类是对字符串为空的情况下没有判断. 这是本人遇到最多的崩溃情况了.解决好这个两个,基本上APP就不会崩溃.那么那么多的对数组操作以及字符串操作一个一个做判断吗? 当然这是我们应该做的,但是也有更好的以及方便的处理方式
提醒一下:
1、不要过分相信服务器返回的数据会永远的正确。
2、在对数据处理上,要进行容错处理,进行相应判断之后再处理数据,这是一个良好的编程习惯。
3.老板让你明天上线,你还没有做容错处理.所谓阎王叫你三更死......都是累,泪........
主要技术点:
运行时:导入框架
```
#import <objc/runtime.h>
```
这里主要通过runtime的method swizzling 交换方法的实现 提前判断方法的参数是否符合要求 在创建创建类当中的load方法里面利用单例交换数组字典等方法,替换成自己的方法,方便对参数做出相应的处理 .打个比方,检测到参数为nil的时候直接return .就有效的防止了崩溃
```
+ (void)swizzleInstanceMethodWithClass:(Class)class
originalSelector:(SEL)originalSelector
swizzledMethod:(SEL)swizzledSelector {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// NSArray
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayI") originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(fcx_safeObjectAtIndex:)];
// NSMutableArray
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(fcx_safeObjectAtIndex:)];
// [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(addObject:) swizzledMethod:@selector(fcx_safeAddObject:)];
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(replaceObjectAtIndex:withObject:) swizzledMethod:@selector(fcx_safeReplaceObjectAtIndex:withObject:)];
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(insertObject:atIndex:) swizzledMethod:@selector(fcx_safeInsertObject:atIndex:)];
// NSDictionary
// [self swizzleClassMethodWithClass:[NSDictionary class] originalSelector:@selector(dictionaryWithObjects:forKeys:count:) swizzledMethod:@selector(fcx_safeDictionaryWithObjects:forKeys:count:)];
// NSMutableDictionary
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSDictionaryM") originalSelector:@selector(setObject:forKey:) swizzledMethod:@selector(fcx_safeSetObject:forKey:)];
});
}
```
这里对字典赋值进行一个讲解,大家可以举一反三去思考.后面会直接提供代码.
在前面的单例中我们已经利用swizzling 将setObject:forKey: 这个方法替换成了我们自己的方法(fcx_safeSetObject:forKey:
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSDictionaryM") originalSelector:@selector(setObject:forKey:) swizzledMethod:@selector(fcx_safeSetObject:forKey:)];
//替换之后的方法 通俗讲就是我们利用对字段赋值的时候会直接调用下面的方法
```
- (void)fcx_safeSetObject:(id)anObject forKey:(id)aKey {
//如果字典的key值为空则 return
if (!aKey)
{
FCXSCLOG(@"[%@ %@] nil key. key cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
return;
}
//如果字典的key值为空则 return
if (!anObject)
{
FCXSCLOG(@"[%@ %@] nil object. object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
return;
}
[self fcx_safeSetObject:anObject forKey:aKey];
}
```
当然各位也可以根据自己的需求进行相应的处理.下面是源码.供大家参考
//温馨提示直接创建一个FCXSafeCollection类 然后将下面代码全部粘贴到.m文件拖动工程即可
```
#import "FCXSafeCollection.h"
#import
#if DEBUG
#defineFCXSCLOG(...) fcxSafeCollectionLog(__VA_ARGS__)
#else
#defineFCXSCLOG(...)
#endif
voidfcxSafeCollectionLog(NSString *fmt, ...) NS_FORMAT_FUNCTION(1, 2);
voidfcxSafeCollectionLog(NSString *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
NSString *content = [[NSString alloc] initWithFormat:fmtarguments:ap];
NSLog(@"***Terminating app due touncaught exception\n");
NSLog(@"***reason:-%@", content);
va_end(ap);
NSLog(@"*** First throw callstack:\n%@", [NSThread callStackSymbols]);
}
#pragmamark - NSArray
@interfaceNSArray (Safte)
@end
@implementationNSArray (Safte)
- (id)fcx_safeObjectAtIndex:(NSUInteger)index{
if (self.count > index) {
return [self fcx_safeObjectAtIndex:index];
}else {
FCXSCLOG(@"[%@ %@] index %lubeyond bounds [0 .. %lu]",
NSStringFromClass([selfclass]),
NSStringFromSelector(_cmd),
(unsigned long)index,
MAX((unsigned long)self.count- 1, 0));
return nil;
}
}
@end
//**************************************************************
#pragmamark - NSMutableArray
@interfaceNSMutableArray (Safte)
@end
@implementationNSMutableArray (Safte)
- (id)fcx_safeObjectAtIndex:(NSUInteger)index{
if (self.count > index) {
return [self fcx_safeObjectAtIndex:index];
}else {
FCXSCLOG(@"[%@ %@] index %lubeyond bounds [0 .. %lu]",
NSStringFromClass([selfclass]),
NSStringFromSelector(_cmd),
(unsigned long)index,
MAX((unsigned long)self.count - 1, 0));
return nil;
}
}
- (void)fcx_safeAddObject:(id)anObject{
if (anObject) {
[self fcx_safeAddObject:anObject];
}else {
FCXSCLOG(@"[%@ %@], nil object.object cannot be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd));
}
}
- (void)fcx_safeReplaceObjectAtIndex:(NSUInteger)indexwithObject:(id)anObject {
if (index >= self.count) {
FCXSCLOG(@"[%@ %@] index %lubeyond bounds [0 .. %lu].",
NSStringFromClass([selfclass]),
NSStringFromSelector(_cmd),
(unsigned long)index,
MAX((unsigned long)self.count- 1, 0));
return;
}else if (!anObject) {
FCXSCLOG(@"[%@ %@] nil object.object cannot be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd));
return;
}
[self fcx_safeReplaceObjectAtIndex:index withObject:anObject];
}
- (void)fcx_safeInsertObject:(id)anObjectatIndex:(NSUInteger)index {
if (index > self.count)
{
FCXSCLOG(@"[%@ %@] index %lubeyond bounds [0...%lu].",
NSStringFromClass([selfclass]),
NSStringFromSelector(_cmd),
(unsigned long)index,
MAX((unsigned long)self.count- 1, 0));
return;
}
if (!anObject)
{
FCXSCLOG(@"[%@ %@] nil object.object cannot be nil", NSStringFromClass([self class]),NSStringFromSelector(_cmd));
return;
}
[self fcx_safeInsertObject:anObject atIndex:index];
}
@end
//**************************************************************
#pragmamark - NSDictionary
@interfaceNSDictionary (Safte)
@end
@implementationNSDictionary (Safte)
+ (instancetype)fcx_safeDictionaryWithObjects:(constid_Nonnull __unsafe_unretained*)objects forKeys:(const id_Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt {
id validObjects[cnt];
id validKeys[cnt];
NSUInteger count = 0;
for (NSUInteger i = 0; i < cnt; i++)
{
if (objects[i] && keys[i])
{
validObjects[count] = objects[i];
validKeys[count] = keys[i];
count ++;
}
else
{
FCXSCLOG(@"[%@ %@] NIL objector key at index{%lu}.",
NSStringFromClass(self),
NSStringFromSelector(_cmd),
(unsigned long)i);
}
}
return [self fcx_safeDictionaryWithObjects:validObjectsforKeys:validKeys count:count];
}
@end
//**************************************************************
#pragmamark - NSMuatbleDictionary
@interfaceNSMutableDictionary (Safte)
@end
@implementationNSMutableDictionary (Safte)
- (void)fcx_safeSetObject:(id)anObjectforKey:(id)aKey {
if (!aKey)
{
FCXSCLOG(@"[%@ %@] nil key. keycannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
return;
}
if (!anObject)
{
FCXSCLOG(@"[%@ %@] nil object.object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
return;
}
[self fcx_safeSetObject:anObject forKey:aKey];
}
@end
//**************************************************************
@implementationFCXSafeCollection
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//NSArray
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayI")originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(fcx_safeObjectAtIndex:)];
//NSMutableArray
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM")originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(fcx_safeObjectAtIndex:)];
//[selfswizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM")originalSelector:@selector(addObject:)swizzledMethod:@selector(fcx_safeAddObject:)];
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM")originalSelector:@selector(replaceObjectAtIndex:withObject:) swizzledMethod:@selector(fcx_safeReplaceObjectAtIndex:withObject:)];
[self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM")originalSelector:@selector(insertObject:atIndex:) swizzledMethod:@selector(fcx_safeInsertObject:atIndex:)];
//NSDictionary
//[selfswizzleClassMethodWithClass:[NSDictionary class]originalSelector:@selector(dictionaryWithObjects:forKeys:count:)swizzledMethod:@selector(fcx_safeDictionaryWithObjects:forKeys:count:)];
//NSMutableDictionary
[selfswizzleInstanceMethodWithClass:NSClassFromString(@"__NSDictionaryM") originalSelector:@selector(setObject:forKey:)swizzledMethod:@selector(fcx_safeSetObject:forKey:)];
});
}
+ (void)swizzleInstanceMethodWithClass:(Class)class
originalSelector:(SEL)originalSelector
swizzledMethod:(SEL)swizzledSelector{
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
if (class_addMethod(class,originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))){
class_replaceMethod(class,swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else {
method_exchangeImplementations(originalMethod,swizzledMethod);
}
}
+ (void)swizzleClassMethodWithClass:(Class)class
originalSelector:(SEL)originalSelector
swizzledMethod:(SEL)swizzledSelector{
Method originalMethod = class_getClassMethod(class,originalSelector);
Method swizzledMethod = class_getClassMethod(class,swizzledSelector);
//if (class_addMethod(class, originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod))) {
//
//class_replaceMethod(class, swizzledSelector,method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
//}else {
//
method_exchangeImplementations(originalMethod,swizzledMethod);
//}
}
@end
#pragmamark - KeyValueSafeCollections
@interfaceNSObject (FCXKeyValueSafeCollections)
@end
@implementationNSObject (FCXKeyValueSafeCollections)
//当对一个非类对象属性设置nil时,就会执行setNilValueForKey:方法,setNilValueForKey:方法的默认实现,是产生一个NSInvalidArgumentException的异常,但是你可以重写这个方法.
- (void)setNilValueForKey:(NSString*)key {
FCXSCLOG(@"[%@ %@]: could not set nilas the value for the key %@.", NSStringFromClass([self class]),NSStringFromSelector(_cmd), key);
}
```
//如果没有对应的访问器方法(setter方法),如果接受者的类的+accessInstanceVariablesDirectly方法返回YES,那么就查找这个接受者的与key相匹配的实例变量(匹配模式为_,_is,,is):比如:key为age,只要属性存在_age,_isAge,age,isAge中的其中一个就认为匹配上了,如果找到这样的一个实例变量,并且的类型是一个对象指针类型,首先released对象上的旧值,然后把传入的新值retain后的传入的值赋值该成员变量,如果方法的参数类型是NSNumber或NSValue的对应的基本类型,先把它转换为基本数据类,再执行方法,传入转换后的数据.
//+(BOOL)accessInstanceVariablesDirectly {
//return YES;
//}
//对于数据模型中缺少的、不能与任何键配对的属性的时候,系统会自动调用setValue:forUndefinedKey:这个方法,该方法默认的实现会引发一个NSUndefinedKeyExceptiony异常,但是我们可以重写setValue:forUndefinedKey:方法让程序在运行过程中不引发任何异常信息且正常工作
- (void)setValue:(id)valueforUndefinedKey:(NSString *)key {
FCXSCLOG(@"[%@ %@]: this class is notkey value coding-compliant for the key %@.", NSStringFromClass([selfclass]), NSStringFromSelector(_cmd), key);
}
//通过valueForKey获取对象属性值的方法时,如果代码中的key值不存在,系统会自动调用valueForUndefinedKey:这个方法,该方法默认的实现会引发一个NSUndefinedKeyExceptiony异常,但是我们可以重写valueForUndefinedKey:方法让程序在运行过程中不引发任何异常信息且正常工作
/**
*通过valueForKey获取对象属性值的方法时,如果代码中的key值不存在,系统会自动调用valueForUndefinedKey:这个方法,该方法默认的实现会引发一个NSUndefinedKeyExceptiony异常,但是我们可以重写valueForUndefinedKey:方法让程序在运行过程中不引发任何异常信息且正常工作
*
*@return nil
*
*@notice虽然这步可以返回nil不闪退,但是后续操作依然可能有问题
*/
- (id)valueForUndefinedKey:(NSString*)key {
FCXSCLOG(@"[%@ %@]: this class is notkey value coding-compliant for the key %@.", NSStringFromClass([selfclass]), NSStringFromSelector(_cmd), key);
return nil;
}
@end
有问题+QQ648731281 欢迎骚扰 相互交流学习