RunTime

2017-01-12  本文已影响18人  CoderLNHui

简介

内容五大区

发送消息

//    Person *p = [Person alloc];

Person * p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));
p = objc_msgSend([NSObject class], @selector(alloc));

//    p = [p init]; 

 p = objc_msgSend(p, sel_registerName("init"));

 p = objc_msgSend(p, @selector(init));
    

// 调用eat

[p eat];
// 本质:让对象发送消息 

objc_msgSend(p, @selector(eat));
调用类方法的方式:两种

第一种通过类名调用

 [Person eat];

第二种通过类对象调用

[[Person class] eat];

 用类名调用类方法,底层会自动把类名转换成类对象调用

   本质:让类对象发送消息

 objc_msgSend([Person class], @selector(eat));

![](file:///var/folders/0r/rg7wk5l93hx06wq9jn7y5slh0000gn/T/cn.wiz.wiznoteformac/WizNote/84162c9f-5769-4da2-bf63-eca9c06ab026/index_files/916bc807-3d97-4318-9f8b-6b502aeb5012.png)

方法调用流程

什么时候会报 unrecognized selector 的异常?

三次拯救程序崩溃的机会

这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但 Normal forwarding 转发会创建一个 NSInvocation 对象,相对 Normal forwarding 转发更快点,所以这里叫 Fast forwarding

objc中的类方法和实例方法有什么本质区别和联系

类方法是存储在元类对象的方法缓存中

runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)

一个objc对象的isa的指针指向什么?有什么作用?

交换方法


@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    // 方法一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)sh_imageNamed:(NSString *)name;

    // 方法二:交换imageNamed和sh_imageNamed的实现,就能调用imageName,间接调用sh_imageNamed的实现。

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

}

@end

@implementation UIImage (Image)

// 把类加载进内存的时候调用,只会调用一次

+ (void)load

{

    // self -> UIImage

    // 获取imageNamed

    // 获取哪个类的方法

    // SEL:获取哪个方法

    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));

    // 获取xmg_imageNamed

    Method sh_imageNamedMethod = class_getClassMethod(self, @selector(sh_imageNamed:));

    

    // 交互方法:runtime

    method_exchangeImplementations(imageNamedMethod, sh_imageNamedMethod);

    // 调用imageNamed => sh_imageNamedMethod

    // 调用sh_imageNamedMethod => imageNamed

}

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

// 1.加载图片

// 2.判断是否加载成功

+ (UIImage *)sh_imageNamed:(NSString *)name

{

    // 这里调用sh_imageNamed,相当于调用imageName

   UIImage *image = [UIImage sh_imageNamed:name];

    

    if (image) {

        NSLog(@"加载成功");

    } else {

        NSLog(@"加载失败");

    }

    

    return image;

}

@end

交换原理

![](file:///var/folders/0r/rg7wk5l93hx06wq9jn7y5slh0000gn/T/cn.wiz.wiznoteformac/WizNote/84162c9f-5769-4da2-bf63-eca9c06ab026/index_files/d9b2f12f-993f-4682-973f-05d5b3df8ebd.png)

什么是 method swizzling(俗称黑魔法)

![](file:///var/folders/0r/rg7wk5l93hx06wq9jn7y5slh0000gn/T/cn.wiz.wiznoteformac/WizNote/84162c9f-5769-4da2-bf63-eca9c06ab026/index_files/f3b78e2c-ae96-47aa-a4bc-6bb8944eda05.jpg)

![](file:///var/folders/0r/rg7wk5l93hx06wq9jn7y5slh0000gn/T/cn.wiz.wiznoteformac/WizNote/84162c9f-5769-4da2-bf63-eca9c06ab026/index_files/82c98e34-89f3-4b50-961d-748c0fde1202.jpg)

动态添加方法

隐式参数self,_cmd

经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。
* Runtime(动态添加方法):OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中.


@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];   

    Person *p = [[Person alloc] init];

    // 默认person,没有实现run:方法,可以通过performSelector调用,但是会报错。

    // 动态添加方法就不会报错

    [p performSelector:@selector(run:) withObject:@10];
}

@end

@implementation Person

// 没有返回值,1个参数

// void,(id,SEL)

void aaa(id self, SEL _cmd, NSNumber *meter) {

    NSLog(@"跑了%@", meter);

}

// 任何方法默认都有两个隐式参数,self,_cmd(当前方法的方法编号)

// 什么时候调用:只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理

// 作用:动态添加方法,处理未实现

+ (BOOL)resolveInstanceMethod:(SEL)sel

{

    // [NSStringFromSelector(sel) isEqualToString:@"eat"];

    if (sel == NSSelectorFromString(@"run:")) {

        // 动态添加run方法

        // class: 给哪个类添加方法

        // SEL: 添加哪个方法,即添加方法的方法编号

        // IMP: 方法实现 => 函数 => 函数入口 => 函数名(添加方法的函数实现(函数地址))

        // type: 方法类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd

        class_addMethod(self, sel, (IMP)aaa, "v@:@");

        

        return YES;

    }

    

    return [super resolveInstanceMethod:sel];

}

@end

给分类添加属性

原理

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];   

    // 给系统NSObject类动态添加属性name

    NSObject *objc = [[NSObject alloc] init];

    objc.name = @"sh";    

}

