OC和Swift中的Runtime

转载

运行时机制

runtime是一套比较底层的纯C语言的API, 属于C语言库, 包含了很多底层的C语言API。
在我们平时编写的iOS代码中, 最终都是转成了runtime的C语言代码。

所谓运行时,也就是在编译时是不存在的,只是在运行过程中才去确定对象的类型、方法等。利用Runtime机制可以在程序运行时动态修改类、对象中的所有属性、方法等。

还记得我们在网络请求数据处理时,调用了-setValuesForKeysWithDictionary:方法来设置模型的值。这里什么原理呢?为什么能这么做?其实就是通过Runtime机制来完成的,内部会遍历模型类的所有属性名,然后设置与key对应的属性名的值。

我们在使用运行时的地方,都需要包含头文件:#import <objc/runtime.h>。如果是Swift就不需要包含头文件,就可以直接使用了。

runtime基础

1. 获取对象所有属性名

利用运行时获取对象的所有属性名是可以的,但是变量名获取就得用另外的方法了。我们可以通过class_copyPropertyList方法获取所有的属性名称。

  • 第一个参数:类
  • 第二个参数:存放属性个数的地址

下面我们通过一个Person类来学习,这里的方法没有写成扩展,只是为了简化,将获取属性名的方法直接作为类的实例方法:

Objective-C版
@interface Person : NSObject{
    NSString *_variableString;
}

// 默认会是什么呢?
@property (nonatomic, copy) NSString *name;

// 默认是strong类型
@property (nonatomic, strong) NSMutableArray *array;

// 获取所有的属性名
- (NSArray *)allProperties;
@end

下面主要是写如何获取类的所有属性名的方法。注意,这里的objc_property_t是一个结构体指针objc_property *,因此我们声明的properties就是二维指针。在使用完成后,我们一定要记得释放内存,否则会造成内存泄露。这里是使用的是C语言的API,因此我们也需要使用C语言的释放内存的方法free。

@implementation Person

typedef struct objc_property *objc_property_t;

- (NSArray *)allProperties {
    unsigned int count;
    
    // 获取类的所有属性
    // 如果没有属性,则count为0,properties为nil
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
    
    for (NSUInteger i = 0; i < count; i++) {
        // 获取属性名称
        const char *propertyName = property_getName(properties[i]);
        NSString *name = [NSString stringWithUTF8String:propertyName];
        
        [propertiesArray addObject:name];
    }
    
    // 注意,这里properties是一个数组指针,是C的语法,
    // 我们需要使用free函数来释放内存,否则会造成内存泄露
    free(properties);
    
    return propertiesArray;
}

来测试一下,我们的方法是否正确获取到了呢?看下面的打印结果就明白了吧

Person *p = [[Person alloc] init];
    p.name = @"Lili";
    
    size_t size = class_getInstanceSize(p.class);
    NSLog(@"size=%ld", size);
    
    for (NSString *propertyName in p.allProperties) {
        NSLog(@"%@", propertyName);
    }
    // 打印结果:
    // 2016-04-19 11:37:24.589 LSRuntimeOCDemo[1554:108843] size=32
    // 2016-04-19 11:37:24.590 LSRuntimeOCDemo[1554:108843] name
    // 2016-04-19 11:37:24.590 LSRuntimeOCDemo[1554:108843] array

Swift版

对于Swift版,使用C语言的指针就不容易了,因为Swift希望尽可能减少C语言的指针的直接使用,因此在Swift中已经提供了相应的结构体封装了C语言的指针。但是看起来好复杂,使用起来好麻烦。看看Swift版的获取类的属性名称如何做:

class Person: NSObject {
    var name: String = ""
    var hasBMW = false
    
    override init() {
        super.init()
    }
    
    func allProperties() ->[String] {
        // 这个类型可以使用CUnsignedInt,对应Swift中的UInt32
        var count : UInt32 = 0
        
        let properties = class_copyPropertyList(Person.self, &count)
        
        ///定义一个元素为字符串的数组
        var propertyNames: [String] = []
        
        // Swift中类型是严格检查的,必须转换成同一类型
        for var i  in 0..<Int(count) {
            // UnsafeMutablePointer<objc_property_t>是可变指针,因此properties就是类似数组一样,可以通过下标获取
            let property = properties[i]
            let name = property_getName(property)
            
            
            // 这里还得转换成字符串
            let strName = String.fromCString(name)
            propertyNames.append(strName!)
        }
        // 不要忘记释放内存,否则C语言的指针很容易成野指针的
        free(properties)
        
        
        return propertyNames
    }
}

