Objective-C:正确处理子类NSControl和NSActionCell之间的共享属性?

时间:2014-02-17 23:21:00

标签: objective-c macos cocoa nscell nscontrol

我一直不确定的一件事是如何正确处理自定义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中找到的大多数属性提供了“封面”方法。我更感兴趣的是:它们是如何实现的?或者甚至更好,如何实现他/她自己的子类的共享属性?显然,只有Apple的工程师真的知道他们框架内部正在发生什么,但我想也许有人可以用一种很好的清洁方式来模仿Apple的封面方法方法。

我很难解释一些东西,所以我会提供一个我正在谈论的例子。假设我已经将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的属性。

我的问题是:有效保持共享属性在控件和单元格之间同步的标准方法是什么?这看起来似乎是一个主观问题,我想是这样,但我想认为有一种最好的“最佳方式”来做到这一点。

我过去曾使用过各种不同的方法,但我想缩小处理它的方式,并且只使用能够以最低开销提供最佳数据完整性的技术。我应该使用绑定吗?如何实现调用其对应方匹配方法的自定义mutator?志愿?我是一个迷失的原因吗?

以下是我过去做过的一些事情(其中一些或全部可能完全是错误的或直接的错误):

  1. Cocoa Bindings - 我只是将控件的属性绑定到单元格的属性(反之亦然):

    [self bind:@"sharedProp" toObject:self.cell withKeyPath:@"sharedProp" options:nil];
    

    这似乎是一个非常好的方法,但你会绑定/从哪个对象?我已经阅读了所有的KVO / KVC / Bindings文档,但是在任何一种情况下,当属性应该相同时,我从未真正了解绑定的方向性的重要性。是否有一般规则?

  2. 从Mutators发送消息 - 我会从控件的mutator发送一条消息:

    - (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];
    }
    

    这似乎也很合理,但似乎也更容易出错。如果一个对象在没有值检查的情况下向另一个发送消息,则无限循环可能很容易发生,对吧?在上面的例子中,我为此添加了检查,但我确信有更好的方法

  3. 观察并报告 - 我会观察每个对象实现中的属性更改:

    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语句。沿着观察的路线,我也发送了通知,但由于控制单元关系与人们可以得到的“一对一”(甜蜜的双关语),这看起来很愚蠢。

    < / LI>

    同样,我知道这有点主观,但我真的很感激一些指导。我一直在学习Cocoa / Objective-C一段时间了,这从一开始就困扰着我。了解其他人如何处理控件和单元格之间的属性同步可能真的帮助我!

    谢谢!

1 个答案:

答案 0 :(得分:3)

首先,请注意,由于NeXT时代的性能问题,NSCell主要存在。远离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