@end

@interface NSObject (Property)

// @property分类:只会生成get,set方法声明,不会生成实现,也不会生成下划线成员属性

@property NSString *name;

@end

// 定义关联的key

static const char *key = "name";

@implementation NSObject (Property)

- (void)setName:(NSString *)name

{
    // 让这个字符串与当前对象产生联系,Associated(联合的,关联的) 

    // 第一个参数:给哪个对象添加关联(属性)

    // 第二个参数:关联的key,通过这个key获取,属性名称

    // 第三个参数:关联的value,属性值

    // 第四个参数:关联的策略

   objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (NSString *)name

{ // 根据关联的key,获取关联的值。

    return objc_getAssociatedObject(self, key);

}

@end

怎么添加属性、方法等?

增加实例变量?

分析如下:

因为编译后的类已经注册在runtime中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,同时runtime 会调用 class_setIvarLayoutclass_setWeakIvarLayout 来处理 strong weak 引用,所以不能向存在的类中添加实例变量

运行时创建的类是可以添加实例变量,调用 class_addIvar 函数,但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

关联的对象dealloc

如果是 MRC 代码 则会手动释放实例变量们(iVars)

继承关系中每一层的父类 都再调用 -dealloc

解除所有使用 runtime Associate方法关联的对象, 解除所有 __weak 引用

调用 free()

字典转模型

根据字典自动打印属性Str

@implementation NSDictionary (Property)

// isKindOfClass:判断是否是当前类或者子类

// 生成属性代码 => 根据字典中所有key

- (void)createPropertyCode

{

    NSMutableString *codes = [NSMutableString string];

    // 遍历字典

    [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {

        

        NSString *code;

        if ([value isKindOfClass:[NSString class]]) {

            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;",key];

        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]) {

            code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",key];

        } else if ([value isKindOfClass:[NSNumber class]]) {

             code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@;",key];

        } else if ([value isKindOfClass:[NSArray class]]) {

             code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",key];

        } else if ([value isKindOfClass:[NSDictionary class]]) {

             code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",key];

        }

        // @property (nonatomic, strong) NSString *source;

        

        [codes appendFormat:@"\n%@\n",code];

        

    }];

    

    NSLog(@"%@",codes);

    

}

@end

KVC实现

// 1.遍历字典中所有key,去模型中查找有没有对应的属性

    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {

        

        // 2.去模型中查找有没有对应属性 KVC

        // key:source value:来自sh

        // [item setValue:@"来自sh" forKey:@"source"]

        [item setValue:value forKey:key];
/*

    [item setValue:@"来自sh" forKey:@"source"]:内部

    1.首先去模型中查找有没有setSource,找到,直接调用赋值 [self setSource:@"来自sh"]

    2.去模型中查找有没有source属性,有,直接访问属性赋值  source = value

    3.去模型中查找有没有_source属性,有,直接访问属性赋值 _source = value

    4.找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误

 */

    }];

@implementation Status

+ (instancetype)statusWithDict:(NSDictionary *)dict

{

    Status *status = [[self alloc] init];

    

    [status setValuesForKeysWithDictionary:dict];

    

    return status;

    

}

@end

key找不到的错。

