美文网首页
Runtime(3)常用方法

Runtime(3)常用方法

作者: iOS资深入门 | 来源:发表于2020-06-16 16:55 被阅读0次

一、类别中添加属性

新建一个Person类, 添加一个name属性。

@interface Person : NSObject

@property (nonatomic) NSString * name;

@end

@implementation Person

@end

建一个Person类的类别stature,添加一个height属性。

@interface Person (stature)

@property (nonatomic) NSInteger height;

@end


@implementation Person (stature)

@end

然后在调用的时候发现


image.png

setHeight:方法未找到

    //获取property列表
    unsigned int count = 0;
    objc_property_t * list  = class_copyPropertyList([p class], &count);

    for (int i = 0 ; i < count; i ++) {
        objc_property_t property = list[I];
        const char * name = property_getName(property);
        NSLog(@"property - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
    }
    free(list);

    //获取ivar列表
    unsigned int ivarCount = 0;
    Ivar * ivars = class_copyIvarList([p class], &ivarCount);

    for (int i = 0; i < ivarCount; i ++) {
        Ivar ivar = ivars[I];
        const char * name = ivar_getName(ivar);
        NSLog(@"ivar - %@",[NSString stringWithCString:name encoding:NSUTF8StringEncoding]);
    }
    free(ivars);
    
    //获取方法列表
    unsigned int methodCount = 0;
    Method * methods = class_copyMethodList([p class], &methodCount);
    for (int i = 0; i < methodCount; i ++) {
        Method method = methods[I];
        SEL sel = method_getName(method);
        NSLog(@"method - %@", NSStringFromSelector(sel));
    }
    free(methods);

输出

property - height
property - name
property - age
ivar - _name
ivar - _age
method - .cxx_destruct
method - name
method - setName:
method - age
method - setAge:

发现只是添加了property,并没有自动生成成员变量和set、get方法。
手动添加setter/getter方法,使用runtime关联属性后即可正常使用。

- (void)setHeight:(NSInteger)height {
    const char * key = "height";
    objc_setAssociatedObject(self, key, @(height), OBJC_ASSOCIATION_ASSIGN);
}

- (NSInteger)height {
    const char * key = "height";
    return [objc_getAssociatedObject(self, key) integerValue];
}

二、动态添加类

    const char * className = "MyClass";
    Class MyClass = objc_allocateClassPair([NSObject class], className, 0);
    //在objc_allocateClassPair  和 objc_registerClassPair之间添加变量,不然会添加失败。
    objc_registerClassPair(MyClass);
官方文档描述

三、添加实例变量

    const char * key = "name";
    BOOL isSuccess = class_addIvar(MyClass, key, sizeof(NSString *), 0, "@");
    NSLog(@"name添加%@", isSuccess ? @"成功":@"失败");
    objc_registerClassPair(MyClass);

    const char * ageKey = "age";
    isSuccess = class_addIvar(MyClass, ageKey, sizeof(unsigned int), 0, "I");
    NSLog(@"age添加%@", isSuccess ? @"成功":@"失败");

输出

name添加成功
age添加失败

原因

1.因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,同时runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。
2.运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
https://www.jianshu.com/p/faf14147c25d

    //变量存取
    id myclass = [[MyClass alloc] init];
    Ivar nameIvar = class_getInstanceVariable(MyClass, key);
    object_setIvar(myclass, nameIvar, @"小强");

    NSLog(@"%@", object_getIvar(myclass, nameIvar));

四、动态添加属性

Property Type String
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6

const char * propertyDicKey = "_dictForCustProperty";

{
    //存储添加的属性与值
    class_addIvar(MyClass, propertyDicKey, sizeof([NSMutableDictionary class]), 0, "@");

    id myClass = [[MyClass alloc] init];
    //创建实例之后初始化一下
    NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
    Ivar propertyIvar = class_getInstanceVariable(MyClass, propertyDicKey);
    object_setIvar(myClass, propertyIvar, dic);

}

- (void)addPropertyToClass:(Class)class propertyName:(NSString *)propertyName typeClass:(Class)typeClass {
    objc_property_attribute_t type = {"T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass(typeClass)] UTF8String]};
    //C copy 参考Property Type String
    objc_property_attribute_t ownership = {"C", ""};
    objc_property_attribute_t backingivar = {"V", "_name"};
    objc_property_attribute_t attrs[] = {type, ownership, backingivar};
    
    //添加属性
    class_addProperty(class, [propertyName UTF8String], attrs, 3);
    
    //添加 setter / getter
    SEL setterSel = NSSelectorFromString([NSString stringWithFormat:@"set%@:",[propertyName capitalizedString]]);
    class_addMethod(class, setterSel, (IMP)setValue, "@@:");
    class_addMethod(class, NSSelectorFromString(propertyName), (IMP)getter, "v@:");
    
}

void setValue (id self, SEL _cmd, id value) {
    //sel转为key  setName: --> name
    NSString * key = [NSStringFromSelector(_cmd) stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""];
    NSString * head = [[key substringWithRange:NSMakeRange(0, 1)] lowercaseString];
    key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:head];
    key = [key substringToIndex:key.length - 1];
    
    
    Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
    NSMutableDictionary * dic = object_getIvar(self, ivar);
    [dic setObject:value forKey:key];
    object_setIvar(self, ivar, dic);
}

id getter (id self, SEL _cmd) {
    
    Ivar ivar = class_getInstanceVariable([self class], propertyDicKey);
    NSMutableDictionary * dic = object_getIvar(self, ivar);

    return [dic objectForKey:NSStringFromSelector(_cmd)];
}

五、动态添加方法

{
    //添加实例方法
    class_addMethod(MyClass, @selector(methodTest), (IMP)methodTestIMP, "v@:");
    objc_msgSend(myclass, @selector(methodTest));

    //添加类方法  
    class_addMethod(object_getClass(MyClass), @selector(classMethodTest), (IMP)classMethodTestIMP, "v@:");
    objc_msgSend(MyClass, @selector(classMethodTest));
}

void methodTestIMP (id self, SEL _cmd) {
    NSLog(@"这是一个实例方法");
};

void classMethodTestIMP (id self, SEL _cmd) {
    NSLog(@"这是一个类方法");
};

type encodings
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100

六、方法交换

+ (void)load {
    Method old = class_getInstanceMethod([self class], @selector(viewWillAppear:));
    Method new = class_getInstanceMethod([self class], @selector(track_viewWillAppear:));
    method_exchangeImplementations(old, new);
}

- (void)track_viewWillAppear:(BOOL)animated {
    NSLog(@"%@---%@",self , NSStringFromSelector(_cmd));
    [self track_viewWillAppear:animated];
}

相关文章

网友评论

      本文标题:Runtime(3)常用方法

      本文链接:https://www.haomeiwen.com/subject/orurtktx.html