关于Swift中如何C语言的指针问题,这里不细说,如果需要了解,请查阅相关文章。
测试一下是否获取正确:

        let p = Person()
        p.name = "Lili"
        
        // 打印结果:["name", "hasBMW"],说明成功
        print( p.allProperties() )

2.获取对象的所有属性名和属性值

对于获取对象的所有属性名,在上面的-allProperties方法已经可以拿到了,但是并没有处理获取属性值,下面的方法就是可以获取属性名和属性值,将属性名作为key,属性值作为value。

Object-C版
- (NSDictionary *)allPropertyNamesAndValues {
    NSMutableDictionary *resultDict = [NSMutableDictionary dictionary];
    
    unsigned int outCount;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    
    for (int i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);
        
        // 得到属性名
        NSString *propertyName = [NSString stringWithUTF8String:name];
        
        // 获取属性值
        id propertyValue = [self valueForKey:propertyName];
        
        if (propertyValue && propertyValue != nil) {
            ///如果值为空则不添加进字典
            [resultDict setObject:propertyValue forKey:propertyName];
        }
    }
    
    // 记得释放
    free(properties);
    
    return resultDict;
}

测试一下

// 此方法返回的只有属性值不为空的属性
    NSDictionary *dict = p.allPropertyNamesAndValues;
    for (NSString *propertyName in dict.allKeys) {
        NSLog(@"propertyName: %@ propertyValue: %@",
              propertyName,
              dict[propertyName]);
    }
输出结果:
2016-04-19 12:30:49.367 LSRuntimeOCDemo[1983:145600] propertyName: name propertyValue: Lili
Siwft版
func allPropertyNamesAndValues() ->[String: AnyObject] {
        var count: UInt32 = 0
        let properties = class_copyPropertyList(Person.self, &count)
        
        var resultDict: [String: AnyObject] = [:]
        for var i in 0..<Int(count) {
            let property = properties[i]
            
            // 取得属性名
            let name = property_getName(property)
            if let propertyName = String.fromCString(name) {
                // 取得属性值
                if let propertyValue = self.valueForKey(propertyName) {
                    ///属性不为空的添加在字典中
                    resultDict[propertyName] = propertyValue
                }
            }
        }
        
        return resultDict
    }

测试一下:

let dict = p.allPropertyNamesAndValues()
        for (propertyName, propertyValue) in dict {
            print("propertyName: (\(propertyName)), propertyValue: (\(propertyValue))")
        }

打印结果:

propertyName: (hasBMW), propertyValue: (0)
propertyName: (name), propertyValue: (Lili)

3. 获取对象的所有方法名

通过class_copyMethodList方法就可以获取所有的方法。

  • 第一个参数:类
  • 第二个参数:存放方法个数的地址
Object-C版
- (void)allMethods {
    unsigned int outCount = 0;
    Method *methods = class_copyMethodList([self class], &outCount);
    
    for (int i = 0; i < outCount; ++i) {
        Method method = methods[i];
        
        // 获取方法名称,但是类型是一个SEL选择器类型
        SEL methodSEL = method_getName(method);
        // 需要获取C字符串
        const char *name = sel_getName(methodSEL);
        // 将方法名转换成OC字符串
        NSString *methodName = [NSString stringWithUTF8String:name];
        
        // 获取方法的参数列表
        int arguments = method_getNumberOfArguments(method);
        NSLog(@"方法名:%@, 参数个数:%d", methodName, arguments);
    }
    
    // 记得释放
    free(methods);
}

测试

///获取所有的方法名
    [p allMethods];

打印结果:

2016-04-19 13:33:49.999 LSRuntimeOCDemo[2174:175322] 方法名:allProperties, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:allPropertyNamesAndValues, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:allMethods, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:setArray:, 参数个数:3
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:.cxx_destruct, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:name, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:array, 参数个数:2
2016-04-19 13:33:50.000 LSRuntimeOCDemo[2174:175322] 方法名:setName:, 参数个数:3
Swift版
func allMethods() {
        var count : UInt32 = 0
        let methods = class_copyMethodList(Person.self,&count)
        
        for var i in 0..<Int(count) {
            let method = methods[i]
            let sel = method_getName(method)
            // 获取方法的参数列表
            let argument = method_getNumberOfArguments(method)
             print("name: \(sel), arguemtns: \(argument)")
        }
    }

测试一下:

///获取所有方法名
        p.allMethods()

打印结果:

name: hasBMW, arguemtns: 2
name: setHasBMW:, arguemtns: 3
name: allProperties, arguemtns: 2
name: allPropertyNamesAndValues, arguemtns: 2
name: allMethods, arguemtns: 2
name: name, arguemtns: 2
name: .cxx_destruct, arguemtns: 0
name: init, arguemtns: 2
name: setName:, arguemtns: 3

4. 获取对象的成员变量名称

要获取对象的成员变量,可以通过class_copyIvarList方法来获取,通过ivar_getName来获取成员变量的名称。对于属性,会自动生成一个成员变量。使用方法与前面类似。

  • 第一个参数:类
  • 第二个参数:存放变量个数的地址
Object-C版
- (NSArray *)allMemberVariables {
    unsigned int count = 0;
    ///Ivar 变量
    ///获取成员变量的数组 (指针)
    Ivar *ivars = class_copyIvarList([self class], &count);
    
    NSMutableArray *results = [[NSMutableArray alloc] init];
    for (NSUInteger i = 0; i < count; ++i) {
        ///获取每个成员变量
        Ivar variable = ivars[i];
        ///获取成员变量的字符名称
        const char *name = ivar_getName(variable);
        ///将名称转为NSString
        NSString *varName = [NSString stringWithUTF8String:name];
        
        [results addObject:varName];
    }
    free(ivars);
    return results;
}

测试:

NSLog(@"%@",[p allMemberVariables]);

打印结果:

2016-04-19 14:09:50.781 LSRuntimeOCDemo[2555:199104] (
    "_variableString",
    "_name",
    "_array"
)
Swift版

Swift的成员变量名与属性名是一样的,不会生成下划线的成员变量名,这一点与Oc是有区别的。

func allMemberVariables() ->[String] {
        var count:UInt32 = 0
        let ivars = class_copyIvarList(Person.self, &count)
        
        var result: [String] = []
        for var i = 0; i < Int(count); ++i {
            let ivar = ivars[i]
            
            let name = ivar_getName(ivar)
            
            if let varName = String.fromCString(name) {
                result.append(varName)
            }
        }
        
        return result
    }

测试:

///获取成员变量
  print(p.allMemberVariables())

打印结果:说明Swift的属性不会自动加下划线,属性名就是变量名:

["name", "hasBMW"]

5. 运行时发消息

Object-C版
Person *p = [[Person alloc] init];
p.name = @"Lili";
objc_msgSend(p, @selector(allMethods));

这样就相当于手动调用[p allMethods];。但是编译器会抱错,问题提示期望的参数为0,但是实际上有两个参数。解决办法是,关闭严格检查:

如果,此时报错
objc_msgSend()报错Too many arguments to function call ,expected 0,have2
那么,请Build Setting-->搜索 Enable Strict Checking of objc_msgSend Calls 改为 NO

屏幕快照 2016-04-19 下午2.44.35.png
Swift版

抱歉,Swift中没有此类方法。

6. Category扩展”属性”

iOS的category是不能扩展存储属性的,但是我们可以通过运行时关联来扩展“属性”。

Object-C 版

假设扩展下面的“属性”:

// 由于扩展不能扩展属性,因此我们这里在实现文件中需要利用运行时实现。
#import <objc/runtime.h>

typedef void(^LSCallBack)();
@interface NSObject (Property)

@property (nonatomic, copy) LSCallBack callback;
@end

在实现文件中,我们用一个静态变量作为key:

const void *s_LSCallbackKey = "s_LSCallbackKey";