就能继续使用KVC,字典转模型了。


- (void)setValue:(id)value forUndefinedKey:(NSString *)key

{

    

}

RunTime实现

- (void)viewDidLoad {

    [super viewDidLoad];

    //解析Plist文件

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];

    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];

    

    // 设计模型,创建属性代码 => dict

//    [dict[@"user"] createPropertyCode];

    

    // 字典转模型:KVC,MJExtension

    StatusItem *item = [StatusItem modelWithDict:dict];

}
/* 类似下面这种写法
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];

 */
+ (instancetype)modelWithDict:(NSDictionary *)dict

{
    // 0.创建对应的对象

    id objc = [[self alloc] init];

    // runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值

    // 1.获取模型中所有成员变量 key

    // 获取哪个类的成员变量

    // count:成员变量个数

    unsigned int count = 0;

    // 获取成员变量数组

    Ivar *ivarList = class_copyIvarList(self, &count);

    // 遍历所有成员变量

    for (int i = 0; i < count; i++) {

        //根据角标,从数组取出对应的成员属性

       Ivar ivar = ivarList[i];

        //获取成员属性名

        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];

        // 获取成员变量类型

        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

        // @\"User\" -> User

        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];

        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];

        // 获取key

        // 处理成员属性名->字典中的key

        // 从第一个角标开始截取

        NSString *key = [ivarName substringFromIndex:1];

        //根据成员属性名去字典中查找对应的value

        // key:user  value:NSDictionary

        

        id value = dict[key];

        // 二级转换:判断下value是否是字典(字典中还有字典),如果是,字典转换成对应的模型

        // 并且是自定义对象才需要转换

        if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {

            // 字典转换成模型 userDict => User模型

            // 转换成哪个模型

            //根据字符串类名生成类对象

            Class modelClass = NSClassFromString(ivarType);

            
            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) {

            [objc setValue:value forKey:key];

        }

    }  

    return objc;

}

其他用法

然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)

> 那么runtime如何实现weak变量的自动置nil?
runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,假设 weak 指向的对象内存地址是a,那么就会以a为key,在这个 weak hash表中搜索,找到所有以a为key的 weak 对象,从而设置为 nil。
// 模拟下weak的setter方法,大致如下- (void)setObject:(NSObject *)object

{

    objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);

    [object cyl_runAtDealloc:^{

        _object = nil;

    }];

}

Objective-C 对象的结构图

ISA指针

根类(NSObject)的实例变量

倒数第二层父类的实例变量

...

父类的实例变量

类的实例变量

- 根类对象就是NSObject,它的super class指针指向nil

- 类对象既然称为对象,那它也是一个实例。类对象中也有一个 isa 指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,`superclass` 指针指向 `NSObject` 类

[图片上传中。。。(6)]

@implementation Son : NSObject

- (id)init

{

    self = [super init];

    if (self) {

        NSLog(@"%@", NSStringFromClass([self class]));

        NSLog(@"%@", NSStringFromClass([super class]));

    }

    return self;

}

@end

id objc_msgSend(id self, SEL op, ...)

    - 调用 `[super class]`时,会转化成 `objc_msgSendSuper` 函数

id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

 - 第一个参数是 objc_super 这样一个结构体,其定义如下

 struct objc_super {

 __unsafe_unretained id receiver;

 __unsafe_unretained Class super_class;

 };

- 第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self

 - 第二个成员是记录当前类的父类是什么,告诉程序从父类中开始找方法,找到方法后,最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用, 此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son

 - objc Runtime开源代码对- (Class)class方法的实现

-(Class)class { return object_getClass(self);

}

[更多RunTime参考](

https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01《招聘一个靠谱的iOS》面试题参考答案/《招聘一个靠谱的iOS》面试题参考答案(下).md)##)

利用runTime归档与反归档

思考一个问题,现在一个类中有 100 个属性, 我现在的 应用程序中,需要用到 10个类,这十个类都需要归档。如何实现 编码 和 解码方法?

NSCoding
我们在 NSCoding 的协议方法里面做的操作如下:
编码方法 - (void)encodeWithCoder:(NSCoder *)aCoder
,里面做的操作

