Cocoa学习

我的Cocoa/Cocoa Touch学习笔记

关于NSCell

作为一个初学者,我一直很弄不明白NSCell的子类,比如,NSButtonCellNSImageCell及其对应的控件之间的关系。今天,在做一个TableView实现的时候,我终于开始有点悟了——好吧,你大可以鄙视我,我的脑袋是不灵光。尽管这是一个简单的问题,但是我还是简单的记录一下我的理解。

问题的起源是“Cocoa Programming For Mac OS X”上一段关于NSCell的叙述:

NSControl inherits from NSView. With its graphics context, NSView is a relatively large and expensive object to create. When the NSButton class was created, the first thing someone did was to create a calculator with 10 rows and 10 columns of buttons. The performance was less than it could have been because of the 100 tiny views. Later, someone had the clever idea of moving the brains of the button into another object (not a view) and creating one big view (called an NSMatrix) that would act as the view for all 100 button brains. The class for the button brains was called NSButtonCell.
– Chapter 17. Custom Views, “For the More Curious: Cells”

现在在此阅读这句话,我似乎已经能够基本理解,不过当时那叫一个困惑啊。为什么用Cell就比Button性能高呢?为什么Cell可以替代Button呢?

我的理解如下(可能并不是很精确):

  • NSButton的继承关系是:NSButton -> NSControl -> NSView -> NSResponder -> NSObject。应该说,是一个很长的继承链了。
  • NSButtonCell的继承关系是:NSButtonCell -> NSCell -> NSObject

比之NSButton,少了两层继承关系。光凭这一点大致就能够解释为什么Cell的性能比Control高很多了。

但是既然Cell和Control都代表了“控件”,那么两者又是怎样的关系呢?

查阅了苹果的文档之后,就发现里面有这么一句话:“The NSButton class uses NSButtonCell to implement its user interface.”

这么一来,一切都清楚了,NSCell是控件的UI显示部分。但是NSCell不是NSView的子类,它又怎么显示自己呢?在NSButton的文档里,又有下面的一段话:

NSButton and NSMatrix both provide a control view, which is needed to display an NSButtonCell object. However, while NSMatrix requires you to access the NSButtonCell objects directly, most of the NSButton class' methods are “covers” for identically declared methods in NSButtonCell. (In other words, the implementation of the NSButton method invokes the corresponding NSButtonCell method for you, allowing you to be unconcerned with the existence of the NSButtonCell.)

这段话的大意是:

NSButtonNSMatrix能够为NSButtonCell提供一个控制视图,用来实现Cell的显示。不过NSMatrix需要直接操作NSButtonCell对象,而NSButton则不需要。因为它已经“封装”了所有NSButtonCell的同名方法。也就是说,对NSButton调用方法(不是所有的方法),实际上是对NSButtonCell调用方法,调用的时候,我们甚至可以无需知道NSButtonCell的存在。

至此,问题解决。甚至,以前没有联系起来的一个问题也解决了:那就是,为什么在Interface Builder中,NSButton总是和NSButtonCell同时存在的——原因,当然也是上面的那段话啦~另外,在Interface Builder里,NSButtonNSButtonCell的Inspector里的属性也基本相同,也是因为上述原因。

一点题外话:

UIKit里没有NSCell的对应类。原因并不是很清楚。不过UIKit里的类的继承结构也与AppKit有所不同。比如,NSTableView的列:NSTableColumn采用的是Cell的显示机制;而UITableView则采用的是UITableViewCell。前者继承自NSObject,后者继承自UIView

关于这样的区别在性能上的差别,我不敢妄下结论,也没法随便比较——毕竟是两个不同平台。似乎在iOS平台上,UITableView的Cell会在离开显示区域的时候被release掉——这可能是为什么iOS平台没有采用UICell(没有这个类的!!!)的原因之一。

不过,我感觉UITableViewNSTableView更加灵活,自定义也更加方便——因为每个UITableViewCell可以很方便的用一个Custom View来做界面;而NSTableColumn则依赖于NSCell,要自定义,需要用自定义NSCell的子类,这样会复杂很多。

(全文完)

Comments