Runtime学习-看这里


runtime原理

原理参考:http://www.jianshu.com/p/69b6cf840cd8

runtime主要是消息发送和消息转发机制,通过objc_msgsend来实现,调用一个方法就是相应的执行objc_msg(obj,SEL,...参数)。

类也是一个结构体

实例方法
struct _class_t OBJC_CLASS_$_Person  = {
    .isa = &OBJC_METACLASS_$_Person, // 指向Person-metaclass
    .superclass = &OBJC_CLASS_$_NSObject, // 指向NSObject-class
    .cache = &_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_Person, // 包含了实例方法, ivar信息等
};

类方法
struct _class_t OBJC_METACLASS_$_Person = {
    .isa = &OBJC_METACLASS_$_NSObject, // 指向NSObject-metaclass
    .superclass = &OBJC_METACLASS_$_NSObject, // 指向NSObject-metaclass
    .cache = &_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_METACLASS_RO_$_Person, // 包含了类方法
};

执行方法

[person read:book]
会被编译成
objc_msgsend(person,@selector(read:),book)

向person这个类或者对象发送执行read方法,参数是book

先开始发送消息

  1. 通过isa指针指向该person类
  2. 查找他的实例方法列表里面是否有该方法,有执行这个方法,没有下一步,
  3. 查找他的类方法列表里面是否有该方法,有跳转代码实现,没有下一步
  4. 查找其继承的父类是否有该方法,有跳转代码执行,没有执行下一步
  5. 没有找到 开始消息转发

消息转发

  1. 看该类是否可以动态添加改方法的实现,就是是否有实现 resolveInstanceMethod并在其中实现read方法。如果有消息转发结束,否则继续转发
  2. 看有没有其他对象执行这条消息,forwardingTargetSelector,也没有实现这个方法,则下一步
  3. 会将消息封装成NSInvocation,祈祷你有去处理这个消息方法,forwardInvocation, 没有的话 程序闪退 找不到该fang'fa

参考:

  • http://www.jianshu.com/p/e071206103a4
  • http://www.jianshu.com/p/46dd81402f63
  • YYKit

Runtime的实际应用

  • 给对象动态的添加属性-在类别中使用非常方便,或者某个成员变量你只在某个地方传递使用一两次也可以动态,不用设置一大堆的成员变量。
  • 对象的属性变量动态的更新值-私有的还是只读的都可以设置
  • 给对象动态添加方法-类似属性的懒加载,用到的时候才去调用。方法也是要加载到内存中,对一些不常用到的这么处理可以节省资源。
  • 交换两个方法的实现-交换方法不是重写方法,是对这两个方法名对应的方法地址对换。
  • NSCoding,或者NSSecretCoding自动归档解档-通过获取他的所有成员变量的名字和类型,实现动态设置。
  • 字典数据自动转模型-1.可以先通过遍历字体的类型打印出他的每个字段的属性写法也可以自己手动定义每一个属性。 2.动态的给每一个属性赋值
  • 动态设置一个类-还在啃...

OC运行的时候都会转化成Runtime来执行,每个OC的方法,底层必然有一个与之对应的runtime方法,方法的实质就是通过Runtime的 objc_msgsend来发送消息。runtime是运行时的语言,是系统运行时的机制,主要是消息机制。

OC是运行时机制 定义了方法 不去实现 编译的时候不会报错 但是如果在运行的时候调用它就会报错 编辑的时候不能决定真正调用的是哪一个方法 在运行时才通过方法名找对应的函数

C是编译时机制 定义了方法 不去实现 编辑的时候就会报错 编辑的时候就觉得方法调用的是哪一个 直接顺序执行


1.动态给对象添加属性


主要两个方法:Set和Get

属性定义之后都有这两个方法,重写这两个方法:

本质是给这个类添加这个属性的关联,这个属性的内存空间并没有添加到这个类空间中

//定义一个静态常量
char * const kNewProperty = "kRuntimeAddNewProperty";

- (NSString *)newProperty
{
    return objc_getAssociatedObject(self, kNewProperty);
}

-(void)setNewProperty:(NSString *)newPro
{
    objc_setAssociatedObject(self, kNewProperty, newPro, OBJC_ASSOCIATION_COPY);
}

