美文网首页
runtime(五)

runtime(五)

作者: dandelionYD | 来源:发表于2019-03-29 21:02 被阅读0次

    本文Demo代码见gitHubDemo

    消息转发应用

    myPerson.h
    #import <Foundation/Foundation.h>
    @interface myPerson : NSObject
    - (void)run;
    - (void)test;
    - (void)other;
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson
    - (void)run{
        NSLog(@"run-123");
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        // 本来能调用的方法
        if ([self respondsToSelector:aSelector]) {
            return [super methodSignatureForSelector:aSelector];
        }
        
        // 找不到的方法
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    
    // 找不到的方法,都会来到这里
    - (void)forwardInvocation:(NSInvocation *)anInvocation{
        NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
    }
    @end
    
    【main】
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            [p run];
            [p test];
            [p other];
        }
        return 0;
    }
    打印:
    14.消息转发应用[26463:15281230] run-123
    14.消息转发应用[26463:15281230] 找不到test方法
    14.消息转发应用[26463:15281230] 找不到other方法
    

    接下来我们看看Runtime的具体的使用

    runtime_15.png
    myPerson.h
    #import <Foundation/Foundation.h>
    @interface myPerson : NSObject
    -(void)eat;
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson
    -(void)eat{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    myDog.h
    #import <Foundation/Foundation.h>
    @interface myDog : NSObject
    -(void)eat;
    @end
    
    myDog.m
    #import "myDog.h"
    @implementation myDog
    -(void)eat{
        NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "myPerson.h"
    #import "myDog.h"
    
    void  run(){
        printf("run----\n");
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson alloc]init];
            [p eat];
            
            object_setClass(p,[myDog class]);//修改了isa指向
            [p eat]; //调用了myDog里面的run方法
            
            //object_isClass 判断一个OC对象是否为Class
            NSLog(@"%d %d %d",
                            object_isClass(p),
                            object_isClass([myPerson class]),
                            object_isClass(object_getClass([myPerson class]))
                            );
    
            
            NSLog(@"==========动态创建类=============");
            // 创建类
            Class newClass = objc_allocateClassPair([NSObject class], "myCat", 0);
            class_addIvar(newClass, "_age", 4, 1, @encode(int));
            class_addIvar(newClass, "_weight", 4, 1, @encode(int));
            class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
            // 注册类
            objc_registerClassPair(newClass);
            id cat = [[newClass alloc] init];
            [cat setValue:@10 forKey:@"_age"];
            [cat setValue:@20 forKey:@"_weight"];
            [cat run];
    
            NSLog(@"%@ %@", [cat valueForKey:@"_age"], [cat valueForKey:@"_weight"]);
    
            // 在不需要这个类时释放
            objc_disposeClassPair(newClass);
            
        }
        return 0;
    }
    打印:
    15.runtime的使用-类[32416:15549185] -[myPerson eat]
    15.runtime的使用-类[32416:15549185] -[myDog eat]
    15.runtime的使用-类[32416:15549185] 0 1 1
    15.runtime的使用-类[32416:15549185] ==========动态创建类=============
    run----
    15.runtime的使用-类[32416:15549185] 10 20
    

    • 成员变量和属性
    runtime_16.png runtime_17.png
    myPerson.h
    #import <Foundation/Foundation.h>
    @interface myPerson : NSObject
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson{
        int _age;
        NSString *_name;
        float _height;
    }
    @end
    
    【main.m】
    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // 获取成员变量信息 和 设置
            Ivar ageIvar = class_getInstanceVariable([myPerson class], "_age");
            Ivar nameIvar = class_getInstanceVariable([myPerson class], "_name");
            Ivar heightIvar = class_getInstanceVariable([myPerson class], "_height");
            
            NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
            NSLog(@"%s %s", ivar_getName(nameIvar), ivar_getTypeEncoding(nameIvar));
            NSLog(@"%s %s", ivar_getName(heightIvar), ivar_getTypeEncoding(heightIvar));
            
            myPerson *p = [[myPerson alloc] init];
            
            /*
            object_setIvar(p, nameIvar, @"123");
            object_setIvar(p, ageIvar, (__bridge id)(void *)10);
            object_setIvar(p, heightIvar,(__bridge id)(void *)180);
             */
            //坑 object_getIvar 不支持非对象 https://www.jianshu.com/p/e498142f788d
           
           //解决:
            object_setIvar(p, nameIvar, @"123");
            object_setIvar(p, ageIvar, @(10));
            object_setIvar(p, heightIvar,@(180));
            NSLog(@"name = %@", object_getIvar(p, nameIvar));
            NSLog(@"age = %@", object_getIvar(p, ageIvar));
            NSLog(@"height = %@", object_getIvar(p, heightIvar));
           
            NSLog(@"====成员变量的数量====");
            // 成员变量的数量
            unsigned int count;
            Ivar *ivars = class_copyIvarList([myPerson class], &count);
            for (unsigned int i = 0; i<count; i++) {
                // 取出i位置的成员变量
                Ivar ivar = ivars[I];
                NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
            }
            free(ivars);
        }
        return 0;
    }
    打印:
    16.runtime的使用-成员变量和属性[35903:15840080] _age I
    16.runtime的使用-成员变量和属性[35903:15840080] _name @"NSString"
    16.runtime的使用-成员变量和属性[35903:15840080] _height f
    16.runtime的使用-成员变量和属性[35903:15840080] name = 123
    16.runtime的使用-成员变量和属性[35903:15840080] age = 10
    16.runtime的使用-成员变量和属性[35903:15840080] height = 180
    16.runtime的使用-成员变量和属性[35903:15840080] ====成员变量的数量====
    16.runtime的使用-成员变量和属性[35903:15840080] _age I
    16.runtime的使用-成员变量和属性[35903:15840080] _name @"NSString"
    16.runtime的使用-成员变量和属性[35903:15840080] _height f
    
    项目实践: 
    //    unsigned int count;
    //    Ivar *ivars = class_copyIvarList([UITextField class], &count);
    //    for (int i = 0; i < count; i++) {
    //        // 取出i位置的成员变量
    //        Ivar ivar = ivars[I];
    //        NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
    //    }
    //    free(ivars);
        
    self.textField.placeholder = @"请输入用户名";   
    [self.textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
    
    <===>   
    //    UILabel *placeholderLabel = [self.textField valueForKeyPath:@"_placeholderLabel"];
    //    placeholderLabel.textColor = [UIColor redColor];
    
    
    <===>    
    //    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    //    attrs[NSForegroundColorAttributeName] = [UIColor redColor];
    //    self.textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:@"请输入用户名" attributes:attrs];
    
    字典转模型
    myPerson.h
    #import <Foundation/Foundation.h>
    @interface myPerson : NSObject
    @property(nonatomic,assign)int ID;
    @property(nonatomic,assign)double height;
    @property(nonatomic,assign)double weight;
    @property(nonatomic,strong)NSString *name;
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson
    @end
    
    NSObject+json.h
    #import <Foundation/Foundation.h>
    @interface NSObject (json)
    + (instancetype)my_objectWithJson:(NSDictionary *)json;
    @end
    
    NSObject+json.m
    #import "NSObject+json.h"
    #import <objc/runtime.h>
    
    @implementation NSObject (json)
    + (instancetype)my_objectWithJson:(NSDictionary *)json{
        id obj = [[self alloc] init];
        unsigned int count;
        Ivar *ivars = class_copyIvarList(self, &count);
        for (unsigned int i = 0; i < count; i++) {
            // 取出i位置的成员变量
            Ivar ivar = ivars[I];
            NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
            [name deleteCharactersInRange:NSMakeRange(0, 1)];
            
            // 设值
            id value = json[name];
            if ([name isEqualToString:@"ID"]){
                value = json[@"id"];
            }
            [obj setValue:value forKey:name];
        }
        free(ivars);
        return obj;
    }
    @end
    
    【main】
    #import <Foundation/Foundation.h>
    #import "NSObject+json.h"
    #import "myPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // 字典转模型
            NSDictionary *json = @{
                                   @"id" : @20,
                                   @"height" : @20,
                                   @"weight" : @60,
                                   @"name" : @"Jack"
                                   };
            myPerson *person = [myPerson my_objectWithJson:json];
            NSLog(@"id=>%d height=>%f  weight=>%f  name=>%@",person.ID,person.height,person.weight,person.name);
    
        }
        return 0;
    }
    
    打印:
    17.runtime的使用-字典转模型[36043:15855031] id=>20 height=>20.000000  weight=>60.000000  name=>Jack
    
    • 方法


      runtime_18.png
    runtime_19.png
    交换方法
    myPerson.h
    #import <Foundation/Foundation.h>
    @interface myPerson : NSObject
    -(void)run;
    -(void)walk;
    @end
    
    myPerson.m
    #import "myPerson.h"
    @implementation myPerson
    -(void)run{
        NSLog(@"%s",__FUNCTION__);
    }
    -(void)walk{
         NSLog(@"%s",__FUNCTION__);
    }
    @end
    
    【main】
    #import <Foundation/Foundation.h>
    #import "myPerson.h"
    #import <objc/runtime.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            myPerson *p = [[myPerson  alloc]init];
            Method runMethod = class_getInstanceMethod([myPerson class], @selector(run));
            Method walkMethod = class_getInstanceMethod([myPerson class], @selector(walk));
            method_exchangeImplementations(runMethod, walkMethod);
            [p run];
            
             //替换方法
            class_replaceMethod(
                                                  [myPerson class],
                                @selector(walk), 
                                imp_implementationWithBlock(^{
                NSLog(@"block实现方法的实现啦");
            }), "v@:");
            [p walk];
        }
        return 0;
    }
    打印:
    18.runtime的使用--交换IMP[36122:15870626] -[myPerson walk]
    18.runtime的使用--交换IMP[36173:15876293] block实现方法的实现啦
    

    补充:

    类名 真身
    NSArray __NSArrayI
    NSMutableArray __NSArrayM
    NSDictionary __NSDictionaryI
    NSMutableDictionary __NSDictionaryM

    示例

    1.拦截UIButton的点击事件 
    UI
    #import <UIKit/UIKit.h>
    @interface UIControl (Extension)
    @end
    
    UIControl+Extension.m
    #import "UIControl+Extension.h"
    #import <objc/runtime.h>
    
    @implementation UIControl (Extension)
    + (void)load{
        // hook:钩子函数
        Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
        Method method2 = class_getInstanceMethod(self, @selector(my_sendAction:to:forEvent:));
        method_exchangeImplementations(method1, method2);
    }
    
    - (void)my_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
        NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action));
        
        // 调用系统原来的实现
        [self my_sendAction:action to:target forEvent:event];
        
        //    if ([self isKindOfClass:[UIButton class]]) {
        //        // 拦截了所有按钮的事件
        //
        //    }
    }
    @end
    
    2.NSMutableArray 添加nil判别
    NSMutableArray+Extension.h
    #import <Foundation/Foundation.h>
    @interface NSMutableArray (Extension)
    @end
    
    NSMutableArray+Extension.m
    #import "NSMutableArray+Extension.h"
    #import <objc/runtime.h>
    
    @implementation NSMutableArray (Extension)
    + (void)load{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        // 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
        Class cls = NSClassFromString(@"__NSArrayM");
            Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
            Method method2 = class_getInstanceMethod(cls, @selector(my_insertObject:atIndex:));
            method_exchangeImplementations(method1, method2);
        });
    }
    
    - (void)my_insertObject:(id)anObject atIndex:(NSUInteger)index{
        if (anObject == nil) return;
        
        [self my_insertObject:anObject atIndex:index];
    }
    @end
    
    
    3.拦截NSDictionary
    NSMutableDictionary+Extension.h
    #import <Foundation/Foundation.h>
    @interface NSMutableDictionary (Extension)
    @end
    
    NSMutableDictionary+Extension.m
    #import "NSMutableDictionary+Extension.h"
    #import <objc/runtime.h>
    
    @implementation NSMutableDictionary (Extension)
    + (void)load{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class cls = NSClassFromString(@"__NSDictionaryM");
            
            unsigned int count;
            Method *methodList = class_copyMethodList(cls, &count);
            for (unsigned int i = 0; i < count; i++) {
                Method method = methodList[I];
                NSString *methodName = NSStringFromSelector(method_getName(method));
                NSLog(@"方法名:%d----%@",i,methodName);
            }
            free(methodList);
            
            
            Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKey:));
            Method method2 = class_getInstanceMethod(cls, @selector(my_setObject:forKey:));
            method_exchangeImplementations(method1, method2);
            
            Class cls2 = NSClassFromString(@"__NSDictionaryI");
            Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:));
            Method method4 = class_getInstanceMethod(cls2, @selector(my_objectForKeyedSubscript:));
            method_exchangeImplementations(method3, method4);
        });
    }
    
    - (void)my_setObject:(id)obj forKey:(id<NSCopying>)key{
        if (!obj) return;
        if (!key) return;
        [self my_setObject:obj forKey:key];
    }
    
    - (id)my_objectForKeyedSubscript:(id)key{//dict[nil]
        if (!key) return nil;
        return [self my_objectForKeyedSubscript:key];
    }
    @end
    

    友情链接:

    相关文章

      网友评论

          本文标题:runtime(五)

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