Method swizzling实践

作者: 千煌89 | 来源:发表于2015-05-13 16:08 被阅读2640次

在项目中我们经常要设置统一的背景色,在之前我是用继承做的。如下:

@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

原本的样子是这样的:


Screen Shot 2015-05-12 at 16.52.05.png

我把上面的文件引入到pch之后,来看看效果:

Paste_Image.png

纳尼?怎么全红了?
我试着加入了如下一个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是什么鬼?会不会就是他在作怪?是不是加个判断是这个类的就不加背景色就行了呢?

Paste_Image.png

结果狠狠的打了我的脸,这个类在UIKit里找不到。
我请教了虾神,虾神让我用reveal看看是不是这玩意作怪,如果是的话,到github上能够找到这个文件。

好的,我先用reveal来看看。

Paste_Image.png Paste_Image.png

在分离模式下并没有看到什么异样,合并起来就变成一片红色了,基本可以肯定是这个UITextEffectsWindow的颜色被我染红了。

既然问题已经找到了,那我们上github找这家伙吧,然后把他加到工程里,就能解决问题啦!
过不了多久,就顺利的找到了这个repoiOS-Runtime-Headers,里面有个包含了UIInputWindowController的UIKit的framework。

Paste_Image.png

是不是把这个UIKit加入工程,就能顺利引用了呢?
我尝试了下,果不其然,跟sdk里的UIKit冲突了= =好吧,肯定有解决的办法的,只是我还没想到,这条线索到这里中断了,尝试一天并没有解决。

然后第二天午睡的时候,趴在桌上做着梦,我突然想到了NSClassFromString()这个方法。既然这里是特例,而且我也已经准确的知道了类名,何不用这个方法呢?

- (void)kg_viewDidLoad
{
    [self kg_viewDidLoad];
    if (![self isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
        [self addBackButton];
    }
}

迫不及待的来看看效果:

Paste_Image.png

奈斯啊!没有白费我做梦也想着他。

总结:
没有金刚钻不要揽瓷器活,黑魔法hack一时爽,出了问题的时候可能也会弄得你痛不欲生,给你带来方便的同时一定要慎用!

相关文章

网友评论

  • 72ea0031e578:除UIInputWindowController之外,还需要添加UIApplicationRotationFollowingController的判断,否则当长按输入框(应弹出“粘贴”框)的时候,会出现同样的现象。
    高浩浩浩浩浩浩:@天下无贼 本身就是UIViewController的类别,这个判断无意义吧
    天下无贼:@千煌89 直接判断是不是继承于UIViewController或者UITableViewController 是不是可以解决??
    千煌89:@arthurdai 并不止这些,后来发现actionsheet等好多都会被覆盖,所以设置颜色这种事情就不用这个了……
  • 南栀倾寒:@千煌89 你只要xcode无法提示都是私有的
  • iHTCboy:@千煌89 好的谢谢!🎓
  • 千煌89:@南栀倾寒 我看UIKit属于public的啊,应该是使用了private的才会被拒绝吧
  • 千煌89:@HTC 你可以看下我文中给出的casa的文章,里面有详说
  • 南栀倾寒:NSClassFromString 创建私有的类 上架app会被apple 拒绝
  • iHTCboy:谢谢分享,很棒!

    我水平有限,不太明白,望楼主指点
    对项目侵略性很大?
    没明白


    不管什么都要继承,而一些TableViewController之类的还没有办法继承。
    一个项目,风格形式继承不→_→是很好吗,不用TVC,用TableView也行啊?

本文标题:Method swizzling实践

本文链接:https://www.haomeiwen.com/subject/mhofqttx.html