第十三条:用“方法调配”技术调试“黑盒方法”
类的方法列表会把选择子的名称映射到相关的方法实现上,使得动态消息能够根据此找到对应的方法,这些方法均以函数指针的形式来表示,其原型如下:
1
id (*IMP)(id, SEL, ...)
例如NSString类中的方法
lowercaseString
,uppercaseString
,capitalizedString
等,这些选择子都映射到不同的IMP上面:1
2
3lowercaseString ---> IMP1
uppercaseString ---> IMP2
capitalizedString ---> IMP3objective-c
中可以通过修改以上的映射关系实现消息实现的改变,这种方法叫做method swizzing
(方法掉配)。1
2
3lowercaseString ---> IMP2
uppercaseString ---> IMP3
capitalizedString ---> IMP1具体的实现方法是调用下列方法
1
void method_exchangeImplementations ( Method m1, Method m2 );
其中
m1
,m2
可通过下列方法获得1
Method class_getInstanceMethod ( Class cls, SEL name );
具体到上面提到的例子可以这样使用:
1
2
3Method lowercaseMethod = class_getInstanceMethod ( [NSString class], @selector(lowercaseString) );
Method uppercaseMethod = class_getInstanceMethod ( [NSString class], @selector(uppercaseString) );
void method_exchangeImplementations ( lowercaseMethod, uppercaseMethod );这样NSString中的文字大小写转换方法就进行了互换。但这样对我们实际的编程中并不会有任何实际的帮助。
前一阵看了一篇文章iOS应用架构谈 view层的组织和调用方案,其中提到的是否有必要让业务方统一派生ViewController
一节中使用到的就是Method Swizzling
技术,具体使用他使用了一个开源库Aspects。具体使用如下创建一个父类为
NSObject
的类NQViewControllerIntercepter
,在类的实现中写下如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41+ (void)load
{
[super load];
[NQViewControllerIntercepter sharedInstance];
}
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
static NQViewControllerIntercepter *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [[NQViewControllerIntercepter alloc] init];
});
return sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (self) {
[UIViewController aspect_hookSelector:@selector(loadView) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo>aspectInfo){
[self loadView:[aspectInfo instance]];
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
[self viewWillAppear:animated viewController:[aspectInfo instance]];
} error:NULL];
}
return self;
}
- (void)loadView:(UIViewController *)viewController
{
NSLog(@"[%@ loadView]", [viewController class]);
}
- (void)viewWillAppear:(BOOL)animated viewController:(UIViewController *)viewController
{
NSLog(@"[%@ viewWillAppear:%@]", [viewController class], animated ? @"YES" : @"NO");
}其中
load
方法会在运行时提前调用,(load与另一个类方法initialize的区别在于,load是类在被引用的时候调用,而initialize是在类的成员方法调用的时候调用,并且这两个方法只会被调用一次)。33行到42行就是我们通过
Aspect
hook到的方法。这样可以在不使用继承的情况下重写父类的方法。第十四条:理解类对象的用意
这条主要围绕这张图进行讲解:
- SuperClass指针确定了继承关系,isa指针描述所属的类。
- 尽量使用类型信息查询方法来确定对象类型(isKindOfClass,isMemberOfClass),而不要直接比较没对象
第十五条:用前缀避免命名空间冲突
- 每个类要选用固定的前缀
- 如果自己开发的程序中用到了第三方库,最好也在前面加上前缀