我一直不确定的一件事是如何正确处理自定义NSControl
子类和NSCell
子类之间的通信。在我对 Cocoa 的介绍中,我多次看到它提到父控件如何提供许多与子单元/单元的实现相同的方法、访问器和突变器。例如,NSControl
类和NSCell
类的头文件中都有-isEnabled
和-setEnabled:
:
NSControl.h
- (BOOL)isEnabled;
- (void)setEnabled:(BOOL)flag;
NSCell.h
- (BOOL)isEnabled;
- (void)setEnabled:(BOOL)flag;
我知道NSControl
类为NSCell
中找到的大多数属性提供了"覆盖"方法。我更感兴趣的是:它们是如何实现的?或者更好的是,应该如何实现他/她自己的子类的共享属性?显然,只有苹果的工程师真正知道他们的框架内部发生了什么,但我想也许有人可以阐明以干净的方式模仿苹果的封面方法的最佳方式。
我不擅长解释东西,所以我会举一个例子来说明我在说什么。假设我已经像这样NSControl
子类
:BSDToggleSwitch.h
#import "BSDToggleSwitchCell.h"
@interface BSDToggleSwitch : NSControl
@property (nonatomic, strong) BSDToggleSwitchCell *cell;
@property (nonatomic, assign) BOOL sharedProp;
@end
我已经NSActionCell
子类:
BSDToggleSwitchCell.h
#import "BSDToggleSwitch.h"
@interface BSDToggleSwitchCell : NSActionCell
@property (nonatomic, weak) BSDToggleSwitch *controlView;
@property (nonatomic, assign) BOOL sharedProp;
@end
如您所见,它们都共享一个名为sharedProp
。
我的问题是:有效保持控件和单元格之间的共享属性同步的标准方法是什么?这似乎是一个主观问题,我想是的,但我想有一个大多数时候的"最佳方法"来做到这一点。
我过去使用过各种不同的方法,但我想缩小处理它的方式,只使用以最低开销提供最佳数据完整性的技术。我应该使用绑定吗?如何实现调用对应匹配方法的自定义突变器?科沃?我是一个失败的事业吗?
以下是我过去做过的一些事情(其中一些或全部可能是完全古怪或直接错误的):
Cocoa 绑定— 我只是将控件的属性绑定到单元格的属性(反之亦然):
[self bind:@"sharedProp" toObject:self.cell withKeyPath:@"sharedProp" options:nil];
这似乎是一个很好的方法,但是您将绑定到哪个对象/从哪个对象绑定?我已经阅读了所有的 KVO/KVC/Bindings 文档,但我从未真正意识到绑定的方向性的重要性,因为无论哪种情况下属性都应该相同。有一般规则吗?
从突变器发送消息— 我会从控件的突变器向单元格发送消息:
- (void)setSharedProp:(BOOL)sharedProp { if ( sharedProp == _sharedProp ) return; _sharedProp = sharedProp; [self.cell setSharedProp:sharedProp]; }
然后我会在单元格的实现中做同样的事情:
- (void)setSharedProp:(BOOL)sharedProp { if ( sharedProp == _sharedProp ) return; _sharedProp = sharedProp; [self.controlView setSharedProp:sharedProp]; }
这似乎也相当合理,但似乎也更容易出错。如果一个对象在没有值检查的情况下向另一个对象发送消息,则无限循环很容易发生,对吗?在上面的示例中,我为此添加了检查,但我相信有更好的方法。
观察和报告— 我会观察每个对象实现中的属性变化:
static void * const BSDPropertySyncContext = @"BSDPropertySyncContext"; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ( context == BSDPropertySyncContext && [keyPath isEqualToString:@"sharedProp"] ) { BOOL newValue = [[change objectForKey:NSKeyValueChangeNewKey] boolValue]; if ( newValue != self.sharedProp ) { [self setSharedProp:newValue]; } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }
再一次,这似乎是可行的,但我不喜欢为每个共享财产编写一个
if
语句。按照观察思路,我也发送了通知,但由于对照细胞关系是"一对一"的(甜蜜的双关语),这似乎很愚蠢。
同样,我知道这有点主观,但我真的很感激一些指导。我已经学习Cocoa/Objective-C一段时间了,这从一开始就困扰着我。了解其他人如何处理控件和单元格之间的属性同步真的可以帮助我!
谢谢!
首先,请注意,NSCell
主要由于NeXT时代的性能问题而存在。从NSCell
迁移缓慢,你通常不应该创建新的,除非你需要与需要它们的东西进行交互(例如使用NSMatrix
,或者如果你正在做一些看起来很像NSMatrix
的事情)。请注意自 10.7 以来对NSTableView
所做的更改,以淡化单元格,以及NSCollectionViewItem
如何成为完整视图控制器。我们现在拥有了大部分时间只使用视图的处理能力,而无需NSCell
。
也就是说,通常控件只是将消息转发到单元格。例如:
- (BOOL)sharedProp {
return self.cell.sharedProp;
}
- (void)setSharedProp:(BOOL)sharedProp {
[self.cell setSharedProp:sharedProp];
}
如果 KVO 是一个问题,您仍然可以将其与keyPathsForValuesAffectingValueForKey:
(及其亲属)联系起来。例如,您可以执行以下操作:
- (NSSet *)keyPathsForValuesAffectingSharedProp {
return [NSSet setWithObject:@"cell.sharedProp"];
}
这应该可以让您观察sharedProp
并透明地将更改转发到cell.sharedProp
。