- (void)setCallback:(LSCallBack)callback {
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
  objc_setAssociatedObject(self, s_LSCallbackKey, callback, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (HYBCallBack)callback {
  return objc_getAssociatedObject(self, s_LSCallbackKey);
}

测试:

p.callback=^(){
        NSLog(@"aaaa");
    };
    p.callback();

其实就是通过objc_getAssociatedObject取得关联的值,通过objc_setAssociatedObject设置关联。

Swift版

Swift版的要想扩展闭包,就比OC版的要复杂得多了。这里只是例子,写了一个简单的存储属性扩展。

let s_LSFullnameKey = "s_LSFullnameKey"

extension Person {
    var fullName: String? {
        get { return objc_getAssociatedObject(self, s_LSFullnameKey) as? String }
        set {
            // 第一个参数:给哪个对象添加关联
            // 第二个参数:关联的key,通过这个key获取
            // 第三个参数:关联的value
            // 第四个参数:关联的策略
            objc_setAssociatedObject(self, s_LSFullnameKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
}

总结

在开发中,我们比较常用的是使用关联属性的方式来扩展我们的“属性”,以便在开发中简单代码。我们在开发中使用关联属性扩展所有响应事件、将代理转换成block版等。比如,我们可以将所有继承于UIControl的控件,都拥有block版的点击响应,那么我们就可以给UIControl扩展一个TouchUp、TouchDown、TouchOut的block等。
对于动态获取属性的名称、属性值使用较多的地方一般是在使用第三方库中,比如MJExtension等。这些三方库都是通过这种方式将Model转换成字典,或者将字典转换成Model

另外

1.交换方法

开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。

// 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。
// 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
// 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。

@implementation UIImage (image)
// 加载分类到内存的时候调用

+(void)load
{
    // 交换方法
    
    // 获取imageWithName方法地址
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    
    // 获取imageWithName方法地址
    Method imageName = class_getClassMethod(self, @selector(imageNamed:));
    
    // 交换方法地址,相当于交换实现方式
    method_exchangeImplementations(imageWithName, imageName);
    
}

// 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.

// 既能加载图片又能打印

+(instancetype)imageWithName:(NSString *)name
{
    
    // 这里调用imageWithName,相当于调用imageName
    UIImage *image = [self imageWithName:name];
    
    if (image == nil) {
        
        NSLog(@"加载空的图片");
    }
    
    return image;
}

测试:

UIImage *image = [UIImage imageNamed:@"123"];

打印结果:

2016-04-19 16:26:45.317 LSRuntimeOCDemo[3394:299037] 加载空的图片

2.动态添加方法

开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。
简单使用

例如:
Person *p = [[Person alloc] init];
// 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。

// 动态添加方法就不会报错
[p performSelector:@selector(eat)];

实现:

#import "Person.h"

@implementation Person

// void(*)()
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法

+(BOOL)resolveInstanceMethod:(SEL)sel
{
    
    if (sel == @selector(eat)) {
        
        // 动态添加eat方法
        
        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");
    }
    
    return [super resolveInstanceMethod:sel];
}

测试:

Person *p = [[Person alloc] init];
// 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
// 动态添加方法就不会报错
[p performSelector:@selector(eat)];

打印:

2016-04-19 16:36:50.275 LSRuntimeOCDemo[3429:304239] <Person: 0x7f8620d9dd00> eat

3.字典转模型

设计模型:字典转模型的第一步
模型属性,通常需要跟字典中的key一一对应
问题:一个一个的生成模型属性,很慢?
需求:能不能自动根据一个字典,生成对应的属性。
解决:提供一个分类,专门根据字典生成对应的属性字符串。

@implementation NSObject (Log)
+(void)resolveDict:(NSDictionary *)dict{
    // 拼接属性字符串代码
    NSMutableString *strM = [NSMutableString string];
    // 1.遍历字典,把字典中的所有key取出来,生成对应的属性代码
    [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
       // 类型经常变,抽出来
        NSString *type;
        if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) {
            type = @"NSString";
        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){
            type = @"NSArray";
        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){
            type = @"int";
        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){ type = @"NSDictionary";
        }
    
       // 属性字符串
        NSString *str;
        if ([type containsString:@"NS"]) {
            str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];
        }else{
            str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];
        }
        // 每生成属性字符串,就自动换行。
        [strM appendFormat:@"\n%@\n",str];
    }];
    // 把拼接好的字符串打印出来,就好了。
    NSLog(@"%@",strM);
    
}
@end
字典转模型的方式一:KVC
@implementation Status
+(instancetype)statusWithDict:(NSDictionary *)dict
{
    Status *status = [[self alloc] init];
    
    [status setValuesForKeysWithDictionary:dict];
    
    return status;
    
}
@end

KVC字典转模型弊端:必须保证,模型中的属性和字典中的key一一对应。
如果不一致,就会调用[<Status 0x7fa74b545d60> setValue:forUndefinedKey:] 报key找不到的错。
分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。
解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖, 就能继续使用KVC,字典转模型了。

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}
字典转模型的方式二:Runtime

思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。
步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。

#import "NSObject+Model.h"

