您的位置:首页 > 编程语言

一行代码解决:服务器返回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];

结果如下:



这样在后面数据操作不会崩溃了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