在项目中我们经常要设置统一的背景色,在之前我是用继承做的。如下:
@interface ZXBaseViewController : UIViewController
@end
@implementation ZXBaseViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self addBackButton];
}
- (void)addBackButton
{
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
item.tintColor = [UIColor whiteColor];
self.navigationItem.backBarButtonItem = item;
[self.view setBackgroundColor:[UIColor redColor]];
}
@end
后来看了casa大神的iOS应用架构谈 view层的组织和调用方案,觉得这种方法耦合度太高,对项目侵略性很大,不管什么都要继承,而一些TableViewController之类的还没有办法继承,又要重写非常麻烦。
于是我想到了使用黑魔法Method swizzling。
Method swizzling的介绍可以看女神念茜的一篇文章:Objective-C的hook方案(一): Method Swizzling
于是没没几分钟,我就写好了一个例子。
@implementation UIViewController (KGTracking)
- (void)addBackButton
{
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
item.tintColor = [UIColor whiteColor];
self.navigationItem.backBarButtonItem = item;
[self.view setBackgroundColor: [UIColor redColor]];
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
swizzleMethod(class, @selector(viewDidLoad), @selector(kg_viewDidLoad));
});
}
void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (void)kg_viewDidLoad
{
[self kg_viewDidLoad];
[self addBackButton];
}
@end
原本的样子是这样的:
我把上面的文件引入到pch之后,来看看效果:
纳尼?怎么全红了?
我试着加入了如下一个log,看看是什么原因
- (void)kg_viewDidLoad
{
[self kg_viewDidLoad];
[self addBackButton];
NSLog(@"viewDidLoad: %@",[self class]);
}
打印结果如下:
2015-05-13 14:59:17.286 KuaiGou[13323:189187] viewDidLoad: KGTabBarController
2015-05-13 14:59:17.307 KuaiGou[13323:189187] viewDidLoad: UINavigationController
2015-05-13 14:59:17.333 KuaiGou[13323:189187] viewDidLoad: KGHomeViewController
2015-05-13 14:59:17.338 KuaiGou[13323:189187] viewDidLoad: UIInputWindowController
等等,这个UIInputWindowController是什么鬼?会不会就是他在作怪?是不是加个判断是这个类的就不加背景色就行了呢?
结果狠狠的打了我的脸,这个类在UIKit里找不到。
我请教了虾神,虾神让我用reveal看看是不是这玩意作怪,如果是的话,到github上能够找到这个文件。
好的,我先用reveal来看看。
在分离模式下并没有看到什么异样,合并起来就变成一片红色了,基本可以肯定是这个UITextEffectsWindow的颜色被我染红了。
既然问题已经找到了,那我们上github找这家伙吧,然后把他加到工程里,就能解决问题啦!
过不了多久,就顺利的找到了这个repoiOS-Runtime-Headers,里面有个包含了UIInputWindowController的UIKit的framework。
是不是把这个UIKit加入工程,就能顺利引用了呢?
我尝试了下,果不其然,跟sdk里的UIKit冲突了= =好吧,肯定有解决的办法的,只是我还没想到,这条线索到这里中断了,尝试一天并没有解决。
然后第二天午睡的时候,趴在桌上做着梦,我突然想到了NSClassFromString()
这个方法。既然这里是特例,而且我也已经准确的知道了类名,何不用这个方法呢?
- (void)kg_viewDidLoad
{
[self kg_viewDidLoad];
if (![self isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
[self addBackButton];
}
}
迫不及待的来看看效果:
奈斯啊!没有白费我做梦也想着他。
总结:
没有金刚钻不要揽瓷器活,黑魔法hack一时爽,出了问题的时候可能也会弄得你痛不欲生,给你带来方便的同时一定要慎用!