猫猫分享,必须精品
原创文章,欢迎转载。转载请注明:翟乃玉的博客
地址: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」可以通过运行时的方法获取到。
1class_getInstanceMethod
获取对象方法的方法编号。这里可以自己用command键点进去看看系统里的,他的返回值类型是Method**类型。
2class_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);
不知道当你看到这些的时候有没有想起猫之前说的指向指针的指针,表示类的类,哈哈,这玩意我想到的是指向方法的指针,总之,先这么理解吧,欢迎大家给猫猫指正,猫本身是体育生,没有上过计算机的专业课,一路磕磕绊绊连蒙带猜就靠各位朋友指点教育批评才混过来的,有错误地方欢迎指正哈,设计底层的东西猫是真瞎。。。