类相关
获取isa指向的Class
Class object_getClass(id obj);//其本质是获取isa指针所指向的数据
-
方法实现:
Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return nil; }
-
示例:
/** object_getClass(id obj);其本质是获取isa指针所指向的数据 */ GLStudent *stu = [[GLStudent alloc] init]; Class cls = object_getClass(stu); //获取的是 GLStudent Class metaCls = object_getClass(cls); //获取的是GLStudent的元类
设置isa指向的Class
Class object_setClass(id obj, Class cls);//其方法本质就是改变ISA的指向
-
方法实现:
Class object_setClass(id obj, Class cls) { if (!obj) return nil; if (!cls->isFuture() && !cls->isInitialized()) { _class_initialize(_class_getNonMetaClass(cls, nil)); } return obj->changeIsa(cls); }
-
示例
@implementation GLPerson - (void)run { NSLog(@"Person is runing"); } @end @implementation GLCar - (void)run { NSLog(@"Car is running"); } @end
GLPerson *person = [[GLPerson alloc] init]; [person run]; //Person is runing object_setClass(person, [GLCar class]); [person run]; //Car is running
判断一个OC对象是否为Class
BOOL object_isClass(id obj);
//该方法的本质是判断obj的ISA指向的对象是否是metaClass
// 而判断是否是metaClass的依据是: class_ro_t中的flags
-
方法实现
BOOL object_isClass(id obj) { if (!obj) return NO; return obj->isClass(); } inline bool objectivec_object::isClass() { if (isTaggedPointer()) return false; return ISA()->isMetaClass();//判断该对象的ISA指向的对象是否是mateClass } struct objectivec_class : objectivec_object { bool isMetaClass() { return data()->ro->flags & RO_META; //是否是mateClass判断的依据 } }
判断一个Class是否为元类
BOOL class_isMetaClass(Class cls);
-
方法实现
//同上,判断cls是否是元类对象,即判断ro里的flags BOOL class_isMetaClass(Class cls) { if (!cls) return NO; return cls->isMetaClass(); } struct objectivec_class : objectivec_object { bool isMetaClass() { return data()->ro->flags & RO_META; //是否是mateClass判断的依据 } }
动态创建、注册、销毁一个类
// 动态创建一个类(参数:父类,类名,额外的内存空间)
Class objectivec_allocateClassPair(Class superclass, const char *name, size_t extraBytes);
//注册一个类(要在类注册之前添加成员变量)
void objectivec_registerClassPair(Class cls);
//销毁一个类
void objectivec_disposeClassPair(Class cls);
OC
允许动态注册一个类(指定父类和类名),Runtime API还可以为这个动态创建的类添加方法、成员变量(需要在注册类之前添加)、属性、协议等等。动态创建类后,需要调用函数注册该类,这样才可以使用。
示例:
/// 创建一个类
/**
* 创建一对类,会将这个类以及这个的元类创建出来
* 第一个参数:[NSObject class],指定父类
* 第二个参数:类名,C语言字符串
* 第三个参数:额外的存储空间,通常为0
*/
Class GLDogCls = objectivec_allocateClassPair([NSObject class], "GLDog", 0);
/// 添加成员变量
/// class_addIvar方法只能在 objectivec_allocateClassPair 之后和 objectivec_registerClassPair之前调用
/// Ivar在底层类结构中,存储在class_ro_t中,是只读的。
/// 新注册的类(objectivec_allocateClassPair),还没有存储到class_ro_t中,
/// objectivec_registerClassPair 注册之后,class_ro_t数据成形,就不可添加了。
/**
* 添加一个 int 类型的 age 变量
* 第一个参数:要添加成员变量的类,不能是元类字符串
* 第二个参数:成员变量的名称,C语言字符串
* 第三个参数:成员变量的大小
* 第四个参数:alignment 内存对齐,通常使用 log2(sizeof(point-type)
*/
BOOL isSuccessAddForAge = class_addIvar(GLDogCls, "_age", sizeof(int), log2(sizeof(int)), @encode(int));
BOOL isSuccessAddForName = class_addIvar(GLDogCls, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
NSLog(@"%d,%d",isSuccessAddForAge,isSuccessAddForName);// 1,1
/// 添加方法
void printInfo(id self,SEL _cmd) {
NSLog(@"my name is %@, and I am %@ years old",
[self valueForKey:@"name"],
[self valueForKey:@"age"]
);
}
class_addMethod(GLDogCls, @selector(printInfo), (IMP)printInfo, "v@:");
/// 注册类
objectivec_registerClassPair(GLDogCls);
/// 创建实例,以及属性赋值
id dogInstance = [[GLDogCls alloc] init];
[dogInstance setValue:@2 forKey:@"age"];
[dogInstance setValue:@"Luck" forKey:@"name"];
//调用方法
[dogInstance performSelector:@selector(printInfo)];//my name is Luck, and I am 2 years old
/// 当某个类确定不再使用时,可以使用 objectivec_disposeClassPair 释放该类
objectivec_disposeClassPair(GLDogCls);
成员变量相关
获取类中某个成员变量的信息
//获取类中某个成员变量的信息
Ivar class_getInstanceVariable(Class cls, const char *name);
//获取成员变量的相关信息
const char *ivar_getName(Ivar v);
const char *ivar_getTypeEncoding(Ivar v);
示例
@interface GLPerson : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@end
// 获取类中某个成员变量的信息
Ivar nameIvar = class_getInstanceVariable([GLPerson class], "_name");
NSLog(@"%s %s",ivar_getName(nameIvar),ivar_getTypeEncoding(nameIvar)); //_name @"NSString"
Ivar ageIvar = class_getInstanceVariable([GLPerson class], "_age");
NSLog(@"%s %s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar)); //_age i
设置和获取成员变量的值
// 设置成员变量的值
void object_setIvar(id obj, Ivar ivar, id value);
// 获取成员变量的值
id object_getIvar(id obj, Ivar ivar);
示例:
/// 设置和获取成员变量的值
GLPerson *person = [[GLPerson alloc] init];
// 设置name的值,不走setter方法
object_setIvar(person, nameIvar, @"小李");
// 获取name的值,不走getter方法
NSLog(@"%@",object_getIvar(person, nameIvar)); //小李
/// 基本数据类型写法稍有不同
// 设置age的值
object_setIvar(person, ageIvar, (__bridge id)(void *)10);
// 获取age的值
void * ageValue = (__bridge void *)(object_getIvar(person, ageIvar));
NSLog(@"%d", (int)ageValue);//10
拷贝实例变量列表
//拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount);
示例:
/// 拷贝实例变量列表(最后需要调用free释放)
unsigned int ivarCount;
Ivar *ivarList = class_copyIvarList([GLPerson class], &ivarCount);
//遍历实例变量列表
for (int i = 0; i < ivarCount; i++) {
Ivar ivar = ivarList[i];
NSLog(@"%s %s",ivar_getName(ivar),ivar_getTypeEncoding(ivar));
}
free(ivarList);
方法相关
获取一个实例方法、类方法
// 获取实例方法
Method class_getInstanceMethod(Class cls, SEL name)
// 获取类方法
Method class_getClassMethod(Class cls, SEL name)
获取方法的相关信息
// 带有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);
拷贝方法列表
// 最后需要调用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);
示例:
@interface GLPerson : NSObject
- (void) run;
@end
@implementation GLPerson
- (void) run {
NSLog(@"%s",__func__);
}
@end
GLPerson *person = [[GLPerson alloc] init];
[person run]; //-[GLPerson run]
//使用C语言函数替换实现
void myrun(id self,SEL _cmd) {
NSLog(@"myrun");
}
class_replaceMethod([GLPerson class], @selector(run), (IMP)myrun, "v@:");
[person run]; //myrun
//也可使用block替换实现
class_replaceMethod([GLPerson class], @selector(run), imp_implementationWithBlock(^(id self,SEL _cmd){
NSLog(@"self is %@",self);
}), "v@:");
[person run]; //self is <GLPerson: 0x1007b85f0>
方法操作相关
// 获取cls中,sel的实现
IMP class_getMethodImplementation(Class cls, SEL name);
//设置方法新的实现
IMP method_setImplementation(Method m, IMP imp);
//交换两个方法实现
void method_exchangeImplementations(Method m1, Method m2);
其中method_exchangeImplementations()
交换两个方法实现在iOS框架中应用的很广。比如:
示例1,UIButton
事件点击全埋点
@implementation UIControl (Extension)
+ (void)load {
Method oldMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method newMethod = class_getInstanceMethod(self, @selector(gl_sendAction:to:forEvent:));
method_exchangeImplementations(oldMethod, newMethod);
}
- (void)gl_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
//如果是按钮
if ([self isKindOfClass:[UIButton class]]) {
//其他处理,埋点
NSLog(@"action=%@,target=%@",NSStringFromSelector(action),target);
}
//调用系统原有的实现
[self gl_sendAction:action to:target forEvent:event];
}
UIButton
继承自UIControl
,通过addTarget-Selector
添加点击事件后,都会来到sendAction:to:forEvent:
方法。所以可以通过方法交换的方式来达到hook的方式。
示例中,还有一个点,调用系统原有实现时,调用了gl_sendAction:to:forEvent:
。字面上看造成了死循环其实不然,我们通过下面的图来解释。
方法没有交换前
@selector(sendAction:to:forEvent:)
可以找到sendAction:to:forEvent:
的实现。
@selector(gl_sendAction:to:forEvent:)
可以找到gl_sendAction:to:forEvent:
的实现。
实现交换后
所以系统内部调用sendAction:to:forEvent:
时,会来到gl_sendAction:to:forEvent:
的实现。当我们调用系统原有的实现就要调用gl_sendAction:to:forEvent:
。
示例2,NSArray防止崩溃
我们都知道数组是不可以添加nil
的,否则会发生崩溃
NSMutableArray *array = [NSMutableArray array];
NSObject *obj = nil;
[array addObject:obj];
crash信息:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
从这我们可以看出,addobjectivecect:
方法最后调用了insertObject:atIndex:
。NSMutableArray的类并不是我们看到的NSMutableArray,而是__NSArrayM 。这是因为Foundation framewor
k框架下广泛使用了类簇的设计模式。它管理了一组隐藏在公共抽象父类下的具体私有子类。
在Cocoa中,实际上许多类都是以类簇的方式实现的,即它们是一群隐藏在通用接口之下与实现相关的类。例如创建数组时可能是__NSArray0
,__NSSingleObjectArray
, __NSArrayI
,所以请不要轻易尝试创建NSString
,NSArray
,NSDictionary
的子类。对类簇使用isKindOfClass
和isMemberOfClass
的结果可能是不正确的。因为类簇是由公共抽象类管理的一组私有类,公共抽象类并不是实例对应的真正的类,类簇中真正的类的从属关系被隐藏了。
所以要对NSMutableArray
操作,选择的类是__NSArrayM
这里要对insertObject:atIndex:
添加非空判断。
@implementation NSMutableArray (Extension)
+ (void)load {
Class cls = NSClassFromString(@"__NSArrayM");
Method old = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method new = class_getInstanceMethod(cls, @selector(gl_insertObject:atIndex:));
method_exchangeImplementations(old, new);
}
- (void)gl_insertObject:(id)anObject atIndex:(NSUInteger)index {
if (!anObject) return;
[self gl_insertObject:anObject atIndex:index];
}
@end
选择器相关
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);