runtime(五)

2019-03-29  本文已影响0人  dandelionYD

本文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_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

友情链接:

上一篇下一篇

猜你喜欢

热点阅读