1、关于类的一些API
//动态创建一个类(参数:父类,类名,额外的内存空间)
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)
object_getClass( ) : 获取isa指向的Class
MJPerson *person = [[MJPerson alloc] init];
[person run];
NSLog(@"%p %p",object_getClass(person),[MJPerson class]);//打印地址结果一样
NSLog(@"%p %p",object_getClass([MJPerson class]),[MJPerson class]);//打印地址结果不一样
object_getClass(person); //object_getClass(person)获取的是perosn对象的类,也就是[MJPerson class]
object_getClass([MJPerson class]); //object_getClass([MJPerson class])获取的是MJPerson的元类,元类是也是特殊的类,但不同于[MJPerson class]
object_setClass :设置对象isa指针指向的class
MJPerson *person = [[MJPerson alloc] init];
[person run]; //打印结果是MJPerson run
object_setClass(person, [MJCat class]); //设置person对象的isa指向MJCat
[person run]; //打印结果是MJCat run
object_isClass :用来判断入参是否是类
MJPerson *person = [[MJPerson alloc] init];
NSLog(@"%d ",object_isClass(person)); //0 : person是对象不是类
NSLog(@"%d ",object_isClass([MJPerson class])); //1 : [MJPerson Class]是类
NSLog(@"%d ",object_isClass(object_getClass(person))); //1 : object_getClass(person)获取person的类,就是MJPerson
NSLog(@"%d ",object_isClass(object_getClass([MJPerson class])));//1 : [MJPerson class]的类是元类,元类也是一种特殊的类
objc_allocateClassPair : 动态创建一个类
void run (id self,SEL _cmd){
NSLog(@"%@ %@",self,NSStringFromSelector(_cmd));
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString *className = [NSString stringWithFormat:@"MJDog"];
//创建类
Class newClass = objc_allocateClassPair([NSObject class], className.UTF8String, 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 dog = [[newClass alloc] init];
[dog run]; //打印结果: run
}
2、关于成员变量的一些API
//获取一个实例变量信息
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)
class_getInstanceVariable : 获取一个实例变量的信息
Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
NSLog(@"%s %s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar));//打印结果: _age i
object_setIvar : 设置成员变量的值
MJPerson *person = [[MJPerson alloc] init];
Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");
object_setIvar(person, nameIvar, @"Jack");
object_setIvar(person, ageIvar, (__bridge id)(void *)10);//object_setIvar方法的第二个参数是id类型,得将int类型的10转成id类型
NSLog(@"person name is %@",person.name); //person name is Jack
NSLog(@"person age is %d",person.age); //person age is 10
class_copyIvarList : 拷贝实例变量列表
访问成员变量列表的这种方法是非常有用的:
比如在iOS项目中,看UITextField控件中的成员变量,从而窥探到_placeholderLabel @"UITextFieldLabel"这样的一个成员变量,取出该成员变量可以改变占位符的字体颜色、大小
unsigned int count;
Ivar *ivars = class_copyIvarList([MJPerson class], &count);
for (int i=0; i
3、关于属性的一些API
// 获取一个属性
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、关于方法的一些API
// 获得一个实例方法、类方法
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)
class_replaceMethod : 动态替换方法
void myrun(){
NSLog(@"my ___ run");
}
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MJPerson *person = [[MJPerson alloc] init];
//1、替换方法:在外部实现一个方法myrun用于替换原有方法run
class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
//2、替换方法:直接使用block来替换掉原有方法run
class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"this is a block");
}), "v");
[person run];
method_exchangeImplementations : 交换方法
//交换方法:实例方法、类方法都可以交换
Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
method_exchangeImplementations(testMethod, runMethod);
[person run];
一般在实际操作中,我们很少交换自己写的方法,它的价值主要体现在交换第三方库或者系统方法。
比如说要实现拦截所有的按钮点击事件,我们可以这么做:
添加UIControl的分类(因为UIButton继承自UIControl):
#import "UIControl+Ext.h"
#import
@implementation UIControl (Ext)
+(void)load{
//交换方法其实就是交换掉Method里面的方法实现IMP
Method beforeMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method afterMethod = class_getInstanceMethod(self, @selector(exchange_sendAction:to:forEvent:));
method_exchangeImplementations(beforeMethod, afterMethod);
}
//UIButton中监听点击事件主要是sendAction:to:forEvent:实现
//所以使用下面的方法exchange_XXX 去替换 sendAction:to:forEvent:
-(void)exchange_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
//调用系统原来的实现要调用exchange_XXX,因为已经交换过方法了
[self exchange_sendAction:action to:target forEvent:event];
if ([self isKindOfClass:[UIButton class]]) {
NSLog(@"这里拦截了所有的按钮点击事件");
}
}
@end
之前我们了解过一个方法Method中包含name、types、imp。
其实method_exchangeImplementations底层就是替换了两个方法的方法实现imp,其余元素不变。
void method_exchangeImplementations(Method m1, Method m2)
{
if (!m1 || !m2) return;
rwlock_writer_t lock(runtimeLock);
//可以看出,主要就是替换了两个方法的方法实现imp
IMP m1_imp = m1->imp;
m1->imp = m2->imp;
m2->imp = m1_imp;
//清除方法缓存
flushCaches(nil);
updateCustomRR_AWZ(nil, m1);
updateCustomRR_AWZ(nil, m2);
}