文章目录
一、Runtime API
1、类
1、动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
2、注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
3、销毁一个类
void objc_disposeClassPair(Class cls)
4、设置和获取isa指向的Class
Class object_setClass(id obj, Class cls)
Class object_getClass(id obj)
5、判断一个OC对象是否为类和元类
BOOL object_isClass(id obj)
BOOL class_isMetaClass(Class cls)
6、获取父类
Class class_getSuperclass(Class cls)
2、成员变量
1、获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
2、拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
3、动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
4、获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
5、设置成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
6、获取成员变量的对象
id object_getIvar(id obj, Ivar ivar)
3、属性
1、获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
2、拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
3、动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
4、动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
5、获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
4、方法
1、获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
2、方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2)
3、拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
4、动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
5、动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
6、获取方法的相关信息(带有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)
6、选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
7、用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
二、示例
1、动态创建类、添加成员变量、添加方法
#import <Foundation/Foundation.h>
void run(id self, SEL _cmd) {
NSLog(@"_____ %@ - %@", self, NSStringFromSelector(_cmd));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1、创建类【类型可动态赋值】
NSString *className = [NSString stringWithFormat:@"ZMDog"];
Class newClass = objc_allocateClassPair([NSObject class], className, 0);
// 添加成员变量
class_addIvar(newClass, "_age", 4, 1, @encode(int));
class_addIvar(newClass, "_weight", 8, 1, @encode(int));
// 添加方法
class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
// 2、注册类
objc_registerClassPair(newClass);
{
// 初始化
id dog = [[newClass alloc] init];
// 赋值
[dog setValue:@10 forKey:@"_age"];
[dog setValue:@20 forKey:@"_weight"];
// 方法实现
[dog run];
// 打印结果
NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
}
// 3、释放类
objc_disposeClassPair(newClass);
}
return 0;
}
注意:
1、成员变量一定要在注册类之前实现
2、方法objc_disposeClassPair
,如果这个类或子类的实例存在,不要调用。
2、设置isa指向的类
ZMPerson.m:
#import "ZMPerson.h"
@implementation ZMPerson
- (void)run {
NSLog(@"%s", __func__);
}
@end
ZMCar.m:
#import "ZMCar.h"
@implementation ZMCar
- (void)run {
NSLog(@"%s", __func__);
}
@end
实现:
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZMPerson *person = [[ZMPerson alloc] init];
[person run];
object_setClass(person, [ZMCar class]);
[person run];
}
return 0;
}
打印结果:
[29582:563758] -[ZMPerson run]
[29582:563758] -[ZMCar run]
3、判断类和元类
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZMPerson *person = [[ZMPerson alloc] init];
NSLog(@"%d %d %d",
object_isClass(person),
object_isClass([ZMPerson class]),
object_isClass(object_getClass([ZMPerson class]))
);
}
return 0;
}
打印结果:
[28681:543229] 0 1 1
4、成员变量【获取成员变量信息、成员变量赋值和取值、拷贝成员变量列表】
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1、获取成员变量信息
Ivar ageIvar = class_getInstanceVariable([ZMPerson class], "_age");
NSLog(@"ageIvar:%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
Ivar nameIvar = class_getInstanceVariable([ZMPerson class], "_name");
ZMPerson *person = [[ZMPerson alloc] init];
// 2、设置成员变量的值
object_setIvar(person, nameIvar, @"123");
object_setIvar(person, ageIvar, (__bridge id)(void *)10);
// 3、获取成员变量的值
NSLog(@"person.name:%@", person.name);
NSLog(@"person.age:%d",person.age);
NSLog(@"==================");
// 成员变量的数量
unsigned int count;
// 拷贝成员变量数组
Ivar *ivars = class_copyIvarList([ZMPerson class], &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
// 释放成员变量
free(ivars);
}
return 0;
}
self.textF.placeholder = @"请填写";
// 直接修改私有成员变量,在iOS13以后被禁止
// [self.textF setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
// =================
// 要改成如下方式实现:
// 获取成员变量
Ivar placeholderIvar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
UILabel *placeholderLabel = object_getIvar(self.textF, placeholderIvar);
//给变量赋值
placeholderLabel.textColor = [UIColor redColor];
5、数模转换
NSObject+Json.h:
#import <Foundation/Foundation.h>
@interface NSObject (Json)
+ (instancetype)zm_objectWithJson:(NSDictionary *)json;
@end
NSObject+Json.m:
#import "NSObject+Json.h"
#import <objc/runtime.h>
@implementation NSObject (Json)
+ (instancetype)zm_objectWithJson:(NSDictionary *)json
{
id obj = [[self alloc] init];
unsigned int count;
// 拷贝示例变量列表
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[i];
// 获取成员变量名称
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
// 移除成员变量首个下划线【_】
[name deleteCharactersInRange:NSMakeRange(0, 1)];
// 设值
id value = json[name];
if ([name isEqualToString:@"ID"]) {
value = json[@"id"];
}
// 通过KVC赋值
[obj setValue:value forKey:name];
}
// 释放变量
free(ivars);
return obj;
}
@end
ZMPerson.h:
#import <Foundation/Foundation.h>
@interface ZMPerson : NSObject
@property (assign, nonatomic) int ID;
@property (assign, nonatomic) int weight;
@property (assign, nonatomic) int age;
@property (copy, nonatomic) NSString *name;
- (void)run;
@end
ZMPerson.m:
#import "ZMPerson.h"
@implementation ZMPerson
- (void)run {
NSLog(@"%s", __func__);
}
@end
【main.m】实现:
#import <Foundation/Foundation.h>
#import "ZMPerson.h"
#import "NSObject+Json.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 字典转模型
NSDictionary *json = @{
@"id" : @10,
@"age" : @20,
@"weight" : @80,
@"name" : @"Tony"
};
ZMPerson *person = [ZMPerson zm_objectWithJson:json];
}
return 0;
}
6、方法交换
ZMPerson.h:
#import <Foundation/Foundation.h>
@interface ZMPerson : NSObject
- (void)run;
- (void)test;
@end
#import "ZMPerson.h"
@implementation ZMPerson
- (void)run {
NSLog(@"%s", __func__);
}
- (void)test {
NSLog(@"%s", __func__);
}
@end
main.m 实现:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "ZMPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZMPerson *person = [[ZMPerson alloc] init];
Method runMethod = class_getInstanceMethod([ZMPerson class], @selector(run));
Method testMethod = class_getInstanceMethod([ZMPerson class], @selector(test));
method_exchangeImplementations(runMethod, testMethod);
[person run];
}
return 0;
}
打印结果:
// 代码执行的是[person run];,结果实际执行到了test中
[31079:590617] -[MJPerson test]
7、方法替代
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
void myrun() {
NSLog(@"---myrun");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
NSLog(@"123123");
}), "v");
[person run];
}
return 0;
}