使用之前先导入 #import

  • id object 要给哪个对象附加该属性 一般直接self
  • const void *key 该属性对应的key
  • id value 这个object的这个key对应的属性值,传nil就清空这个值
  • objc_AssociationPolicy policy 枚举 有assign,retain,copy

上面如果每次都要定义一个key很麻烦的话,也可以这样子。

//定义一个静态常量

char * const kNewProperty = "kRuntimeAddNewProperty";

- (NSString *)newProperty
{
    return objc_getAssociatedObject(self, @selector(newProperty));
}

-(void)setNewProperty:(NSString *)newPro
{
    objc_setAssociatedObject(self, @selector(newProperty), newPro, OBJC_ASSOCIATION_COPY);
}

@SEL可以理解成C语言里的函数指针,利用@select(属性名)生成一个函数指针,这个函数指针由属性名生成,这样子就能保证set和get方法能对应一个唯一的key。参考http://blog.csdn.net/jeffasd/article/details/51706210

YYKit中定义了有一个宏

#ifndef YYSYNTH_DYNAMIC_PROPERTY_OBJECT
#define YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_) \
- (void)_setter_ : (_type_)object { \
    [self willChangeValueForKey:@#_getter_]; \
    objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_ ## _association_); \
    [self didChangeValueForKey:@#_getter_]; \
} \
- (_type_)_getter_ { \
    return objc_getAssociatedObject(self, @selector(_setter_:)); \
}
#endif

使用

//传入setter和getter方法,属性的类型,要设置的属性 _cmd就是当前函数的方法
YYSYNTH_DYNAMIC_PROPERTY_OBJECT(networkActivityInfo, setNetworkActivityInfo, RETAIN_NONATOMIC, _YYUIApplicationNetworkIndicatorInfo *);

====

2.动态给对象更新属性值


遍历对象中的所有的成员变量,获取到那个变量更新设置他的值

这个先了解一下runtime怎么去获取对象的属性和方法

主要用到

Class:typedef struct objc_class *Class;通过objc_class的结构体定义

id:typedef struct objc_object *id;通过objc_object的结构体定义

Class是objc_class结构类型的指针中可找到

id是objc_object结构类型的指针中可找到

isa是一个Objective-C Class类型的指针,实例对象有个isa的属性,他指向Class,而这个Class里面也有个isa属性,他指向meteClass。

参考http://blog.csdn.net/uxyheaven/article/details/38113901

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;

    Class super_class 父类                                            
    const char *name 名字                                           
    long version 版本信息                                            
    long info 运行期间使用的一些位标识                                               
    long instance_size 该实例变量大小                         
    struct objc_ivar_list *ivars 成员变量列表                            
    struct objc_method_list **methodLists 方法列表                   
    struct objc_cache *cache 方法缓存           
    struct objc_protocol_list *protocols 协议数组                  

} OBJC2_UNAVAILABLE;

objc_property_t:An opaque type that represents an Objective-C declared property.一个不可见的类型代表OC声明的属性,个人理解是就是对象属性的信息

objc_property_t对应的方法:

objc_property_t class_getProperty(Class cls, const char *name)通过具体属性名获取该指定的属性

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)获取该对象的所有的属性列表,返回的outCount这个属性列表的个数, 返回多objc_property_t前多了一个*号

const char *property_getName(objc_property_t property)通过属性获取该属性的名字

const char *property_getAttributes(objc_property_t property)获取属性的类型的编码,通过这些编码就可以判断出这个属性是什么类型

objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)获取类的所有属性信息

char *property_copyAttributeValue(objc_property_t property, const char *attributeName)拷贝某个属性信息

objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)获取协议列表

objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)通过指定的协议


Method:An opaque type that represents a method in a class definition.一个不可见的类型代表一个类的方法定义.

typedef struct objc_method *Method;

typedef struct objc_ method {

SEL method_name; 该方法的名称

char *method_types;  该方法的参数的类型

IMP method_imp; 指向该方法的的具体实现的函数指针

};

Method是objc_method结构类型的指针.

插入...

Class IMP SEL差异

参考:http://www.jianshu.com/p/ab1340a68940

IMP:是函数的指针,具体的方法地址。

Class:是指向类结构体的指针,每一个类实例对象的第一个实例变量是指向该对象的结构体的指针,叫isa。通过该指针,对象可以访问他对应的类以及他的相应父类.

