runtime常用方法
2021-01-18 本文已影响0人
冰点雨
1.类
//动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
//注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
//销毁一个类
void objc_disposeClassPair(Class cls)
//获取isa指向的Class
Class object_getClass(id obj)
//设置isa指向的Class
Class object_setClass(id obj, Class cls)
//判断一个OC对象是否为Class
BOOL object_isClass(id obj)
//判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)
//获取父类
Class class_getSuperclass(Class cls)
1.1 动态修改指针指向对象
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Car : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
- (void)run;
@end
@interface Person : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
- (void)run;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Car
- (void)run{
NSLog(@"%s", __func__);
}
@end
@implementation Person
- (void)run{
// NSLog(@"---selector:%@",NSStringFromSelector(_cmd));
NSLog(@"%s", __func__);
NSLog(@"---class:%@",object_getClass(self));
// BOOL isClass = object_isClass(self);
// NSLog(@"---isClass:%d",isClass);
}
@end
- (void)testCard{
Person *p = [[Person alloc]init];
[p run];
object_setClass(p, [Car class]);
[p run];
}
控制台打印参数
2021-01-18 11:24:51.757886+0800 BlockTest[71022:1903556] -[Person run]
2021-01-18 11:24:51.758050+0800 BlockTest[71022:1903556] ---class:Person
2021-01-18 11:24:51.758163+0800 BlockTest[71022:1903556] -[Car run]
1.2 动态创建类
- (void)testClass{
Class newClass = objc_allocateClassPair([NSObject class], "Dog", 0);
// 建议在注册类之前添加这些成员变量、属性和方法
//(成员变量是只读的,类的结构一旦创建,就不能再添加成员变量 ro_t 方法的添加可以放在注册之后,
// 因为方法是放在rw_t中)
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_weight", 4, 1, @encode(int));
SEL sel = NSSelectorFromString(@"run");
IMP imp = [self methodForSelector:sel];
class_addMethod(newClass, @selector(run), imp, "v@:");
// 注册类
objc_registerClassPair(newClass);
id dog = [[newClass alloc] init];
[dog setValue:@10 forKey:@"_age"];
[dog setValue:@20 forKey:@"_weight"];
[dog run];
NSLog(@"age:%@ name:%@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
// 在不需要这个类时释放
// objc_disposeClassPair(newClass);
}
- (void)run{
NSLog(@"======00000");
}
2021-01-18 11:43:35.349399+0800 BlockTest[74008:1922892] ======00000
2021-01-18 11:43:35.349694+0800 BlockTest[74008:1922892] age:10 name:20
2.成员变量
//获取一个实例变量信息 (获取的描述信息,并不是实际值和信息)
Ivar class_getInstanceVariable(Class cls, const char *name)
//拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
//设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)
//动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
//获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
2.1 获取成员变量信息
- (void)testIvars
{
// 3: 获取成员变量信息 获取的描述信息,并不是实际值和信息
Ivar ageIvar = class_getInstanceVariable([Person class], "_age");
NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
// 3.1: 设置和获取成员变量的值
Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
Person *person = [[Person alloc] init];
object_setIvar(person, nameIvar, @"xiao hua"); // 设置的name string
object_setIvar(person, ageIvar, (__bridge id)(void *)10);
NSLog(@"---- %@ %ld", person.name, person.age);
// 4: 成员变量的数量 copy一个成员变量列表 最常用
unsigned int count;
Ivar *ivars = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i]; // 指针可以当做数组来用,相当于是*(ivars + i) 指针跟着移动 然后取出地址
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
free(ivars); // 需要释放
}
2021-01-18 11:31:01.533314+0800 BlockTest[71213:1909377] _age q
2021-01-18 11:31:01.533442+0800 BlockTest[71213:1909377] ---- xiao hua 10
2021-01-18 11:31:01.533531+0800 BlockTest[71213:1909377] _name @"NSString"
2021-01-18 11:31:01.533612+0800 BlockTest[71213:1909377] _age q
3.属性
//获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
//拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
//动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
//动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
//获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
4.方法
//获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
//方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
//拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
//动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
//动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
//获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)
//选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
//用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
4.1 交换方法
- (void)replaceMethodTest{
Person *p = [[Person alloc]init];
// 1:替换方法:对象方法,(一般要替换一个类方法 这里需要传进去的就是元类对象,需注意)
SEL sel = NSSelectorFromString(@"myRun");
IMP imp = [self methodForSelector:sel];
class_replaceMethod([Person class], @selector(run), imp, "v@:");
// 2:imp_implementationWithBlock 这里传入的是block,将block包装秤了一个imp方法
// class_replaceMethod([Person class], @selector(run), imp_implementationWithBlock(^{
// NSLog(@"123123");
// }), "v");
[p run];
}
- (void)myRun{
NSLog(@"======myRun");
}
4.2 建分类交换方法
//HookTool.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface HookTool : NSObject
+ (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector;
@end
NS_ASSUME_NONNULL_END
//HookTool.m
#import "HookTool.h"
#import <objc/runtime.h>
@implementation HookTool
//调用该方法,在单例模式下进行 保证方法只被交换一次
+ (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector
{
if (cls == nil) {
NSLog(@"传入的交换类不能为空");
return;
}
Class class = cls;
// 原方法结构体和替换方法结构体
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果当前类没有原方法的实现IMP,先调用class_addMethod来给原方法添加默认的方法实现IMP
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//要交换的方法不存在
if (!originalMethod) {
class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
method_setImplementation(swizzledMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));
}
if (originalMethod) {
if (didAddMethod) {
// 添加成功了证明自己没有,那么直接替换
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
// 自己有直接交换
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
}
UIControl分类
#import "UIControl+UserStatidtics.h"
#import "HookTool.h"
@implementation UIControl (UserStatidtics)
+(void)load{
static dispatch_once_t onceToken;
_dispatch_once(&onceToken, ^{
// 只在开发模式下,才会出现交换
#ifdef DEBUG
SEL originalSelector = @selector(sendAction:to:forEvent:);
SEL swizzledSelector = @selector(swiz_sendAction:to:forEvent:);
[HookTool swizzlingInClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector];
#endif
});
}
#pragma mark - Method Swizzling
- (void)swiz_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event;
{
//可插入埋点代码
NSLog(@"==== 交换方法");
[self swiz_sendAction:action to:target forEvent:event];
}
@end
2021-01-18 14:18:27.345827+0800 BlockTest[76175:1978395] -[UIControl(UserStatidtics) swiz_sendAction:to:forEvent:]
2021-01-18 14:18:27.345953+0800 BlockTest[76175:1978395] ==== 交换方法
2021-01-18 14:18:27.346053+0800 BlockTest[76175:1978395] ---btn Click
参考文章:https://blog.csdn.net/qq_27909209/article/details/82699408