当前位置: 移动技术网 > 移动技术>移动开发>IOS > iOS - runtime 常用方法举例说明

iOS - runtime 常用方法举例说明

2018年03月13日  | 移动技术网移动技术  | 我要评论
使用的自定义类,如下: 动态变量控制 动态添加方法 动态交换两个方法的实现 拦截并替换方法 在方法上增加额外功能 对runtime进行归纳总结,代码如下: 1、加载运行时头文件: 2、具体实现代码: ...
  • 使用的自定义类,如下:
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic,copy) NSString *name;
@property(nonatomic,copy) NSString *sex;
-(NSString *)sayName;
-(NSString *)saySex;

@end
  • 动态变量控制
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [Person new];
    _person.name = @"xiaoming";
    NSLog(@"XiaoMing first answer is %@",self.person.name);
    
    [self sayName];
}

- (void)sayName
{
    unsigned int count = 0;
    Ivar *ivar = class_copyIvarList([self.person class], &count);
    for (int i = 0; i<count; i++) {
        Ivar var = ivar[i];
        const char *varName = ivar_getName(var);
        NSString *proname = [NSString stringWithUTF8String:varName];
        
        if ([proname isEqualToString:@"_name"]) {   //这里别忘了给属性加下划线
            object_setIvar(self.person, var, @"daming");
            break;
        }
    }
    NSLog(@"XiaoMing change name  is %@",self.person.name);
}
  • 动态添加方法
- (void)viewDidLoad {
    [super viewDidLoad];
    self.person = [Person new];
    [self sayFrom];
}

- (void)sayFrom
{
    class_addMethod([self.person class], @selector(guess), (IMP)guessAnswer, "v@:");
    if ([self.person respondsToSelector:@selector(guess)]) {
        //Method method = class_getInstanceMethod([self.xiaoMing class], @selector(guess));
        [self.person performSelector:@selector(guess)];
        
    } else{
        NSLog(@"Sorry,I don't know");
    }
}

void guessAnswer(id self,SEL _cmd){
    NSLog(@"function: guessAnswer");
    NSLog(@"__%@__", NSStringFromSelector(_cmd));
}

- (void) guess{
    NSLog(@"function: guess");
    NSLog(@"__%@__", NSStringFromSelector(_cmd));
}
  • 动态交换两个方法的实现
- (void)viewDidLoad {
    [super viewDidLoad];
    self.person = [Person new];
    
    NSLog(@"%@",_person.sayName);
    NSLog(@"%@",_person.saySex);
    
    Method m1 = class_getInstanceMethod([self.person class], @selector(sayName));
    Method m2 = class_getInstanceMethod([self.person class], @selector(saySex));
  
    method_exchangeImplementations(m1, m2);
}
  • 拦截并替换方法
@interface Tool : NSObject

+ (instancetype)sharedManager;
- (NSString *)changeMethod;
- (void)addCount;

@end

#import "Tool.h"

@interface Tool ()

@property (nonatomic, assign) NSInteger count;

@end

@implementation Tool

+ (instancetype)sharedManager {
    static Tool *_sInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sInstance = [[Tool alloc] init];
    });
    
    return _sInstance;
}

- (NSString *)changeMethod
{
    return @"haha,你的方法被我替换掉了";
}

- (void)addCount
{
    _count += 1;
    
    NSLog(@"点击次数------%ld", _count);
}

@end
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //这里也可以使用 [self.person class],不过要先初始化
    Method m1 = class_getInstanceMethod([Person class], @selector(sayName));
    Method m2 = class_getInstanceMethod([Tool class], @selector(changeMethod));
    
    method_exchangeImplementations(m1, m2);
}
  • 在方法上增加额外功能
