常用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 对象是否为类对象
BOOL object_isClass(id obj)
判断一个 Class
是否为元类对象
BOOL class_isMetaClass(Class cls)
获取父类
Class class_getSuperclass(Class cls)
成员变量
获取一个实例变量信息
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)
属性
获取一个属性
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)
方法
获得一个实例方法、类方法
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)
动态创建类
void run(id self, SEL _cmd)
{
NSLog(@"%@ - %@", self, NSStringFromSelector(_cmd));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建类
Class newCls = objc_allocateClassPair([Person class], "Student", 0);
// 添加成员变量
class_addIvar(newCls, "_no", 4, 1, @encode(int));
// 添加方法
class_addMethod(newCls, @selector(run), (IMP)run, "v@:");
// 注册类
objc_registerClassPair(newCls);
id stu = [[newCls alloc] init];
[stu setValue:@(10) forKey:@"_no"];
[stu run];
NSLog(@"%@", [stu valueForKey:@"_no"]);
// 不需要这个类时,释放
objc_disposeClassPair(newCls);
}
return 0;
}
获取和设置实例变量
@interface Person : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Ivar ageIvar = class_getInstanceVariable([Person class], "_age");
NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
// _age i
Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
// _name @"NSString"
Person *person = [[Person alloc] init];
object_setIvar(person, ageIvar, (__bridge id)(void *)10);
NSLog(@"%d", person.age);
// 10
object_setIvar(person, nameIvar, @"jack");
NSLog(@"%@, %@", person.name, object_getIvar(person, nameIvar));
// jack, jack
}
return 0;
}
遍历指定类的实例变量
@interface Person : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
unsigned int count;
Ivar *ivars = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
// _age i
// _name @"NSString"
}
free(ivars);
}
return 0;
}
简单字典转模型
runtime
结合 KVC
实现简单的字典转模型。只是简单演示,不考虑特殊字段和继承体系:
@interface Person : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@property (nonatomic, copy) NSString *name;
@end
@interface NSObject (Json)
+ (instancetype)jf_objectWithJson:(NSDictionary *)json;
@end
@implementation NSObject (Json)
+ (instancetype)jf_objectWithJson:(NSDictionary *)json
{
id obj = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
if ([name hasPrefix:@"_"]) {
[name deleteCharactersInRange:NSMakeRange(0, 1)]; // 删除成员变量的_前缀
}
// kvc给成员变量设置值
[obj setValue:json[name] forKey:name];
}
free(ivars);
return obj;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *json = @{
@"age": @10,
@"height": @1.8,
@"name": @"jack"
};
Person *person = [Person jf_objectWithJson:json];
NSLog(@"%d, %.2f, %@", person.age, person.height, person.name);
// 10, 1.80, jack
}
return 0;
}
替换方法
@interface Person : NSObject
- (void)test;
@end
@implementation Person
- (void)test
{
NSLog(@"test");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
class_replaceMethod([Person class], @selector(test), imp_implementationWithBlock(^{
NSLog(@"block");
}), "v@:");
[[[Person alloc] init] test]; // block
}
return 0;
}
交换方法实现
@interface Person : NSObject
- (void)test1;
- (void)test2;
@end
@implementation Person
- (void)test1
{
NSLog(@"%s", __func__);
}
- (void)test2
{
NSLog(@"%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Method test1Method = class_getInstanceMethod([Person class], @selector(test1));
Method test2Method = class_getInstanceMethod([Person class], @selector(test2));
// 相当于替换两个Method里的IMP
method_exchangeImplementations(test1Method, test2Method);
[[[Person alloc] init] test1]; // -[Person test2]
}
return 0;
}
给分类添加"实例变量"
@interface Person (Test)
@property (nonatomic, assign) int age;
@end
@implementation Person (Test)
- (void)setAge:(int)age
{
objc_setAssociatedObject(self, @"age", @(age), OBJC_ASSOCIATION_ASSIGN);
}
- (int)age
{
return [objc_getAssociatedObject(self, @"age") intValue];
}
@end
拦截系统方法
下面例子只是拦截了所有按钮点击事件,这样可以监控到所有按钮的点击事件,也可以处理某个点击事件不响应:
@implementation UIControl (Extension)
+ (void)load
{
// 防止手动调用+load方法,或者其他原因导致load被再次调用
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method methodOld = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method methodNew = class_getInstanceMethod(self, @selector(jf_sendAction:to:forEvent:));
method_exchangeImplementations(methodOld, methodNew);
});
}
- (void)jf_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
NSLog(@"%@, %@, %@", self, target, NSStringFromSelector(action));
// 调用原先的方法
[self jf_sendAction:action to:target forEvent:event];
}
@end
想实现诸如防止字典添加 nil
元素、数组越界等等错误,都可以使用这种方式来实现拦截。