根据 属性类型
,对属性进行编码。
加上编码 标记
,(秉着 见名知意的原则 标记值与属性名一样)。

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
解码方法里面做的操作
根据 属性类型
以及 编码时加的 标记
, 进行解码。
解码完成后,给 属性赋值(可以使用 kvc 赋值方法)。

因此如果我们:
获取到每个属性类型,就可以 根据类型对属性进行编码。
获取到每个属性名, 就可以在编码的时候添加 标记。
有了标记 和 属性类型,就可以正确的解码

objc/runtime 实现归档反归档
我们需要用到 动态运行时类库中的以下几种类型、函数 来获取 属性名,属性类型

类型
**实例变量 Ivar
**
表示类中的实例变量的一种类型。原类型为 struct objc_ivar *


 ///An opaque type that represents an instance variable.

    typedef struct objc_ivar *Ivar;

    

    struct objc_ivar {

        char *ivar_name                                          OBJC2_UNAVAILABLE;

        char *ivar_type                                          OBJC2_UNAVAILABLE;

        int ivar_offset                                          OBJC2_UNAVAILABLE;

#ifdef __LP64__

        int space                                                OBJC2_UNAVAILABLE;

#endif

    }

**实例变量指针 Ivar *
**

表示 Ivar 类型指针,我们通常用来声明 Ivar 类型数组。比如:用来存储 类中所有实例变量的数组 varArray。

   Ivar *varArray = class_copyIvarList([self class], &count);

函数
获取本类中所有实例变量函数class_copyIvarList



    Ivar * class_copyIvarList(Class cls, unsigned int *outCount)

获取一个数组 Ivar *
, 该数组中含有本类中所有的实例变量,但并不会获取父类中声明的实例变量。 当不使用的时候 使用 free()
释放该数组。

**获取实例变量名 ivar_getName
**

const char * ivar_getTypeEncoding( Ivar ivar)
返回一个 实例变量的名称, 为 C 字符串。

**获取实例变量类型 ivar_getTypeEncoding
**

const char * ivar_getTypeEncoding( Ivar ivar)
获取实例变量的 编码类型, 为 C 字符串。

下面我们按照上面归档反归档的分析出来的思路,使用 runtime 中函数,写一个通用的 NSObject 归档反归档分类。
创建一个NSObject 的分类 NSObject+AGCoding

.h 文件

#import <Foundation/Foundation.h>@interface NSObject (AGCoding) <NSCoding>  // 遵守编码协议@end

.m 文件

#import "NSObject+AGCoding.h"#import <objc/runtime.h>#import <UIKit/UIKit.h>/* 成员变量类型 获取 实例变量的类型 方法 NSLog(@"%s", ivar_getTypeEncoding(array[i])); */static NSString *intType     = @"i"; // intstatic NSString *integerType = @"q"; // longstatic NSString *floatType   = @"f"; // floatstatic NSString *doubleType  = @"d"; // doublestatic NSString *boolType    = @"B"; // boolstatic NSString *imageType   = @"UIImage"; // UIImage 类型static NSString *stringType  = @"NSString"; // NSString 类型// 定义属性字典,用来存储 属性名(key)  类型(value)// 比如:               age            qstatic NSMutableDictionary *proDic = nil;