@implementation FiveViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame = CGRectMake(100, 100, 100, 100);
    [button setTitle:@"按钮" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(ButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)ButtonClick
{
    NSLog(@"按钮被点击了");
}

@end
  • 对runtime进行归纳总结,代码如下:

  1、加载运行时头文件:

#import <objc/runtime.h>

  2、具体实现代码:

/** 自定义类:MyObject */
//头文件
#import <Foundation/Foundation.h>

@interface MyObject : NSObject
@property(strong,nonatomic) NSString *string;
@property(strong,nonatomic) NSArray  *array;

- (void)method1;
- (void)method2;
- (void)classMethod1;
@end

//源文件
#import "MyObject.h"
#import <objc/runtime.h>
@interface MyObject (){
    NSInteger _instance1;
    NSString  *_instance2;
}

@property(assign,nonatomic) NSInteger intrger;
- (void)method3WithArge1:(NSInteger)arge1 arge2:(NSString *)arge2;
@end
@implementation MyObject -(void)classMethod1{ } - (void)method1{ NSLog(@"cell Method1"); } - (void)method2{ } - (void)method3WithArge1:(NSInteger)arge1 arge2:(NSString *)arge2{ NSLog(@"arge1 %ld arge2 %@",arge1,arge2); } + (BOOL)resolveInstanceMethod:(SEL)sel{ NSString *selector = NSStringFromSelector(sel); if ([selector isEqualToString:@"method1"]) { class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:"); } return [super resolveInstanceMethod:sel]; } void functionForMethod1(id self,SEL _cmd){ NSLog(@"%@ %p",self,_cmd); } - (instancetype)forwardingTargetForSelector:(SEL)aSelector{ NSString *selector = NSStringFromSelector(aSelector); if([selector isEqualToString:@"method2"]){   //return; } return [super forwardingTargetForSelector:aSelector]; } - (void)forwardInvocation:(NSInvocation *)anInvocation{ } @end /** 实现代码 */ #import <objc/runtime.h> @implementation ViewController - (void)viewDidLoad { [super viewDidLoad];   //1.类相关函数   //得到类名 ViewController NSLog(@"%s",class_getName([self class]));   //父类类名   //This is superClass 0x10f426da0 isa指针地址 NSLog(@"This is superClass %p",class_getSuperclass([self class]));   //是否是元类   //Is MateClass 0 NSLog(@"Is MateClass %d",class_isMetaClass([self class]));   //实例变量大小   //848 size_t a = class_getInstanceSize([self class]); NSLog(@"%ld",a); unsigned int outCount = 0; MyObject *objtect = [[MyObject alloc] init]; Class cls = objtect.class; //类名 NSLog(@"Class name %s",class_getName(cls)); //父类名字 NSLog(@"SuperClass name %s",class_getName(class_getSuperclass(cls))); //是否元类 NSLog(@"Is MetaClass %@",(class_isMetaClass(cls))?@"":@"NOT"); //变量大小 NSLog(@"MyObject size %ld",class_getInstanceSize(cls)); //变量信息 Ivar string = class_getInstanceVariable(cls, "_string"); if (string != NULL) { NSLog(@"MyObject instance messge %s",ivar_getName(string)); } // 属性操作 objc_property_t *v = class_copyPropertyList(cls, &outCount); for (NSInteger i = 0; i< outCount; i++) { objc_property_t property = v[i]; NSLog(@"property is %s ",property_getName(property)); } free(v); //成员变量 Ivar *vars = class_copyIvarList(cls, &outCount); for (NSInteger i =0; i< outCount; i++) { Ivar ivar = vars[i]; NSLog(@"copyIvarList is %s is %ld",ivar_getName(ivar),i); } free(vars); //方法操作 Method *methods = class_copyMethodList(cls, &outCount); for (NSInteger i =0; i< outCount; i++) { Method met = methods[i]; #pragma clang diagnostic ignored"-Wformat" NSLog(@"Method is %s",method_getName(met)); } free(methods); // Format specifies type 'char *' but the argument has type 'SEL _Nonnull' // 获取方法名 Method method = class_getInstanceMethod(cls,@selector(method1)); NSLog(@"Method name is %s",method_getName(method)); // 判断方法是否存在 #pragma clang diagnostic ignored"-Wundeclared-selector" NSLog(@"Have you method %d",class_respondsToSelector(cls, @selector(method3WithArge1:arge2:))); // 指向函数实现的指针,相当于方法的实现 IMP imp = class_getMethodImplementation(cls, @selector(method1)); imp(); //动态创建类 #pragma clang diagnostic ignored" -Wunused-variable" // 注:运行时规定,只能在objc_allocateClassPair与objc_registerClassPair两个函数之间为类添加变量 // 1.添加一个自定义的类 类名是MySubClass // 父类class,类名,额外空间 Class myClass = objc_allocateClassPair(objtect.class, "MySubClass", 0); // 2.增加方法,交换方法 //注: v@: 意思是 v是void @:没有返回参数 if( class_addMethod(myClass, @selector(mysubMethod1),(IMP)mysubMethod1, "v@:")){ class_replaceMethod(myClass, @selector(method1), (IMP)mysubMethod1,"v@:"); } /* 3.增加一个NSSsting类型属性 属性名myString 变量size sizeof(NSString) 对齐 指针类型的为log2(sizeof(NSString*)) 类型 @encode(NSString*) class_addIvar(class,变量名,变量size,对齐,类型) */ //添加同名属性会失败 BOOL isd = class_addIvar(myClass, "_myString", sizeof(NSString *), log(sizeof(NSString *)), @encode(NSString *)); NSLog(@"属性是否添加成功 %d",isd); /* 特性相关编码 属性的特性字符串 以 T@encode(type) 开头, 以 V实例变量名称 结尾,中间以特性编码填充,通过property_getAttributes即可查看 特性编码 具体含义 R readonly C copy & retain ARC strong N nonatomic G(name) getter=(name) S(name) setter=(name) D @dynamic W weak P 用于垃圾回收机制 */ //@T objc_property_attribute_t type; type.name = "T"; type.value = @encode(NSString *); //copy objc_property_attribute_t owership = {"C",""}; //nonatomic objc_property_attribute_t oeership2 = {"N",""}; //V_属性名 objc_property_attribute_t var = {"V","_myString"}; //特性数组 objc_property_attribute_t attributes[] = {type,owership,oeership2,var}; //向类中添加名为myString的属性,属性的特性包含在attributes中 class_addProperty(myClass, "myString", attributes, 4); unsigned int propertyCount; objc_property_t * properties = class_copyPropertyList(myClass, &propertyCount); for (int i = 0; i<propertyCount; i++) { NSLog(@"属性的名称为 : %s",property_getName(properties[i])); NSLog(@"属性的特性字符串为: %s",property_getAttributes(properties[i])); } //释放属性列表数组 free(properties); //在应用中注册由objc_allocateClassPair创建的类 objc_registerClassPair(myClass); id instance = [[myClass alloc] init]; [instance performSelector:@selector(mysubMethod1)]; [instance performSelector:@selector(method1)]; // 销毁一个类及相关联的类 // 不过需要注意的是,如果程序运行中还存在类或其子类的实例,则不能调用针对类调用该方法 //objc_disposeClassPair(myClass); //关联对象 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; view.backgroundColor = [UIColor redColor]; [view setTapActionWithBlock:^{ NSLog(@"saAS"); }]; [self.view addSubview:view];   //SEL   //Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL   //SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法   //而对于字符串的比较仅仅需要比较他们的地址就可以了,可以说速度上无语伦比   //sel : 0x104dd7735 /* 三种方法来获取SEL: sel_registerName函数 Objective-C编译器提供的@selector() NSSelectorFromString()方法 */ SEL sel1 = @selector(method1); NSLog(@"sel : %p", sel1); } static void mysubMethod1(id self,SEL _cmd){ NSLog(@"这是添加的方法"); } @end

 

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网