Runtime

作者: 蓝蓝的白云 | 来源:发表于2016-07-28 21:27 被阅读9次

    1.归档与反归档

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "Dog.h"
    @interface Person : NSObject
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSString *gender;
    @property (nonatomic, strong) NSNumber *age;
    @property (nonatomic, assign) NSInteger weight;
    @property (nonatomic, strong) Dog *dog;
    + (Person *)personWithName:(NSString *)name gender:(NSString *)gender age:(NSNumber *)age weight:(NSInteger)weight;
    - (void)getPersonMessage;
    - (void)walkonTheStreet:(NSString *)str;
    @end
    
    #import "Person.h"
    #import <objc/runtime.h>// 导入<objc/runtime.h>
    @interface Person ()<NSCoding>
    @end
    
    @implementation Person
    - (void)encodeWithCoder:(NSCoder *)aCoder
    {
        unsigned int outCount;
        Ivar *ivarList = class_copyIvarList([Person class], &outCount);
        for (NSInteger i = 0; i < outCount; i++) {  // 采用for循环遍历属性,无需一一归档
            const char *cName = ivar_getName(ivarList[i]);
            NSString *name = [NSString stringWithUTF8String:cName];
            [aCoder encodeObject:[self valueForKey:name] forKey:name];
        }   
    }
    - (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        self = [super init];
        if (self) {
            unsigned int outCount;
            Ivar *ivarList = class_copyIvarList([Person class], &outCount);
            for (NSInteger i = 0; i < outCount; i++) { // 采用for循环遍历属性,无需一一反归档
                const char *cName = ivar_getName(ivarList[i]);
                NSString *name = [NSString stringWithUTF8String:cName];
                [self setValue:[aDecoder decodeObjectForKey:name] forKey:name];
            }
        }
        return  self;
    }
    
    

    如下方法

    Ivar *class_copyIvarList(Class cls, unsigned int *outCount)      //获取所有成员变量
     const char *ivar_getName(Ivar v)            //获取某个成员变量的名字
     const char *ivar_getTypeEncoding(Ivar v)   //获取某个成员变量的类型编码
     Ivar class_getInstanceVariable(Class cls, const char *name)    //获取某个类中指定名称的成员变量
     id object_getIvar(id obj, Ivar ivar)    //获取某个对象中的某个成员变量的值
     void object_setIvar(id obj, Ivar ivar, id value)    //设置某个对象的某个成员变量的值
     TypeEncoding:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1  // 编码形式
    

    打印实例变量

    - (NSString *)description
    {
        /*
         * class: 要获取的某个类, outCount: 通过这一个函数执行之后会将成员变量的个数赋值到此
         */
        unsigned int outCount;
        Ivar *ivarList = class_copyIvarList([Person class], &outCount);
        for (NSInteger i = 0; i < outCount; i ++) {
    //        每次获取一个成员变量
            Ivar ivar = ivarList[i];
    //        打印成员变量的名字和类型编码
            NSLog(@"name = %s, type = %s",ivar_getName(ivar), ivar_getTypeEncoding(ivar));
        }
        return nil;
    }
    

    2.消息转发

    OC的消息发送机制根据方法名来寻找声明完成的实例方法的实现方法,当类中的方法只声明而未实现时,系统会在逐级父类中寻找方法的实现,当父类中均无方法的实现时, 系统会执行以下方法,故可为未实行的方法进行消息转发,添加实现
    1    + resolveInstanceMethod:(SEL)sel      // 为一个实例方法动态添加实现
    + resolveClassMethod:(SEL)sel      //   为一个类方法动态添加实现
    2     - (id)forwardingTargetForSelector:(SEL)aSelector
    //为没有实现的方法指定一个对象
    3     - (void)forwardInvocation:(NSInvocation *)anInvocation
    //子类重载这个方法为消息指定其他对象
    

    a. 为一个实例方法动态添加实现

    + (BOOL)resolveInstanceMethod:(SEL)sel 
    {
       //    将方法转换为字符串
        NSString *selString = NSStringFromSelector(sel);
        if ([selString isEqualToString:@"walkonTheStreet:"]) {
         //   为一个没有实现的方法动态添加实现
            /* cls: 类
             name: 没有实现的方法
             IMP: 要添加的实现
             types: 动态添加的实现的类型编码*/
            class_addMethod(self, @selector(walkonTheStreet:), (IMP)walkFunc, "V@:@");  // void的编码为 V,  SEL的编码为 :
        }
        return [super resolveInstanceMethod:sel];
    }
    
    void walkFunc(id self,SEL sel, NSString *str){
        NSLog(@"Person---%s------%@", __func__, str);
    }
    

    b. 为没有实现的方法指定一个对象

    方式1
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        NSString *selString = NSStringFromSelector(aSelector);
       if ([selString isEqualToString:@"walkonTheStreet:"]) {
           self.dog = [Dog new];
           return self.dog;
       }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    方式2
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        if ([Dog instanceMethodSignatureForSelector:anInvocation.selector]) {
            self.dog = [Dog new];
            [anInvocation invokeWithTarget:self.dog];
        }
    }
    

    // 给方法制定一个有效的签名

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
        if (!methodSignature) {
            methodSignature = [Dog instanceMethodSignatureForSelector:aSelector];
        }
        return methodSignature;
    }
    

    3.给类目添加实例变量

    延展可为类添加属性和方法, 但外界不可访问; 类目可为类添加方法,但无法添加实例变量, 采用runtime可为类目添加外界可访问的实例变量
    #import <Foundation/Foundation.h>
    @interface NSDictionary (Mydict)
    @property (nonatomic,strong) NSString *name;
    @end
    
    #import "NSDictionary+Mydict.h"
    #import <objc/runtime.h>
    @implementation NSDictionary (Mydict)
    
    /*
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)     //为某个类关联某个对象
    id objc_getAssociatedObject(id object, const void *key)
    //获取到某个类的某个关联对象
    void objc_removeAssociatedObjects(id object) //移除已经关联的对象
    */
    // 关联后此属性name在外界可以访问,并且通过移除对象方法随时移除
    - (void)setName:(NSString *)name
    {
    //    objc: 要关联的对象  key: 成员变量对应的key值
    //    @selector(属性名) value: value
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);  // set方法
    }
    
    - (NSString *)name
    {
        return objc_getAssociatedObject(self, @selector(name));  //get方法
    }
    

    相关文章

      网友评论

          本文标题:Runtime

          本文链接:https://www.haomeiwen.com/subject/wagajttx.html