SEL:他的定义是typedef struct objc_selector  *SEL;是指向objc_selector的结构体的指针,表示方法的名字/签名.不同的类可以拥有相同的Selector,这个是没问题的,不同的实例对象performSelector相同的Selector的时候,通过方法链表中根据Selector去查找具体的方法实现的IMP。通过IMP去执行具体的实现代码。

Method class_getInstanceMethod(Class cls, SEL name)获取实例的方法

Method class_getClassMethod(Class cls, SEL name)获取类方法

IMP class_getMethodImplementation(Class cls, SEL name)获取该方法的调用地址,

Method *class_copyMethodList(Class cls, unsigned int *outCount)获取该类的所有方法列表

SEL method_getName(Method m)获取具体的方法名

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)动态添加方法

IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)替换方法

void method_exchangeImplementations(Method m1, Method m2)交换两个方法的指针地址


Ivar:objc_ivar结构类型的实例变量

id object_getIvar(id obj, Ivar ivar)获取具体的实例变量

void object_setIvar(id obj, Ivar ivar, id value)设置实例变量的值

Ivar *class_copyIvarList(Class cls, unsigned int *outCount)获取类实例变量的列表

const char *ivar_getName(Ivar v)获取实例变量的名字


  • 获取属性列表

/**
 *  获取属性列表
 */
- (void)getListProperty
{
    NSMutableString * propertyStr = [[NSMutableString alloc] init];
    
    unsigned int count;
    objc_property_t * propertyList = class_copyPropertyList([self class], &count);
    for(unsigned int i = 0; i < count; i++)
    {
        const char * propertyName = property_getName(propertyList[i]);
        
        NSString * name = [NSString stringWithUTF8String:propertyName];
        
        [propertyStr appendFormat:@"\n%@",name];
    }
    
    NSLog(@"___property___%@",propertyStr);
}
  • 获取方法列表
/**
 *  获取方法列表
 */
- (void)getListMethod
{
    NSMutableString * methodStr = [[NSMutableString alloc] init];
    
    unsigned int count;
    Method * methodList = class_copyMethodList([self class], &count);
    for(unsigned int i = 0; i < count; i++)
    {
        [methodStr appendFormat:@"\n%@",NSStringFromSelector(method_getName(methodList[i]))];
    }
    
    NSLog(@"___method___%@",methodStr);
}
  • 获取成员变量列表
/**
 *  获取成员变量列表
 */
- (void)getListIvar
{
    NSMutableString * ivarStr = [[NSMutableString alloc] init];
    
    unsigned int count;
    Ivar * ivarList = class_copyIvarList([self class], &count);
    for(unsigned int i = 0; i < count; i++)
    {
        NSString * ivar = [NSString stringWithUTF8String:ivar_getName(ivarList[i])];
        [ivarStr appendFormat:@"\n%@",ivar];
    }
    NSLog(@"___ivar___%@",ivarStr);
}
  • 获取协议列表
/**
 *  获取协议列表
 */
- (void)getListProtocol
{
    NSMutableString * protocolStr = [[NSMutableString alloc] init];
    
    unsigned int count;
    Protocol * __unsafe_unretained * protocolList = class_copyProtocolList([self class], &count);
    for(unsigned int i = 0; i < count; i++)
    {
        const char * protocolName = protocol_getName(protocolList[i]);
        
        [protocolStr appendFormat:@"\n%@",[NSString stringWithUTF8String:protocolName]];
    }
    
    NSLog(@"___protocol___%@",protocolStr);
}
  • 动态的修改更新实例遍历的值

看下面代码例子.

/**
 *  改变属性值  设置成只读属性的值也可以更改.....
 *  m中的也能改
 */
