Objective-C的运行时常用方法

前言

runtime是OC成为面向对象的基础,了解runtime的基础用法对理解OC非常必要。大量的开源库,如MJExtension,就是使用运行时动态获取类的属性并实现动态赋值。熟悉runtime的方法也是看开源库的基础之一。

一、OC方法篇

OC对象的方法调用是利用selector寻找对应的imp指针进行调用。
现在我们在代码中理解原理。
定义三个函数,该方法名为imp指针

void testFunc1(id self, SEL _cmd) {

NSLog(@"实现testFunc1");

}
void testFunc2(id self, SEL _cmd) {
    
    NSLog(@"实现testFunc2");
    
}
void testFunc3(id self, SEL _cmd) {
    
    NSLog(@"实现testFunc3");
    
}

添加实例方法

//  添加imp到sel,该方法返回一个bool值,成功为YES
    BOOL isAddInstanceMethodFormImpSuccess = class_addMethod(class, sel_registerName("instanceMethod"), (IMP)testFunc1, "v@:");
    if (isAddInstanceMethodFormImpSuccess) {
        NSLog(@"添加imp方法成功");
    }

添加类方法

    BOOL isAddClassMethodFormImpSuccess = class_addMethod(object_getClass(class), sel_registerName("classMethod"), (IMP)testFunc3, "v@:");
    if (isAddClassMethodFormImpSuccess) {
        NSLog(@"添加imp方法成功");
    }

取得blcok的imp指针

  IMP impOfBlock = imp_implementationWithBlock(^(id obj, NSString *string,...){
      NSLog(@"调用者:%@\nstring:%@", obj, string);
  });
  ```
  添加impOfBlock到sel
  BOOL isAddMethodFormImpOfBlockSuccess = class_addMethod(class, sel_registerName("instanceMethodImpOfBlock:"), impOfBlock, "v@:@");
  if (isAddMethodFormImpOfBlockSuccess) {
      NSLog(@"添加impOfBlock方法成功");
  }
  ```

添加方法后,由于这是运行时添加的,我们不能像平时那样调用,要用使用performSelector系列方法进行调用

[object performSelector:@selector(instanceMethod)];
[object performSelector:@selector(instanceMethodImpOfBlock:) withObject:@"我要调用Block"];
[(id)class performSelector:@selector(classMethod)];

使用UIView作为测试类,该viewController的view作为测试对象,该结果为

2016-08-29 00:20:52.404 RuntimeDemo[3515:1637694] 实现testFunc1
2016-08-29 00:20:52.404 RuntimeDemo[3515:1637694] 调用者:>
string:我要调用Block
2016-08-29 00:20:52.405 RuntimeDemo[3515:1637694] 实现testFunc3


##二,成员变量与属性篇
由于在OC在只有在类的创建期间才能添加ivar,但是我们启动app时,类都创建好了,无法添加,所以我在在运行期间自己创建一个类

根据名字取得类
Class testClass = objc_getClass("MLView");
if (!testClass) {
//动态创建类
testClass = objc_allocateClassPair([UIView class], "MLView", 0);
}

添加ivar

BOOL isAddIvarSuccess = class_addIvar(class, "_runtimeString", sizeof(NSString *), log(sizeof(NSString *)), "@"NSString"");
if (isAddIvarSuccess) {
NSLog(@"添加ivar成功");
}

添加property
objc_property_attribute_t type = { "T", "@\"NSString\"" };
 objc_property_attribute_t backingivar  = { "V", "_runtimeString" };
objc_property_attribute_t attrs[] = {type, backingivar};
BOOL isAddPropertySuccess = class_addProperty(class, "runtimeString", attrs, sizeof(attrs)/sizeof(objc_property_attribute_t));
if (isAddPropertySuccess) {
    NSLog(@"添加property成功");
}
 注册类名,并将viewController的view的class设置为运态创建的类(实践上不建议这样做,不易检查错误,如要添加属性可以变相用关联添加,下文用介绍如何在分类添加属性)

objc_registerClassPair(testClass);
//使用view作为 测试对象
id testObject =self.view;
//设置testObject的类,就可以使用动态创建的ivar
object_setClass(testObject, testClass);

添加完变量,成功了一半,现在开始测试是否添加成功就要进行赋值操作

  获取ivar并赋值
Ivar ivar = class_getInstanceVariable(class, "_runtimeString");
object_setIvar(object, ivar, @"addIvar测试字符串");
NSLog(@"addIvar的值:%@", object_getIvar(object, ivar));
  获取property并赋值
