慎用method swizzling

今天在调试股吧的时候,发现UIImagePickerController右边的BarButtonItem向右边偏移了10个像素。一开始我以为是因为我的UINavigationController的一个category写得有问题导致的,然而我把所有的自定义方法全部弄掉之后,问题还没解决。然后我用了一招更加极端的方法,在applicationDidFinishLoad的地方,把代码全部删掉,把引用的头文件全部删掉,用最原始的UINavigationController和一个空的UIViewController做实验,结果还是偏移了10个像素。这让我百思不得其解,感觉像见鬼了一样。然后我把这个问题交给同事去调,他调了两个小时也崩溃了。

于是我去stackoverflow问了一下。但是大家似乎对这个问题的原因也无法解释,因为我用的是最最基础的代码。一个回答者在下面留言说,最好去查查那个button的大小。虽然UIBarButton是没有frame的,但是我设断点的时候突然发现self.navigationItem.rightButtonItem不是一个Button,而是一个spacing,而且width是-11。但是我奇怪我什么都没做过,为什么会多出来一个spacing,而且宽度是个负数。后来我想起天天基金组前几天在跟财富通组讨论怎么解决iOS7上NavigationBarButtonItem偏移的问题,财富通组的同事说,只要引他们一个头文件就解决了。我想大概是这个头文件引起的问题,它解决了自定义控件向中间偏移的问题,却导致了原生控件向两边偏移的问题。但奇怪的是,我根本没有引用它的头文件,也没调用它的方法,怎么会在我这个地方生效呢?

后来我查了一下那个category,这个是直接引用别人的开源代码。看了代码我就知道是怎么回事了。

这串代码坑爹的地方在于,它把自己的方法和系统的setRightBarButton置换了一下(method swizzling)。于是我明明调用的是系统的方法,而实际上却是调用了它的方法。

更加坑爹的代码在于,method swizzling是在这个类+ load方法里面进行的。只要这个category最后编译进我的代码里,它就会起作用,无论我是不是引入它的头文件。而恰恰财富通组把这个category放在了给我们的framework里面,我们的工程连接了他们的framework,然后这个方法就生效了。

所以说,method swizzling要慎用。虽然objc的动态特性很酷,可以用一些tricks快速解决问题而不需要做大量修改,但是在大型工程里面,特别是涉及到framework的时候,很容易引起side effects。我是iOS组里唯一一个有财富通SVN权限和股吧权限的人,所以我能够发现问题。如果把问题交给别的同事,估计永远就不知道是怎么回事了。

No Comments

Post a Comment