@implementation NSObject (AGCoding)// 归档是一个编码的过程- (void)encodeWithCoder:(NSCoder *)aCoder{    // (1). 给字典分配空间    proDic = [NSMutableDictionary dictionary];    // (2). 获取类中所有实例变量    unsigned int count; // 属性个数    // 定义Ivar, copy    Ivar *varArray = class_copyIvarList([self class], &count);    // (3). for循环,获取属性名称 属性类型    for (int i = 0; i < count; i++) {        Ivar var = varArray[i];        // 1. 获取属性名称 : age      name       image        const char *cName = ivar_getName(var); // 属性名c字符串        NSString *proName = [[NSString stringWithUTF8String:cName] substringFromIndex:1]; // OC字符串,并且去掉下划线 _        // 2. 获取属性类型 : NSInteger NSString  UIImage 等类型        const char *cType = ivar_getTypeEncoding(var); // 获取变量类型, c 字符串        // 3. kvc 取值, 用于 (5) 中判断是否为 object 类型        id value = [self valueForKey:proName];        // 4. 把属性类型 从 c 转化为 oc 字符串        // c 字符串 转化为 oc 字符串,会加上 @""        // 属性类型        NSString *proType = [NSString stringWithUTF8String:cType]; // oc 字符串        // 5. 如果是OC类型数据,且不属于 NSNumber类,把 @"" 去掉        if ([value isKindOfClass:[NSObject class]] && ![value isKindOfClass:[NSNumber class]]) {            // 截取前: @"@\"NSString\""            // 截取后: @\"NSString\"            NSUInteger length = proType.length;            proType = [proType substringWithRange:NSMakeRange(2, length - 3)];        }        // (4). proDic字典赋值 : 属性名(key)_属性类型(value)        if (![proName isEqualToString:@"proDic"]) {            [proDic setValue:proType forKey:proName];        }        // (5). 根据类型进行编码        if ([proType isEqualToString:integerType]) { // integer 类型            [aCoder encodeInteger:[value integerValue] forKey:proName];        } else if ([proType isEqualToString:imageType]) { // image 类型            [aCoder encodeDataObject:UIImagePNGRepresentation( value)];        } else if ([proType isEqualToString:stringType]) { // string 类型            [aCoder encodeObject:value forKey:proName];        }        // 若再有类型添加即可    } // for 循环,获取结束    // 释放varArray    free(varArray);}// 反归档,是一个解码的过程。- (instancetype)initWithCoder:(NSCoder *)aDecoder{    self = [self init];    if (self) {        // 注意:解档时重新给 proDic赋值        [self setProDic];        //  解码,给 属性赋值        // 注意: 根据属性类型解码,根据属性名 kvc 赋值        // (1) 获取 字典中的属性名        for (NSString *proName in proDic) {            // (2) 获取 属性类型            NSString *proType = proDic[proName];            // (3) 解码, kvc 赋值            if ([proType isEqualToString:integerType]) { // integer 类型                // 解码                NSInteger number = [aDecoder decodeIntegerForKey:proName];                // 赋值                [self setValue:@(number) forKey:proName];            } else if ([proType isEqualToString:imageType]) { // image 类型                UIImage *image = [UIImage imageWithData:[aDecoder decodeDataObject]];                [self setValue:image forKey:proName];            } else if ([proType isEqualToString:stringType]) { // string 类型                NSString *string = [aDecoder decodeObjectForKey:proName];                [self setValue:string forKey:proName];            }            // 若再有类型添加即可        } // for 循环结束    }    return self;}// 解码时重新给当前类的proDic赋值- (void)setProDic{    // (1). 给字典分配空间    proDic = [NSMutableDictionary dictionary];    // (2). 获取类中所有实例变量    unsigned int count; // 属性个数    // 定义Ivar, copy    Ivar *varArray = class_copyIvarList([self class], &count);    // (3). for循环,获取属性名称 属性类型    for (int i = 0; i < count; i++) {        Ivar var = varArray[i];        // 1. 获取属性名称 : age      name       image        const char *cName = ivar_getName(var); // 属性名c字符串        NSString *proName = [[NSString stringWithUTF8String:cName] substringFromIndex:1]; // OC字符串,并且去掉下划线 _        // 2. 获取属性类型 : NSInteger NSString  UIImage 等类型        const char *cType = ivar_getTypeEncoding(var); // 获取变量类型, c 字符串        // 3. 把属性类型 从 c 转化为 oc 字符串        // c 字符串 转化为 oc 字符串,会加上 @""        // 属性类型        NSString *proType = [NSString stringWithUTF8String:cType]; // oc 字符串        // 获取 @ 的个数        NSArray *array = [proType componentsSeparatedByString:@"@"];        // 4. 如果属性字符串中,多了 @"" 把 @"" 去掉        if (array.count >= 2) {            // 截取前: @"@\"NSString\""            // 截取后: @\"NSString\"            NSUInteger length = proType.length;            proType = [proType substringWithRange:NSMakeRange(2, length - 3)];        }        // (4). proDic字典赋值 : 属性名(key)_属性类型(value)        if (![proName isEqualToString:@"proDic"]) {            [proDic setValue:proType forKey:proName];        }    } // for 循环,获取结束// 释放varArray    free(varArray);}@end

上一篇下一篇

猜你喜欢

热点阅读