objc_property_t property = class_getProperty(class, "runtimeString");
const char *propertyAttr = property_getAttributes(property);
NSLog(@"addProperty的细节:%s", propertyAttr);
[object setValue:@"addProperty测试字符串" forKey:@"_runtimeString"];
NSLog(@"addProperty的值:%@", [object valueForKey:@"_runtimeString"]);
运行后得到的结果为

2016-08-29 00:34:15.870 RuntimeDemo[8155:1663698] addIvar的值:addIvar测试字符串
2016-08-29 00:34:15.870 RuntimeDemo[8155:1663698] addProperty的细节:T@"NSString",V_runtimeString
2016-08-29 00:34:18.211 RuntimeDemo[8155:1663698] addProperty的值:addProperty测试字符串



##三、运行时简单封装
####1、在分类中添加属性
现在我要在给NSObject分类里添加一个对象

@property (nonatomic, strong) NSString *featureIdentifier;

然后重写setter和getter方法,并用关联机制
  • (void)setFeatureIdentifier:(NSString *)featureIdentifier
    {
    objc_setAssociatedObject(self, @selector(featureIdentifier), featureIdentifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
  • (NSString *)featureIdentifier
    {
    return objc_getAssociatedObject(self, @selector(featureIdentifier));
    }
####2、获取该类的ivar列表
  • (NSArray *)arrayOfIvars
    {
    unsigned int count = 0;
    Ivar *ivar = class_copyIvarList(self, &count);
    NSMutableArray *ivarNameArray = [[NSMutableArray alloc] init];
    for (int i = 0; i Ivar iva = ivar[i];
    const char *name = ivar_getName(iva);
    NSString *strName = [NSString stringWithUTF8String:name];
    [ivarNameArray addObject:strName];

    }

    free(ivar);
    return ivarNameArray;

}

####3、取得该类属性列表
  • (NSArray *)arrayOfProperties
    {
    unsigned int count = 0;
    objc_property_t *properties = class_copyPropertyList(self, &count);

    NSMutableArray *propertyNameArray = [[NSMutableArray alloc] init];
    for (int i = 0; i objc_property_t property = properties[i];
    const char *name = property_getName(property);
    NSString *strName = [NSString stringWithUTF8String:name];
    [propertyNameArray addObject:strName];

    }

    free(properties);
    return propertyNameArray;

}


####4、取得该类实例方法列表
  • (NSArray *)arrayOfInstanceMethods
    {
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(self, &count);
    NSMutableArray *methods = [[NSMutableArray alloc] init];

    for (NSInteger i = 0; i < count; i++) {
    SEL selector = method_getName(methodList[i]);
    NSString *selString = NSStringFromSelector(selector);
    [methods addObject:selString];
    }

    free(methodList);
    return methods;
    }

####5、取得该类方法方法列表
unsigned int count = 0;
Method *methodList = class_copyMethodList(object_getClass(self), &count);
NSMutableArray *methods = [[NSMutableArray alloc] init];

for (NSInteger i = 0; i < count; i++) {
    SEL selector = method_getName(methodList[i]);
    NSString *methodString = NSStringFromSelector(selector);
    [methods addObject:methodString];
}

free(methodList);
return methods;
####6、取得该类遵循协议列表
  • (NSArray *)arrayOfProtocols{

    unsigned int count = 0;
    Protocol * __unsafe_unretained *protocols = class_copyProtocolList(self, &count);
    NSMutableArray *protocolArray = [[NSMutableArray alloc] init];
    for (int i = 0; i < count; i++) {
    [protocolArray addObject:[NSString stringWithUTF8String:protocol_getName(protocols[i])]];

    }

free(protocols);
return protocolArray;

}

####7、取得该工程所有类的列表
  • (NSArray *)arrayOfAllClass {
    NSMutableArray * classList = [NSMutableArray array];

    unsigned int classesCount = 0;
    Class * classes = objc_copyClassList(&classesCount);

for ( int i = 0; i < classesCount; ++i) {
    NSString *className = NSStringFromClass(classes[i]);

    [classList addObject:className];
}

free(classes);
return classList;

}

####8、取得该对象中有值的的property字典
  • (NSDictionary *)dictionaryOfPropertyKeyValues
    {
    NSArray *properties = [[self class] arrayOfProperties];
    NSMutableDictionary *keyValueDictionary = [[NSMutableDictionary alloc] init];
    for (NSInteger i = 0; i < properties.count; i++) {
    if ([self valueForKey:properties[i]]) {
    [keyValueDictionary setObject:[self valueForKey:properties[i]] forKey:properties[i]];
    }
    }
    return keyValueDictionary;
    }

[此处应该有demo][https://github.com/luomagaoshou/MLRuntimeDemo]

你可能感兴趣的:(Objective-C的运行时常用方法)