Objective-C Runtime 之四 常用 API


objc_getClass

通过字符串获取某一个类。

// 通过字符串获取到这个类然后生成一个实例。
Class cls = objc_getClass("Person");
Person *p = [cls new];
[p hello];

objc_getMetaClass 是通过一个字符串获取元类。 object_getClass 是通过一个对象获取其 isa

class_getName

通过一个类获取类名字符串。

NSLog(@"%s", class_getName([Person class]));

class_copyIvarList

class_copyIvarList 可以获取变量列表,之后 ivar_getName 可以获得变量名,ivar_getTypeEncoding 可以获得变量类型。

unsigned int outCount = 0;
Ivar *ivarList = class_copyIvarList([Person class], &outCount);

for (NSUInteger i = 0; i < outCount; i++) {
    Ivar ivar = ivarList[i];
    NSLog(@"%s", ivar_getName(ivar));
    NSLog(@"%s", ivar_getTypeEncoding(ivar));
}

/*
 _name
 @"NSString"
 _age
 q
 */

class_copyPropertyList

class_copyPropertyList 用于获取属性列表。property_getName 用于获取属性名,property_getAttributes 用于获取属性内容。

其中,关于属性中编码含义参见 Declared Properties

unsigned int outCount = 0;
objc_property_t *propertyList = class_copyPropertyList([Person class], &outCount);

for (NSUInteger i = 0; i < outCount; i++) {
    objc_property_t p = propertyList[i];
    NSLog(@"%s", property_getName(p));
    NSLog(@"%s", property_getAttributes(p));
}

/*
 name
 T@"NSString",C,N,V_name
 age
 Tq,N,V_age
 */

class_copyMethodList

class_copyMethodList 用于获取方法列表

unsigned int outCount = 0;
Method *methodList = class_copyMethodList([Person class], &outCount);
 
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSUInteger i = 0; i < outCount; i++) {
    Method method = methodList[i];
    SEL sel = method_getName(method);
    dict[NSStringFromSelector(sel)] = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
}
NSLog(@"%@", dict);

/*
 {
 ".cxx_destruct" = "v16@0:8";
    age = "q16@0:8";
    "helloTo:" = "v24@0:8@16";
    name = "@16@0:8";
    "setAge:" = "v24@0:8q16";
    "setName:" = "v24@0:8@16";
    walk = "v16@0:8";
 }
 */

class_copyProtocolList

class_copyProtocolList 用于获取协议列表。

__unsafe_unretained Protocol **protocolList = class_copyProtocolList([Person class], &outCount);

class_addIvar

class_addIvar 用于动态添加变量。一个编译好的类是无法添加变量的,但是我们是动态创建并注册一个类,在注册之前为其动态添加变量。

// 给类分配空间
Class Game = objc_allocateClassPair([NSObject class], "Game", 0);

// 添加变量
BOOL isAdded = class_addIvar(Game, "name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

// 注册类
objc_registerClassPair(Game);

if (isAdded) {
    id game = [[Game alloc] init];
    [game setValue:@"mario" forKey:@"name"];
    NSLog(@"%@", game);
    NSLog(@"%@", [game valueForKey:@"name"]);
}

// <Game: 0x1006aaba0>
// mario

class_addMethod

即使是以及编译好的类,也可以动态添加方法。


// 获取方法
Method method = class_getInstanceMethod([Person class], @selector(hello));
// 获取方法实现
IMP methodIMP = method_getImplementation(method);
// 获取方法类型(即返回值、参数类型)
const char *types = method_getTypeEncoding(method);

class_addMethod([Animal class], @selector(hi), methodIMP, types);

Animal *a = [Animal new];
[a hi];

本例将 Person 类的一个方法添加给了 Animal 类中。

method_exchangeImplementations

method_exchangeImplementations 用于交换两个方法的实现。


Method method1 = class_getInstanceMethod([Person class], @selector(hello));

Method method2 = class_getInstanceMethod([Person class], @selector(walk));

method_exchangeImplementations(method1, method2);

Person *p = [Person new];
[p hello];
[p walk];

// Walk!
// Hello!

本例我们交换了两个方法的实现,最终调用 hello 输出 Walk!,调用 walk 输出 Hello!