猫猫学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);

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

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