- (void)updatePropertyContent
{
    RTObj * obj = [RTObj new];
    
    unsigned int count;
    Ivar * ivarList = class_copyIvarList([obj class], &count);
    for(unsigned int i = 0; i < count; i++)
    {
        Ivar var = ivarList[i];
        NSString * varName= [NSString stringWithUTF8String:ivar_getName(var)];
        if([varName isEqualToString:@"_itemName"])
        {
            object_setIvar(obj, var, @"季磊磊");
        }
        else if([varName isEqualToString:@"_itemContent"])
        {
            //只读的属性
            object_setIvar(obj, var, @"改变值了????");
        }
        else if ([varName isEqualToString:@"_itemDataList"])
        {
            NSLog(@"该属性的值 %@",object_getIvar(obj, var));
            
            //成员变量
            object_setIvar(obj, var, @[@"你完了",@"被人偷改了",@"怎么办",@"nothing"]);
        }
        else if ([varName isEqualToString:@"_itemSelectIndex"])
        {
            //m文件中的成员变量
            object_setIvar(obj, var, @(100));
            
            NSLog(@"m文件中的成员变量__%@",object_getIvar(obj,var));
        }
        else if ([varName isEqualToString:@"_itemDescription"])
        {
            //m文件中的属性
            object_setIvar(obj, var, @"描述描述描述描述描述描述描述");
            
            NSLog(@"m文件中的属性__%@",object_getIvar(obj,var));
        }
    }
    
    NSLog(@"___change property value name = %@, content = %@, list = %@",obj.itemName, obj.itemContent, [obj getDataList]);
}



3.给对象动态添加方法


了解这个之前先了解一下 performSelector

performSelector是运行的时候系统负责去动态调用方法,在编译的时候不做任何校验。所以运行的时候如果调用的函数不存在,程序就是奔溃,所以一般使用performselector之前都要通过responseToSelector校验一下方法是否实现。保证程序的健壮性.

如果一个类在是实现文件中实现了某个函数但是在头文件中却没有公开声明,在已知接口的前提下,可以使用performSelector通过函数名直接调用这个类的‘私有方法’


在使用performSelector的时候如果方法未定义,会有警告,去除警告的方法:http://www.jianshu.com/p/1465c2783241


参考:http://www.jianshu.com/p/60773495dc1e

动态添加方法运用的就是 . 懒加载

使用场景:

  • 如果一个类的方法有很多,加载类的时候比较耗资源,可以把一些不常用的方法弄成动态添加的方式,分布加载。

  • 在实现文件.m中实现了该方法但是没有在头文件中声明可以通过performSelector来直接调用。

  • 听说面试很多都会问到这个问题 你有没有用到过performSelector 其他就是问你有没有用过runtime动态添加方法

动态添加方法主要实现:

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

  • cls:要添加的方法的类
  • name:要添加的方法名
  • imp:要添加的方法对应的地址指针 函数的地址
  • 方法的类型:返回值+参数类型
 c  A char
 i  An int
 s  A short
 l  A long l is treated as a 32-bit quantity on 64-bit programs.
 q  A long long
 C  An unsigned char
 I  An unsigned int
 S  An unsigned short
 L  An unsigned long
 Q  An unsigned long long
 f  A float
 d  A double
 B  A C++ bool or a C99 _Bool
 v  A void
 *  A character string (char *)
 @  An object (whether statically typed or typed id)
 #  A class object (Class)
 :  A method selector (SEL)
 [array type]  An array
 {name=type...}  A structure
 (name=type...)  A union
 bnum  A bit field of num bits
 ^type  A pointer to type
 ?  An unknown type (among other things, this code is used for function pointers)

看代码例子:

定义:

+(BOOL)resolveInstanceMethod:(SEL)sel
{
    if(sel == @selector(reloadRTData))
    {
        //动态添加方法        
        class_addMethod([self class], @selector(reloadRTData), (IMP)reloadRTData, "v@:");
    }
    else if (sel == @selector(passRTValue:))
    {
        class_addMethod(self, sel, (IMP)passRTValue, "v@:@");
    }
}

void reloadRTData(id self, SEL _cmd)
{
    NSLog(@"添加了 reloadRTData方法###%@",NSStringFromSelector(_cmd));
}

void passRTValue(id self, SEL _cmd, NSString * message)
{
    NSLog(@"passValue is %@",message);
}

说明如果使用performselector调用方法的时候如果这个方法未实现他会调用resolveInstanceMethod,传入方法名的信息,可以在这里面动态的判断去动态的添加方法。


要添加的动态方法定义:

void reloadRTData(id self, SEL _cmd)他的两个参数,一个self,一个_cmd.方法名本身,这两个是影藏参数,OC的每个方法都有这两个参数。

例如定义的的方法void reloadRTData(id self, SEL _cmd)

他的最后一个参数 方法类型的写法是:v@:

第一个v是指返回的类型是void: void->V

第二个@是指object类型id的self:id->@

第三个:是指方法的选择器SEL的_cmd:SEL->:

所以上面方法类型参数写法是v@:

