Runtime梳理(三)动态的创建类,添加成员变量,方法

2018-11-15  本文已影响0人  飞奔的小鲨鱼

在上一篇KVO的实现中用到了动态的创建类,添加成员变量等方法,这一篇说说这些方法的使用。

父类Person

@interface Person : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) NSInteger age;
- (void)work;
@end
@implementation Person
- (void)setName:(NSString *)name{
    _name = name;
    NSLog(@"Person ---> setName %@",name);
}
- (void)setAge:(NSInteger)age{
    _age = age;
    NSLog(@"Person ---> setAge %ld",age);
}
- (void)work{
    NSLog(@"Person ---> work");
}
@end

1.动态的创建一个类,用到了下面这个方法:

// ex:创建一个Student类,继承自Person
const char * className = [@"Student" cStringUsingEncoding:NSUTF8StringEncoding];
// param1:新创建类的父类
// param2:新创建类的类名
// param3:默认为0
Class student = objc_allocateClassPair([Person class], className, 0);
// add iVar add method ...
objc_registerClassPair(student);
  • @note To create a new class, start by calling \c objc_allocateClassPair.
  • Then set the class's attributes with functions like \c class_addMethod and \c class_addIvar.
  • When you are done building the class, call \c objc_registerClassPair. The new class is now ready for use.

为这个新创建的类添加方法和成员变量,并且注册这个类。

2.添加成员变量

    // add iVar
    class_addIvar(student, "_name",sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
    class_addIvar(student, "_age", sizeof(NSInteger), log2(sizeof(NSInteger)), @encode(NSInteger));

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

返回YES,添加成功,返回NO,添加失败,说明该类中已经存在这个名称。它的调用是在objc_allocateClassPair之后,objc_registerClassPair之前,给一个存在的类添加实例变量是不允许的。

我们尝试着给Person添加address

添加成员变量.png

3.添加方法
通过class_addMethod(Class cls, SEL name, IMP imp, const char *types)方法添加,参数依次为:添加方法的类,方法名,方法实现,方法参数的描述。

class_addMethod will add an override of a superclass's implementation, but will not replace an existing implementation in this class. To change an existing implementation, use method_setImplementation.

通过这个方法会重写父类的实现,需要改变本类方法实现,通过方法method_setImplementation.

static void stu_setName(id self, SEL _cmd, id value){
    NSLog(@"student ---> stu_setName %@",value);
}
const char * types = method_getTypeEncoding(class_getInstanceMethod(
[Person class], @selector(setName:)));
BOOL result1 = class_addMethod(student, @selector(setName:), (IMP)(stu_setName), types);
BOOL result2 = class_addMethod(student, @selector(setName:), (IMP)(stu_setName), types);
NSLog(@"result1: %d, result2: %d",result1,result2);
id stu = [[student alloc] init];
[stu setValue:@"小鲨鱼" forKey:@"name"];

打印结果:

result1: 1, result2: 0
student ---> stu_setName 小鲨鱼

4.重写本类中的方法
通过方法method_setImplementation(Method m, IMP imp) 返回类型IMP,方法m之前的实现.

static void study(id self, SEL _cmd){
    NSLog(@"student ---> study.");
}
static void play (id self, SEL _cmd){
    NSLog(@"student ---> play.");
}
class_addMethod(student, @selector(study), (IMP)(study), nil);
id stu = [[student alloc] init];
[stu performSelector:@selector(study)];
Method studyMethod = class_getInstanceMethod(student, @selector(study));
method_setImplementation(studyMethod, (IMP)(play));
[stu performSelector:@selector(study)];

打印结果:

student ---> study.
student ---> play.

在调用study的方法的时候打印的是play的结果,调换了方法的实现。method_exchangeImplementations(Method m1, Method m2),可能也是通过这种方法实现m1,m2的交换。

5.添加属性

objc_property_attribute_t type = { "T", "@\"NSString\"" };
objc_property_attribute_t attrs[] = {type};
BOOL result4 = class_addProperty([student class], "number", attrs, 1);
[stu setValue:@"2018" forKey:@"number"];
NSLog(@"------ %@ , %d",[stu valueForKey:@"number"],result4);

打印结果

------ 2018 , 1
上一篇 下一篇

猜你喜欢

热点阅读