iOS笔记:从一次调试看类簇(Class Clusters)

昨天在做界面开发的时候,碰到一个修改 UISearchbBar上的cancle取消按钮颜色的问题,跳转到UISearchBar的头文件看API说明,并未找到可以修改其属性和方法。

然后我使用Xcode上的 Debug View Hierarchy 工具来查看图层,发现原来显示取消按钮的是一个叫 UINavigationButton 类的对象:

iOS笔记:从一次调试看类簇(Class Clusters)_第1张图片
Debug View Hierarchy.png

UINavigationButtion感觉既熟悉又陌生,我在代码里尝试调用它,发现是个私有类,无法被获取。于是我想到了 Runtime ,用 class_copyIvarList来获取它所有的成员变量,通过遍历找到想要的对象名称,代码如下:

unsigned int memberCount = 0;
Ivar *ivarList = class_copyIvarList([UISearchBar class], &memberCount);

for (unsigned int j=0; j

通过日志找到 cancle 相关的名称:

iOS笔记:从一次调试看类簇(Class Clusters)_第2张图片
ivarList debug.png

找到了对应对象的名称,就可以通过KVC的 valueForKey来获取对象的实例并修改它的属性,代码如下:

UIButton *cancleNavBtn = [self.searchBar valueForKey:@"_cancelButton"];
[cancleNavBtn setTitle:@"取消" forState:UIControlStateNormal];
[cancleNavBtn setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];

通过断点我们可以发现其实这个 button 就是之前的UINavigationButton
上面解决的方式还不够完美,我们再看日志下一行有个cancel相关的 UIBarButtonItem ,我推测UIBarButtonItem应该是UINavigationButtion的子类。
既然是 UIBarButtonItem 就应该可以用appearance来修改全局的属性,于是我推导出正确修改取消按钮的方法:

[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil]
                                   setTitle:@"取消"];
NSDictionary *textDic = @{NSForegroundColorAttributeName:[UIColor blackColor]};
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil]
                     setTitleTextAttributes:textDic
                                   forState:UIControlStateNormal];

  • 上面应该才是官方推荐的正确姿势,但这些内容并不是本篇文章的重点,我们回到刚才的UINavigationButton,这个私有button让我联想到几年前读的@我就叫Sunny怎么了的一篇文章从NSArray看类簇,内容是关于类簇(Class Clusters),那么 UIButton 算不算是一种类簇呢?
  • 类簇(Class Clusters)是什么?

    类簇(Class Clusters) 是抽象工厂模式在iOS下的一种实现,在我们完全不知情的情况下,偷偷隐藏了很多具体的实现类,只暴露出简单的接口。
    用通俗一点讲就是一个public的抽象类加上一些private的私有类构成的,它是对一些实现细节进行隐藏,而对外公开的行为进行统一的一种设计。
    例如我们常用的NSNumber, NSArray, NSDictionary以及NSString等都属于类簇。

为了确定UIButton算不算是类簇,我们可以通过LLDB命令来验证这一点。
首先对UIButton的构造函数buttonWithType:进行断点得到其运行地址,命令如下:

breakpoint set -F '+[UIButton buttonWithType:]'

再用地址来打印对应的汇编代码,命令如下:

dis -a 0x0000000xxxxxxxxx//这里的内存地址是随机的,根据上面获取的地址

然后我们会得到一大段汇编代码:
iOS笔记:从一次调试看类簇(Class Clusters)_第3张图片
LLDB_1.png
iOS笔记:从一次调试看类簇(Class Clusters)_第4张图片
LLDB_2.png

注意一下每一行最后有Button关键字的地方,会发现一些平时没见过的私有Button,如UIPopoverButton, UINavigationButton, UITexturedButton, _UIPlacardButton_UIShortPlacardButton以及UIRoundedRectButton等。

通过这些发现的private的私有类,那么我们就可以证明其实这里的UIButton是一个类簇。

总结:

刨根问底一直都是我们程序员追求的目标,本文通过一次简单的调试,意外发现UIButton也是类簇的一员,并使用强大的LLDB让我们了解到一些UIButton内部构造。遇到问题时,知其原理,才能事半功倍。

你可能感兴趣的:(iOS笔记:从一次调试看类簇(Class Clusters))