方法都有id self, SEL _cmd都有这两个参数,所以一定是有@:这个组合

使用:

//在RTObj中实现
if([obj respondsToSelector:@selector(reloadRTData)])
{
    [obj performSelector:@selector(reloadRTData)];
}

4.交换两个方法的实现


参考:http://www.jianshu.com/p/4ded7ee15ec1

原理:交换两个方法的实现是交换两个方法对应的函数地址IMP

使用场景:可以拦截替换系统的方法,变成自己定义的方法

主要用到的runtime中的void method_exchangeImplementations(Method m1, Method m2)方法,交换两个实例的方法。

一般交换方法的时候都写在类方法load中:

例子:

+(void)load
{
    Method m1 = class_getInstanceMethod([self class], @selector(methodExchange3));
    Method m2 = class_getInstanceMethod([self class], @selector(methodExchange4));
    
    method_exchangeImplementations(m1, m2);
}


4.NSCoding和NSSecretCoding自动编码解码


以NSSecretCoding为例,他是集成于NSCoding,encode和encode的时候比NSCoding多了一个类型的判断.

看获取所有属性以及其类型的代码:

/** 通过属性的名字获取该属性的类型*/
- (NSDictionary *)getPropertyClassesByName
{
    NSMutableDictionary * dictionary = objc_getAssociatedObject([self class], _cmd);
    
    if(dictionary)
    {
        return dictionary;
    }
    
    dictionary = [NSMutableDictionary dictionary];
    
    Class subClass = [self class];
    while (subClass != [NSObject class] && subClass)
    {
        unsigned int propertyCount;
        objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
        
        for(int i = 0; i < propertyCount; i++)
        {
            objc_property_t property = properties[i];
            const char * propertyName = property_getName(property);
            
            NSString * key = @(propertyName);
            
            char * ivar = property_copyAttributeValue(property, "V");
            if(ivar)
            {
                NSString *ivarName = @(ivar);
                
                if ([ivarName isEqualToString:key] ||
                    
                    [ivarName isEqualToString:[@"_" stringByAppendingString:key]])
                    
                {
                    Class propertyClass = nil;
                    
                    char *typeEncoding = property_copyAttributeValue(property, "T");
                    
                    switch (typeEncoding[0])
                    
                    {
                            
                        case 'c': // Numeric types
                            
                        case 'i':
                            
                        case 's':
                            
                        case 'l':
                            
                        case 'q':
                            
                        case 'C':
                            
                        case 'I':
                            
                        case 'S':
                            
                        case 'L':
                            
                        case 'Q':
                            
                        case 'f':
                            
                        case 'd':
                            
                        case 'B':
                            
                        {
                            
                            propertyClass = [NSNumber class];
                            
                            break;
                            
                        }
                            
                        case '*': // C-String
                            
                        {
                            
                            propertyClass = [NSString class]; 
                            
                            break; 
                            
                        } 
                            
                        case '@': // Object 
                            
                        { 
                            NSString * classStr = @(typeEncoding);
                            classStr = [classStr stringByReplacingOccurrencesOfString:@"@" withString:@""];
                            classStr = [classStr stringByReplacingOccurrencesOfString:@"\"" withString:@""];
                            classStr = [classStr stringByReplacingOccurrencesOfString:@"\\" withString:@""];
                            
                            
                            propertyClass = NSClassFromString(classStr);
                            
                            break; 
                            
                        } 
                            
                        case '{': // Struct 
                            
                        { 
                            
                            propertyClass = [NSValue class]; 
                            
                            break; 
                            
                        } 
                            
                        case '[': // C-Array 
                            
                        case '(': // Enum 
                            
                        case '#': // Class 
                            
                        case ':': // Selector 
                            
                        case '^': // Pointer 
                            
                        case 'b': // Bitfield 
                            
                        case '?': // Unknown type 
                            
                        default: 
                            
                        { 
                            
                            propertyClass = nil; // Not supported by KVC 
                            
                            break; 
                            
                        } 
                            
                    } 
                    
                    free(typeEncoding); 
                    
                    // If known type, add to dictionary 
                    
                    if (propertyClass)
                    {
                        dictionary[key] = propertyClass;
                    }
                }
            }
            
            free(ivar);

            subClass = [subClass superclass];
        }
        
        objc_setAssociatedObject([self class], _cmd, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    return dictionary;
        
}

说明

NSMutableDictionary * dictionary = objc_getAssociatedObject([self class], _cmd);
    
if(dictionary)
{
    return dictionary;
}

objc_setAssociatedObject([self class], _cmd, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

头部有一个通过该函数名来获取这个函数名的属性值,

尾部有一个通过该函数名来设置以这个函数名为属性的值是dictionary

这么做将属性的类型信息储存下来,下回再次调用的时候就不用重新遍历了.

while (subClass != [NSObject class] && subClass)

subClass = [subClass superclass];

while循环遍历是确定该类是否有父类,如果他有父类信息,则父类的属性信息也需要存储到dic中

然后通过获取成员变量的列表,遍历成员变量,通过property_copyAttributeValue来获取类型的编码信息。

第二个参数const char *attributeName V是属性的名字 T编码的类型。

  • property_copyAttributeValue函数,返回的char *在使用完后需要调用free()释放。

  • property_copyAttributeList函数,返回值在使用完后需要调用free()释放。

类型编码字符串形式的文档:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html

coding decode 解码

-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if(self = [super init])
    {
        [[self getPropertyClassesByName] enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull propertyClass, BOOL * _Nonnull stop) {
           
            id object = [aDecoder decodeObjectOfClass:propertyClass forKey:key];
            
            if (object)
            {
                [self setValue:object forKey:key];
            }
        }];
    }
    return self;
}

coding encode 编码

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    for (NSString *key in [self getPropertyClassesByName])
    {
        id object = [self valueForKey:key];
        
        if (object)
        {
            [aCoder encodeObject:object forKey:key];
        }
    }
}

