一行代码解决:服务器返回null导致应用崩溃
2016-08-12 09:24
453 查看
背景1:是否有这样一种感受,你的应用会崩溃,查看崩溃日志或者调试发现后台返回的数据字段里面有null,这个null居然还是导致应用崩溃的“元凶”。
背景2:服务器返回的字段名和不一致,如果model属性少,你可以一个个对应赋值,多了肯定都会想到用运行时机制来赋值:
[self
setValue:temDic[key]
forKeyPath:key],这时候需要对两个地方的字段做映射。
思路:1、先判断是否需要映射 2、把服务器返回的数据赋给model 3、model属性null检查(不同类型赋不同值,并不是把所有null都赋@"")4、这个写法应该适应所有自定义的类型,所有给NSObject 加一个Category。
#import
"NSObject+runtime.h"
@implementation
NSObject (runtime)
- (void)fetchValueFormNetDict:(NSDictionary
*)dic andMapDic:(NSDictionary
*)MapDic{
NSArray *properties = [self
getAllProperties];
//需要映射
if (MapDic !=
nil) {
NSArray *allKeys = dic.allKeys;
NSMutableDictionary *temDic = [NSMutableDictionary
dictionaryWithCapacity:allKeys.count];
for (int
i = 0; i < allKeys.count;
i ++) {
NSString *key = allKeys[i];
[temDic
setObject:dic[key]
forKey:MapDic[key]];
}
//
遍历属性数组
for (NSString
*key in properties) {
//
判断字典中是否包含这个key
[self
setValue:temDic[key]
forKeyPath:key];
}
}
else//不需要映射
{
//
遍历属性数组
for (NSString
*key in properties) {
//
判断字典中是否包含这个key
[self
setValue:dic[key]
forKeyPath:key];
}
}
//null
或者 nil
处理
[self
nullDeal];
}
//获取所有的属性名
- (NSArray
*)getAllProperties
{
u_int count;
objc_property_t *properties
=class_copyPropertyList([self
class], &count);
NSMutableArray *propertiesArray
= [NSMutableArray
arrayWithCapacity:count];
for (int
i = 0; i<count; i++)
{
const
char* propertyName =property_getName(properties[i]);
[propertiesArray
addObject: [NSString
stringWithUTF8String: propertyName]];
}
free(properties);
return propertiesArray;
}
-(void)nullDeal
{
//找到属性对应的类型
NSDictionary *typeDic = [self
classPropsFor:[self
class]];
NSArray *arr = typeDic.allKeys;
//所有属性名字
for (int
i = 0; i < arr.count;
i++) {
//属性类型名字(字符串格式)
NSString *typeString = typeDic[arr[i]];
if ([[self
valueForKey:arr[i]]
isKindOfClass:[NSNull
class]] || [self
valueForKey:arr[i]] ==
nil) {
if ([typeString
isEqualToString:@"NSArray"])
{
[self
setValue:@[]
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSString"])
{
[self
setValue:@""
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSNumber"])
{
[self
setValue:@0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSDictionary"])
{
[self
setValue:@{}
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"B"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"f"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"d"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"i"])
{
[self
setValue:0
forKey:arr[i]];
}
}
}
}
//获取属性名称数组
- (NSDictionary
*)classPropsFor:(Class)klass
{
if (klass ==
NULL) {
return
nil;
}
NSMutableDictionary *results = [[NSMutableDictionary
alloc]
init];
unsigned
int outCount, i;
objc_property_t *properties
= class_copyPropertyList(klass,
&outCount);
for (i =
0; i < outCount; i++) {
objc_property_t property = properties[i];
const
char *propName =
property_getName(property);
if(propName) {
const
char *propType =
getPropertyType(property);
NSString *propertyName = [NSString
stringWithUTF8String:propName];
NSString *propertyType = [NSString
stringWithUTF8String:propType];
NSLog(@"propertyName
%@ propertyType %@", propertyName, propertyType);
[results
setObject:propertyType
forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary
dictionaryWithDictionary:results];
}
//获取属性类型的方法
c语言写法
T@"NSString",C,N,V_name Tf,N,V__float
static
const
char
*getPropertyType(objc_property_t
property) {
const
char *attributes =
property_getAttributes(property);
printf("attributes=%s\n",
attributes);
char buffer[1
+ strlen(attributes)];//多一个结束符号
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute =
strsep(&state,
",")) !=
NULL) {
if (attribute[0]
== 'T' && attribute[1]
!= '@') {
NSString *name = [[NSString
alloc]
initWithBytes:attribute +
1
length:strlen(attribute)
- 1
encoding:NSASCIIStringEncoding];
return (const
char *)[name
cStringUsingEncoding:NSASCIIStringEncoding];
}
else
if (attribute[0]
== 'T' && attribute[1]
== '@' &&
strlen(attribute) ==
2) {
// it's an ObjC id type:
return
"id";
}
else
if (attribute[0]
== 'T' && attribute[1]
== '@') {
// it's another ObjC object type:
NSString *name = [[NSString
alloc]
initWithBytes:attribute +
3
length:strlen(attribute)
- 4
encoding:NSASCIIStringEncoding];
return (const
char *)[name
cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return
"";
}
@end
基本工作差不多完了,现在来自定义一个类型试试:
#import
<Foundation/Foundation.h>
@interface UserModel :
NSObject
@property(nonatomic,copy)NSString
*name;
@property(nonatomic,copy)NSString
*home;
@property(nonatomic,strong)NSNumber
*old;
@property(nonatomic,strong)NSArray
*arr;
@property(nonatomic,strong)NSDictionary
*dic;
@property(nonatomic,assign)BOOL
isBoll;
-(instancetype)initWithDic:(NSDictionary
*)dic;
@end
#import
"UserModel.h"
#import
"NSObject+runtime.h"
@implementation UserModel
-(instancetype)initWithDic:(NSDictionary
*)dic
{
if (self
== [super
init]) {
// dic
网络数据
mapDic 映射之后的字典
[self
fetchValueFormNetDict:dic
andMapDic:[self
mapDic]];
}
return
self;
}
////设置
映射字典格式:@"网络数据字段名":@"model属性名"
//如果服务器返回的字段
和 model的属性名字不一致
则需要映射
返回映射字典
否则返回nil表示不需要映射
-(NSDictionary
*)mapDic
{
return
@{@"wzcName":@"name",@"wzcAddress":@"home",@"isbol":@"isBoll"};
}
@end
实列化一个UserModel
对象
NSDictionary
*data1 = @{@"wzcName":[NSNull
null],@"wzcAddress":@"杭州市西湖区",@"isbol":@1};
UserModel
*model1 = [[UserModel
alloc]initWithDic:data1];
结果如下:
这样在后面数据操作不会崩溃了。
背景2:服务器返回的字段名和不一致,如果model属性少,你可以一个个对应赋值,多了肯定都会想到用运行时机制来赋值:
[self
setValue:temDic[key]
forKeyPath:key],这时候需要对两个地方的字段做映射。
思路:1、先判断是否需要映射 2、把服务器返回的数据赋给model 3、model属性null检查(不同类型赋不同值,并不是把所有null都赋@"")4、这个写法应该适应所有自定义的类型,所有给NSObject 加一个Category。
#import
"NSObject+runtime.h"
@implementation
NSObject (runtime)
- (void)fetchValueFormNetDict:(NSDictionary
*)dic andMapDic:(NSDictionary
*)MapDic{
NSArray *properties = [self
getAllProperties];
//需要映射
if (MapDic !=
nil) {
NSArray *allKeys = dic.allKeys;
NSMutableDictionary *temDic = [NSMutableDictionary
dictionaryWithCapacity:allKeys.count];
for (int
i = 0; i < allKeys.count;
i ++) {
NSString *key = allKeys[i];
[temDic
setObject:dic[key]
forKey:MapDic[key]];
}
//
遍历属性数组
for (NSString
*key in properties) {
//
判断字典中是否包含这个key
[self
setValue:temDic[key]
forKeyPath:key];
}
}
else//不需要映射
{
//
遍历属性数组
for (NSString
*key in properties) {
//
判断字典中是否包含这个key
[self
setValue:dic[key]
forKeyPath:key];
}
}
//null
或者 nil
处理
[self
nullDeal];
}
//获取所有的属性名
- (NSArray
*)getAllProperties
{
u_int count;
objc_property_t *properties
=class_copyPropertyList([self
class], &count);
NSMutableArray *propertiesArray
= [NSMutableArray
arrayWithCapacity:count];
for (int
i = 0; i<count; i++)
{
const
char* propertyName =property_getName(properties[i]);
[propertiesArray
addObject: [NSString
stringWithUTF8String: propertyName]];
}
free(properties);
return propertiesArray;
}
-(void)nullDeal
{
//找到属性对应的类型
NSDictionary *typeDic = [self
classPropsFor:[self
class]];
NSArray *arr = typeDic.allKeys;
//所有属性名字
for (int
i = 0; i < arr.count;
i++) {
//属性类型名字(字符串格式)
NSString *typeString = typeDic[arr[i]];
if ([[self
valueForKey:arr[i]]
isKindOfClass:[NSNull
class]] || [self
valueForKey:arr[i]] ==
nil) {
if ([typeString
isEqualToString:@"NSArray"])
{
[self
setValue:@[]
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSString"])
{
[self
setValue:@""
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSNumber"])
{
[self
setValue:@0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"NSDictionary"])
{
[self
setValue:@{}
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"B"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"f"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"d"])
{
[self
setValue:0
forKey:arr[i]];
}
if ([typeString
isEqualToString:@"i"])
{
[self
setValue:0
forKey:arr[i]];
}
}
}
}
//获取属性名称数组
- (NSDictionary
*)classPropsFor:(Class)klass
{
if (klass ==
NULL) {
return
nil;
}
NSMutableDictionary *results = [[NSMutableDictionary
alloc]
init];
unsigned
int outCount, i;
objc_property_t *properties
= class_copyPropertyList(klass,
&outCount);
for (i =
0; i < outCount; i++) {
objc_property_t property = properties[i];
const
char *propName =
property_getName(property);
if(propName) {
const
char *propType =
getPropertyType(property);
NSString *propertyName = [NSString
stringWithUTF8String:propName];
NSString *propertyType = [NSString
stringWithUTF8String:propType];
NSLog(@"propertyName
%@ propertyType %@", propertyName, propertyType);
[results
setObject:propertyType
forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary
dictionaryWithDictionary:results];
}
//获取属性类型的方法
c语言写法
T@"NSString",C,N,V_name Tf,N,V__float
static
const
char
*getPropertyType(objc_property_t
property) {
const
char *attributes =
property_getAttributes(property);
printf("attributes=%s\n",
attributes);
char buffer[1
+ strlen(attributes)];//多一个结束符号
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute =
strsep(&state,
",")) !=
NULL) {
if (attribute[0]
== 'T' && attribute[1]
!= '@') {
NSString *name = [[NSString
alloc]
initWithBytes:attribute +
1
length:strlen(attribute)
- 1
encoding:NSASCIIStringEncoding];
return (const
char *)[name
cStringUsingEncoding:NSASCIIStringEncoding];
}
else
if (attribute[0]
== 'T' && attribute[1]
== '@' &&
strlen(attribute) ==
2) {
// it's an ObjC id type:
return
"id";
}
else
if (attribute[0]
== 'T' && attribute[1]
== '@') {
// it's another ObjC object type:
NSString *name = [[NSString
alloc]
initWithBytes:attribute +
3
length:strlen(attribute)
- 4
encoding:NSASCIIStringEncoding];
return (const
char *)[name
cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return
"";
}
@end
基本工作差不多完了,现在来自定义一个类型试试:
#import
<Foundation/Foundation.h>
@interface UserModel :
NSObject
@property(nonatomic,copy)NSString
*name;
@property(nonatomic,copy)NSString
*home;
@property(nonatomic,strong)NSNumber
*old;
@property(nonatomic,strong)NSArray
*arr;
@property(nonatomic,strong)NSDictionary
*dic;
@property(nonatomic,assign)BOOL
isBoll;
-(instancetype)initWithDic:(NSDictionary
*)dic;
@end
#import
"UserModel.h"
#import
"NSObject+runtime.h"
@implementation UserModel
-(instancetype)initWithDic:(NSDictionary
*)dic
{
if (self
== [super
init]) {
// dic
网络数据
mapDic 映射之后的字典
[self
fetchValueFormNetDict:dic
andMapDic:[self
mapDic]];
}
return
self;
}
////设置
映射字典格式:@"网络数据字段名":@"model属性名"
//如果服务器返回的字段
和 model的属性名字不一致
则需要映射
返回映射字典
否则返回nil表示不需要映射
-(NSDictionary
*)mapDic
{
return
@{@"wzcName":@"name",@"wzcAddress":@"home",@"isbol":@"isBoll"};
}
@end
实列化一个UserModel
对象
NSDictionary
*data1 = @{@"wzcName":[NSNull
null],@"wzcAddress":@"杭州市西湖区",@"isbol":@1};
UserModel
*model1 = [[UserModel
alloc]initWithDic:data1];
结果如下:
这样在后面数据操作不会崩溃了。
相关文章推荐
- 解决服务器返回错误数据格式导致Json解析出错造成app崩溃
- iOS中解决后台返回的null导致的崩溃问题--NullSafe
- 服务器代码未执行完,接口返回501错误,导致通过开放平台的微博经常重复
- 《对“XXX::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们》的问题的解决方法
- 一个服务器搭多个tomcat导致session丢失,或者同一个IP不同端口,多个应用的session会冲突解决方法
- 一个服务器搭多个tomcat导致session丢失,或者同一个IP不同端口,多个应用的session会冲突解决方法
- 【已解决】onCreateViewHolder中的代码错误,导致程序崩溃
- 一个服务器搭多个tomcat导致session丢失,或者同一个IP不同端口,多个应用的session会冲突解决方法
- 解决curl超时导致应用崩溃的问题
- Solr 千万级大数据索引查询导致服务器崩溃的原因和解决
- 简单一行代码解决流读取导致StringBuilder.toString()乱码问题
- 提交代码到svn时服务器重启导致svn无法更新问题解决办法
- 解决curl超时导致应用崩溃的问题
- iOS一行代码让你的应用中UIScrollView的滑动与侧滑返回并存
- 解决"链接服务器 '(null)' 的 OLE DB 访问接口 'STREAM' 返回了对列 '[!BulkInsert].fieldname' 无效的数据"的一种替换方法
- 类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们的问题的解决方法
- 关于出现VirtualAlloc pointer is null导致git崩溃的解决办法
- 解决后台杀应用,小米推送再次初始化导致ShareSDK初始化报错,使应用崩溃
- 关于Fragment中的getActivity()返回null导致程序崩溃的解决方案
- Android应用第一次安装成功点击“打开”后Home键切出应用后再点击桌面图标返回导致应用重启问题的解决方法