猫猫学iOS之RunTime运用方法交换

猫猫分享,必须精品

原创文章,欢迎转载。转载请注明:翟乃玉的博客
地址:http://www.jianshu.com/notebooks/4236923/latest

应用场景

在iOS开发当中,我们经常会用一些常用的系统写方法,但是有时候这些方法让我们并不满意,比如说我切割字符串,系统有个方法是如下
- (NSString *)substringFromIndex:(NSUInteger)from;切割一串字符串(self)从第from个字符开始到最后,返回值是切割好的字符串
比如,我想将字符串@"123456789"从第二个字符串切割,然后打印出来,这时候我可以通过下面的几种方法解决。

方法一:

最简单最直接的方法,上代码:

NSString *str = [@"123456789" substringFromIndex:2];
NSLog(@"str = %@",str);

效果:
效果

简单粗暴,但是这个仅限于此场景,如果我想对截取后的字符做更多操作,需要每个地方都这么写,会有很多重复代码。

方法二:

运用分类技术,自己写方法来实现

分类代码:

@interface NSString (NYCategory)
- (NSString *)ny_substringFromIndex:(NSUInteger)from;
@end

@implementation NSString (NYCategory)

-(NSString *)ny_substringFromIndex:(NSUInteger)from{
    NSString *subStr = [self substringFromIndex:from];
    NSLog(@"截取后的字符串是 %@",subStr);
    return subStr;
}
@end

调用:

//首先导入分类
#import "NSString+NYCategory.h"
//调用
NSString *str = [@"123456789" ny_substringFromIndex:2];
NSLog(@"str = %@",str);

效果:


效果

这种方法可以做更多的事情,比如我想在切割的时候把当前字符串赋值成切割好的字符串,或者说给切割的字符串去掉空格等等,总之,没有做不到 只有想不到。

  • 但是他还是有一定的缺点:
    1 对导入分类有着很高的依赖,每次用这个方法我必须都要导入自己的分类。
    2 方法名不能跟系统的一样,有时候我们就像用系统的,并且还就是想要新东西(他喵的你有病吧。。。),于是最开始我很天真的定义分类用系统的方法名,然后调用,然后就悲剧了,如下:



    很容易造成了递归死循环。。。然后改成super,更悲剧了,你懂的,分类根本没有super这一说,好吧,那就用定义NSString的子类,重写方法,然后调用super。。。(我觉得问这问题的人真的有病。。。)

方法三:

方法三就是运用运行时的交换方法的手段来改进方法二的两个缺点,简单说,就是我又不想导入分类,也不想定义子类,然后还想用系统NSString的方法名,就原来怎么用我现在就怎么用,还想要这样的效果。有这样的好事嘛?有 RunTime...(这东西感觉基本就是为了面试官而存在的)

实现:

实现起来还是用分类,与之不同的是我们需要用到类加载方法和runtime的方法交换函数method_exchangeImplementations

分类:

@interface NSString (NYCategory)
- (NSString *)ny_substringFromIndex:(NSUInteger)from;
@end

#import "NSString+NYCategory.h"
#import <objc/message.h>

@implementation NSString (NYCategory)
// 当程序一运行,所有类会被加载,这时候会调用这个方法
+ (void)load{

    //class_getInstanceMethod是获取类的对象的方法
    Method subStrMethod = class_getInstanceMethod([NSString class], @selector(substringFromIndex:));
    
    Method ny_subStrMethod = class_getInstanceMethod([NSString class], @selector(ny_substringFromIndex:));
    
    // 交换方法实现
    method_exchangeImplementations(subStrMethod, ny_subStrMethod);
    
}

-(NSString *)ny_substringFromIndex:(NSUInteger)from{
    NSString *subStr = [self ny_substringFromIndex:from];
    NSLog(@"截取后的字符串是 %@",subStr);
    return subStr;
}

调用:

    NSString *str = [@"123456789" substringFromIndex:2];
    NSLog(@"str = %@",str);
效果

在这里调用的时候我们并没有导入分类,用的方法名也是系统的,但是效果却可以做到方法二的效果。

原理:在运行的时候,当分类被加载(load方法执行)的时候,用到了运行时的交换方法实现的机制,对方法名和实现进行了对调,这里我们需要明白下面的概念:

  • 一个方法包括了 方法名方法实现
  • 方法名: 「类型是Method」可以通过运行时的方法获取到。
    1 class_getInstanceMethod 获取对象方法的方法编号。这里可以自己用command键点进去看看系统里的,他的返回值类型是
    Method**类型。
    2 class_getClassMethod 获取类方法的方法编号。
  • 方法实现: 也就是方法具体要做的事情「类型是IMP」可以通过运行时的方法class_getMethodImplementation获取到。
    /**
     *  获取类方法
     *
     *  @param cls  Class:获取哪个类方法
     *  @param name SEL:获取方法编号,根据SEL就能去对应的类找方法
     *
     *  @return  Method 类方法名
     */
    OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    
    /**
     *  获取对象方法
     *
     *  @param cls  Class:获取哪个类方法
     *  @param name SEL:获取方法编号,根据SEL就能去对应的类找方法
     *
     *  @return  Method 对象方法名
     */
    OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    
    /**
     *  获取方法实现
     *
     *  @param cls  Class:获取哪个类方法
     *  @param name SEL:获取方法编号,根据SEL就能去对应的类找方法
     *
     *  @return  IMP 方法实现
     */
    OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
  • 交换方法
    对两个方法名的方法实现进行对调,在运行的时候,当调用方法时(perform之类的),原来的时候,我们调用@selector(substringFromIndex:)方法,他会自己找到他的实现,运行,但是当我们对调之后,调用@selector(substringFromIndex:)会运行@selector(ny_substringFromIndex:)的实现
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

不知道当你看到这些的时候有没有想起猫之前说的指向指针的指针,表示类的类,哈哈,这玩意我想到的是指向方法的指针,总之,先这么理解吧,欢迎大家给猫猫指正,猫本身是体育生,没有上过计算机的专业课,一路磕磕绊绊连蒙带猜就靠各位朋友指点教育批评才混过来的,有错误地方欢迎指正哈,设计底层的东西猫是真瞎。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,838评论 0 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,107评论 19 139
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,744评论 7 64
  • 一、前言 简书上有哪些优质用户?有多少大V粉丝数上万,获赞数上万?小透明的自己能排到多少位?大V之间相互关注情况如...
    古柳_Deserts_X阅读 4,549评论 52 101
  • 在这片天地里,我有自己的故事。我希望我不属于任何一个人,任何一个地方。我只是一个游云野鹤,到哪里也没有家。...
    沐十一阅读 353评论 0 3