这个就实现了自动编码解码.


5.字典数据自动转模型


自动生成属性

通过字典的key和value的类型直接方便的输出每个属性的定义方式。唯一方便的就是不用自己对应去输入每一个属性。

代码:

/**
 *  通过字典 拼接出其中所有的 属性类型字符串
 */
+ (void)logAllPropertyAtDataDic:(NSDictionary *)dataDic
{
    NSMutableString * strM = [NSMutableString new];
    
    [dataDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
        NSString * type;
        if([obj isKindOfClass:NSClassFromString(@"__NSCFString")])
        {
            type = @"NSString";
        }
        else if ([obj isKindOfClass:NSClassFromString(@"__NSCFBoolean")])
        {
            type = @"BOOL";
        }
        else if ([obj isKindOfClass:NSClassFromString(@"__NSArrayI")])
        {
            type = @"NSArray";
        }
        else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")])
        {
            type = @"int";
        }
        else if ([obj isKindOfClass:NSClassFromString(@"__NSDictionaryI")])
        {
            type = @"NSDictionary";
        }
        
        NSString * str;
        if([type isEqualToString:@"NSString"])
        {
            str = [NSString stringWithFormat:@"@property (nonatomic, copy) %@ *%@;",type,key];
        }
        else if([type hasPrefix:@"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);
    
    //打印出来黏贴到类声明文件中
}

参考:YYKitNSObject+YYModel

博文链接:http://www.jianshu.com/p/2cb815053b55

将字典或者json转化成Model


6.动态的创建类


参考:http://www.jianshu.com/p/2973241f5348

个人感觉不是很实用,可能会在某些特定的环境下使用到,看代码:

[super viewDidLoad];
    //定义一个 Person 类, 继承自 NSObject
    Class Person = objc_allocateClassPair([NSObject class], "Person", 0);
    //添加属性
    objc_property_attribute_t type = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership = { "C", "" }; // C = copy
    objc_property_attribute_t backingivar  = { "V", "_privateName" };
    objc_property_attribute_t attrs[] = { type, ownership, backingivar };
    class_addProperty(Person, "name", attrs, 3);
    //添加方法
    class_addMethod(Person, @selector(name), (IMP)nameGetter, "@@:");
    class_addMethod(Person, @selector(setName:), (IMP)nameSetter, "v@:@");
    //注册该类
    objc_registerClassPair(Person);

    //获取实例
    id instance = [[Person alloc] init];
    NSLog(@"%@", instance);
    [instance setName:@"hxn"];

    NSLog(@"%@", [instance name]);

定义这个类,然后添加属性和方法。可以的话 大家可以自己封装一下。








如果有错误或者意见欢迎提出...

共进...




你可能感兴趣的:(Runtime学习-看这里)