我就职过的公司,都出现过不止一次因为后台实际数据类型和文档不一致导致的程序线上崩溃问题。原则上即使服务器离线了,app 都不应该直接闪退,所以 app 应当具有对服务器异常情况进行处理的能力。使用 ValueX 可以有效避免后台返回数据类型异常导致的程序崩溃。

异常场景

  • 场景1: NSString 类型变成 NSNull
  • 场景2: nil 变成 @"<null>"
  • 场景3: userId 不确定是 NSString 还是 NSNumber
  • 场景4: NSDictionary 中嵌套的容器变成了 Json 字符串格式

问题抽象

  • 实际类型与定义类型不符
  • 实际类型与期望类型不一致

设计接口

假如我得到一个值 NSString *obj,实际上可能是 NSNull ,可能是 NSNumber ,可能是空数据的 @"<null>" 或者 @"(null)" 等各种表述。那么我需要确保 NSSafeString(obj) 的结果一定是 NSString 类型的值,如果是空数据,就应该是 nil。如果是 @123 这样的数字,那么应该得到 @"123" 这样的字符串。

如果是 json 数据,它可能以 NSData 类型存在,可能以 Json 字符串 NSString 类型存在,可能以 NSArray 或者 NSDictionary 对象形式存在。我需要的是我要什么类型就能直接获取。例如 ValueX(obj).stringValue 就得到这个数据的 Json 字符串,ValueX(obj).dictionaryValue 就得到这个数据的字典对象。

所以这个库就分为两部份工作,一部分是将实际类型与定义类型一致化;第二部分是将已知类型转换成它能够转换的其他类型。

类型安全

针对 ObjC 中常用的 6 种数据类型 NSString、NSNumber、NSData、NSArray、NSSet、NSDictionary 一一进行判断,写出6个函数:

得到安全类型:
FOUNDATION_EXTERN NSString * __nullable NSSafeString(id obj);
FOUNDATION_EXTERN NSNumber * __nullable NSSafeNumber(id obj);
FOUNDATION_EXTERN NSData * __nullable NSSafeData(id obj);
FOUNDATION_EXTERN NSArray * __nullable NSSafeArray(id obj);
FOUNDATION_EXTERN NSSet * __nullable NSSafeSet(id obj);
FOUNDATION_EXTERN NSDictionary * __nullable NSSafeDictionary(id obj);

确保从这些函数出来的值是与定义类型一致的值。

类型转换

即使拿到的值与定义类型一致,有时候我们要用的可能又是其他类型。

数据类型转换函数:
FOUNDATION_EXTERN VXObject *ValueX(id <VXConvertable>obj);

确保 VXObject 类型可以通过点语法直接得到原数据所能够转换的其他类型。VXObject 是一种中间类型,针对上述 6 种数据类型,每一种可以转换成其他哪些类型进行一一判断。

其中,NSNumber 只可以转换为 NSString、NSData,遵循 VXConvertable 协议。表示数字的 NSString,可以转换成 NSNumber,表示 Json 的字符串可以转换成对应的 NSArray 或者 NSDictionary,遵守 VXConvertableData 协议。反过来 NSArray、NSSet 或者 NSDictionary 则遵守 VXConvertableObject 协议。

@interface NSString (VXObject) <VXConvertableData>
@end
@interface NSNumber (VXObject) <VXConvertable>
@end
@interface NSData (VXObject) <VXConvertableData>
@end
@interface NSArray (VXObject) <VXConvertableObject>
@end
@interface NSSet (VCObject) <VXConvertableObject>
@end
@interface NSDictionary (VXObject) <VXConvertableObject>
@end

它们的协议内容是:

@protocol VXConvertable <NSObject>
- (VXObject *)vx;
@end

@protocol VXConvertableObject <VXConvertable>
- (VXObject *)vxWithOptions:(NSJSONWritingOptions)opt;
@end

@protocol VXConvertableData <VXConvertable>
- (VXObject *)vxWithOptions:(NSJSONReadingOptions)opt;
@end

就按照这个思路进行开发,实现的代码就不再赘述了。