@implementation NSObject (Model)
+(instancetype)modelWithDict:(NSDictionary *)dict
{
    // 思路:遍历模型中所有属性-》使用运行时
    
    // 0.创建对应的对象
    id objc = [[self alloc] init];
    
    // 1.利用runtime给对象中的成员属性赋值
    
    // class_copyIvarList:获取类中的所有成员属性
    // Ivar:成员属性的意思
    // 第一个参数:表示获取哪个类中的成员属性
    // 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值
    // 返回值Ivar :指的是一个ivar数组,会把所有成员属性放在一个数组中,通过返回的数组就能全部获取到。
    // 类似下面这种写法
    
//    Ivar ivar;
//    Ivar ivar1;
//    Ivar ivar2;
//    // 定义一个ivar的数组a
//    Ivar a[] = {ivar,ivar1,ivar2};
//    
//    // 用一个Ivar 指针指向数组第一个元素
//    Ivar ivarList = a;
//    
//    // 根据指针访问数组第一个元素
//    ivarList[0];
    
    
    unsigned int count;
    
    // 获取类中的所有成员属性
    Ivar *ivarList = class_copyIvarList(self, &count);
    
    for (int i = 0; i < count; i++) {
        
        // 根据角标,从数组取出对应的成员属性
        Ivar ivar = ivarList[i];
        
        // 获取成员属性名
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 处理成员属性名->字典中的key
        // 从第一个角标开始截取
        NSString *key = [name substringFromIndex:1];
        
        // 根据成员属性名去字典中查找对应的value
        id value = dict[key];
        
        // 二级转换:如果字典中还有字典,也需要把对应的字典转换成模型
        // 判断下value是否是字典
        if ([value isKindOfClass:[NSDictionary class]]) {
            // 字典转模型
            // 获取模型的类对象,调用modelWithDict
            // 模型的类名已知,就是成员属性的类型
            
            // 获取成员属性类型
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            // 生成的是这种@"@\"User\"" 类型 -》 @"User"  在OC字符串中 \" -> ",\是转义的意思,不占用字符
            // 裁剪类型字符串
            NSRange range = [type rangeOfString:@"\""];
            
            type = [type substringFromIndex:range.location + range.length];
            
            range = [type rangeOfString:@"\""];
            
            // 裁剪到哪个角标,不包括当前角标
            type = [type substringToIndex:range.location];
            // 根据字符串类名生成类对象
            Class modelClass = NSClassFromString(type);
            
            
            if (modelClass) { // 有对应的模型才需要转
                
                // 把字典转模型
                value  =  [modelClass modelWithDict:value];
            }
            
            
        }
        
        // 三级转换:NSArray中也是字典,把数组中的字典转换成模型.
        // 判断值是否是数组
        if ([value isKindOfClass:[NSArray class]]) {
            // 判断对应类有没有实现字典数组转模型数组的协议
            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
                
                // 转换成id类型,就能调用任何对象的方法
                id idSelf = self;
                
                // 获取数组中字典对应的模型
                NSString *type =  [idSelf arrayContainModelClass][key];
                
                // 生成模型
                Class classModel = NSClassFromString(type);
                NSMutableArray *arrM = [NSMutableArray array];
                // 遍历字典数组,生成模型数组
                for (NSDictionary *dict in value) {
                    // 字典转模型
                    id model =  [classModel modelWithDict:dict];
                    [arrM addObject:model];
                }
                
                // 把模型数组赋值给value
                value = arrM;
                
            }
        }
        
        
        if (value) { // 有值,才需要给模型的属性赋值
            // 利用KVC给模型中的属性赋值
            [objc setValue:value forKey:key];
        }
        
    }
    
    return objc;
}

-(NSDictionary *)arrayContainModelClass{
NSDictionary *dic=@{@"aa":@"sta"};
return dic;
}
这个方法主要是确定三级转换时,用哪个类接收

- 二级转换时,如果用类去接收字典,那么需要重写setter方法


测试:

// 解析Plist文件
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
// 获取字典数组NSArray *dictArr = statusDict[@"statuses"];
// 自动生成模型的属性字符串//
[NSObject resolveDict:dictArr[0][@"user"]];

_statuses = [NSMutableArray array];
// 遍历字典数组
for (NSDictionary *dict in dictArr) {
Status *status = [Status modelWithDict:dict];
[_statuses addObject:status];
}
// 测试数据
NSLog(@"%@ %@",_statuses,[_statuses[0] user]);

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,952评论 4 60
  • 2016年的假期,我们去了成都。当时并没有写些什么,时隔将近一年,才记下《成都印象》。 春熙路。 春...
    藏南藏北阅读 646评论 3 7
  • 根据市场调研机构Counterpoint数据,高端智能手机市场的钱全让苹果给赚走了。在售价超过400美金(2745...
    败叔阅读 169评论 0 0
  • 坐地铁时,看见边上男生书包里带了一直小奶猫。小奶猫一路不停的猫猫的叫着,吸引着男生尽量劝哄着,生怕引起别人注意,影...
    萌萌哒哈士奇阅读 215评论 1 1