iOS Runtime修炼<四>

裸露在我的面前

通过这段时间的接触,我与Runtime都觉得与彼此很合得来,于是它打算将所有的招式都展现给我。本来我就是武学奇才,再加上天资聪慧,不一会儿,我就领悟了许多。当然,我也会共享我的一些好东西给它,毕竟礼尚往来嘛。


BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

这一招式之前简单的接触过,作用就是为某个类增加一个新的方法。如果类中已经存在叫这个名字的方法,函数就会返回NO。需要注意的地方是如果添加的方法覆盖了父类中的实现(这个是可以的),那么这个方法不会在当前类中生效,也就是说方法仍然是父类中的实现。要想改变这种局面,可以使用method_exchangeImplementations函数交换两个方法的实现。如:

void method_exchangeImplementations(Method m1, Method m2) {
    // 大概的实现
    IMP imp1 = method_getImplementation(m1);
    IMP imp2 = method_getImplementation(m2);
    method_setImplementation(m1, imp2);
    method_setImplementation(m2, imp1); 
}

在此顺便说一下types参数的心法:如之前传入的“v@:”三个字符,第一个代表返回值,没有返回值,则是v;第二个跟第三个字符是固定的,因为每个方法都有固定的2个参数(self和_cmd);@代表对象,: 代表方法选择器。更多的字符含义请参考 (Objective-C Runtime Programming Guide > Type Encodings)。

Method class_getInstanceMethod(Class aClass, SEL aSelector)

这一招式是从参数aClass中得到选择器为aSelector的实例方法;若指定的类与其父类中都不包含指定的选择器,则返回NULL。

通过class_getInstanceMethod我们就能顺利的使用method_exchangeImplementations函数了。只有在较少情况下使用method_exchangeImplementations函数,如下是给黑盒方法增加日志记录的例子:
NSString有一个lowercaseString方法,我们可以为其添加日记,记录转换的过程。

@interface NSString (LJAddLog) 
- (NSString *)lj_logLowercaseString;
@end

@implementation NSString (LJAddLog)
- (NSString *) lj_logLowercaseString {
    /*不会陷入无限循环,因为在运行期,此方法是指向lowercaseString方法的,相当于是在调用lowercaseString方法*/
    NSString *low = [self lj_logLowercaseString];
    /*打印加入的日志记录*/
    NSLog(@"%@ ---> %@", self, low);
    /*返回转换后的小写字母字符串*/
    return low;
}

Method originMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method addLogMethod = class_getInstanceMethod([NSString class], @selector(lj_logLowercaseString));
method_exchangeImplementations(originMethod, addLogMethod);

NSString *s = @"XIE MO TUI SAN";
/*在运行期调用的是lj_logLowercaseString方法*/
NSString *lowString = [s lowercaseString];
// XIE MO TUI SAN ---> xie mo tui san

相类似的招式还有class_getClassMethod,这里就不再说了。

Method * class_copyMethodList(Class cls, unsigned int *outCount)

这一招式是用来得到参数cls中的方法列表,输出有两个,outCount和Method *(指针数组)。返回的指针数组内存需要调用者管理。
⚠️此方法不会获取父类中的方法,这是与class_getInstanceMethod最大的区别。

IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

这一招式有点意思,它的作用是替换某个类中的某个方法,参数就不用再说了,太简单。然而此招式有过人之处,那就是如果类中没有被替换的方法,则会自动调用class_addMethod将方法增加到类中;否则就使用method_setImplementation将方法替换。返回值是被替换前的方法的实现地址。

没想到这厮有这么多招式,还好彼此之间有那么些许相似,不然我就要跟它混记一辈子了。我也跟它讲了我的经历,当它听到我要离开的消息,有点不舍的感觉,但毕竟男儿志在四方,它也便理解了,于是给我介绍了一位它的朋友,说是让我去找它,能助我早日修仙成功。


关注微信公众号CodingArtist,可以第一时间得到文章更新通知! _

你可能感兴趣的:(iOS Runtime修炼<四>)