【问题标题】:Cocoa Interface StyleCocoa 界面风格
【发布时间】:2008-12-16 16:21:14
【问题描述】:

我正在做一个 iPhone 应用程序的项目。我们有一个可可顾问进来几个星期。他向我展示了一个有趣的 Cocoa 习语,处理接口,但我们之间存在语言障碍,他无法真正解释为什么这样做或记录在哪里,所以我可以自己学习更多。我进入了猴子看模式,只是使用了他喜欢的风格。但我对这种风格的历史一无所知,这让我很烦。这当然不是一个非正式的协议。果然,查看一些 Cocoa API 标头,我有时会看到他断言的样式是“Cocoa”方式。这是一个示例(注意访问器、修改器等,每个都有自己的接口声明,没有有趣的大括号):

@interface AViewController : UIViewController <UITextViewDelegate> {

@public
    UITableView *tableView;
@private
    NSUInteger someIndex;
}

@property (nonatomic, retain) ...
@end

@interface AViewController (AViewControllerCreation)

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil withController:(id)controller;

@end

@interface AViewController (AViewControllerMutator)

- (void) doSomeSettingStuff;

@end

@interface AViewController (AViewControllerAccessor)

- (NSString *)doSomeAccessorStuff;

@end

@interface AViewController (AViewControllerAction)

- (IBAction)cancel:(id)sender;

@end

@interface AViewController (AViewControllerTableViewDelegate)  <UITableViewDelegate, UITableViewDataSource>

@end

你可以在NSButton、NSControl等中看到这种设置界面的风格。有趣的是,对应的类如UIButton、UIControl不使用这种成语。嗯,这些可能是因为我认为 UIKit 是在 AppKit 之后完成的。那么这个成语是“老帽子”吗?另外,除了风格之外还有什么理由吗?是不是很好的风格?坏的?有什么文档可以解决这个问题吗?谢谢大家。

【问题讨论】:

    标签: iphone objective-c cocoa coding-style interface


    【解决方案1】:

    这些在 Objective-C 中被称为“类别”。类别使得同一类可以有多个@interface 和@implementation 块。这甚至可以在您可以在标准 Apple 框架中的类上添加方法的范围内工作,例如在 NSString 上添加一个类别以向其添加新方法。类别可以添加方法但不能添加实例变量,因此在您的示例中,第一个 @interface 是核心类声明,所有其他都是 AViewController 类上的类别。

    这绝不是“旧帽子”,但您的示例将类别的使用带到了一个相当奇怪的极端。只要将一个类的实现分解为多个块在逻辑上是有意义的,类别就有意义,例如,如果该类有一堆在逻辑上分为两个或多个组的方法。它们有时还用于通过将名为“private”的类别@interface 与@implementation 放在同一文件中来声明伪私有方法。 ObjC 的动态分派意味着不存在私有方法之类的东西,但这种方法避免了发布您不希望人们使用的方法的名称。

    上面的例子实际上并没有错,但它有点荒谬。这表明承包商认为,出于某种原因,每种新方法都应该始终有自己的类别,这不是真的。

    【讨论】:

    • 谢谢汤姆。顺便说一句,我删除了很多方法,所以它纯粹是迂腐的。我确实明白他为什么喜欢对事物进行分组(如访问器/突变器)。同样,查看 NSButton 与 UIButton 标头后,我发现两者都被使用了。但是您的回答指出这些实际上是类别。最好的答案汤姆!
    【解决方案2】:

    这个例子很奇怪,对于任何有经验的 Cocoa 程序员来说肯定会引发危险。

    以相同的方式使用类别将私有方法与公共实现分开是一种常见的做法,我过去也做过同样的事情来将私有线程方法与在主线程上运行的代码分开。不过,我看不出像这样分离所有公共方法会带来什么好处。

    #pragma mark &lt;label&gt; 关键字是解决这种情况的好工具。它允许您在实现中对类似的方法进行分组。我认为这就是您的目标,尽管您不需要在创建群组时过火。例如,在窗口控制器类中,我将有 #pragma mark API#pragma mark NSWindowController Overrides#pragma mark NSObject Overrides#pragma mark NSWindow Delegate Methods 等等。这有助于我在 Xcode 中快速找到并跳转到我正在寻找的方法,尽管这只是风格问题,所以你可以真正使用它,但你认为合适。

    【讨论】:

    • 嗨,马克。是的,我喜欢#pragma 并且已经在 xcode 中使用它,它真的很有帮助!但它似乎只是在组织方面受益。是啊,他的这个成语不是我想出来的!我一开始不喜欢,后来就跟着去了。它在 NSButton、NSControl 等中,嗯,那是 Apple 的代码;)
    【解决方案3】:

    拥有一个公共类别的一个非常好的理由,Apple 经常使用的一个,是为了扩展一个具有该类别框架中存在的功能的类,但在定义该类的框架中不存在该功能。例如,NSString 是在 Foundation.framework 中定义的,但 NSString 上的类别定义用于将 NSString 绘制到屏幕的方法是在 AppKit.framework 中定义的。

    类别的另一个很好的用法是依赖隐藏;例如,如果您确实需要对类的一部分进行 boost,则可以将其放在单独的头文件和实现文件中,并且需要 boost 部分的类的用户可以将该头文件与最初定义该类的头文件一起导入,并且只有那个文件需要很长时间才能编译。这在 64 位运行时更有用,其中类别可以添加实例变量。

    正如 Tom 指出的那样,对多个源文件(但只有一个标头)进行非常大的实现也是一个不错的选择:)

    我只是想补充一下 Tom 的原始答案:通常,在声明私有方法时,最好使用类扩展而不是类类别。这样,您可以在与公共方法相同的@implementation 块中实现扩展,而不会收到有关“缺少类别实现”的警告。示例:

    // .h file
    
    @interface Foo : NSObject
    -(void)publicMethod;
    @end
    
    // .m file
    
    @interface Foo () 
    // Notice the empty paren; this is how you define
    // a class extension, which is not the same as a category
    -(void)somePrivateMethod;
    @end
    
    @implementation Foo
    #pragma mark Public methods
    -(void)publicMethod;
    { ... }
    
    #pragma mark Private methods
    -(void)privateMethod;
    { ... }
    @end
    

    【讨论】:

      【解决方案4】:

      我不知道;对我来说,这些看起来很像非正式协议,主要针对代表。请参阅Cocoa Programming with Mac OS X, 3rd Edition 的第 297 - 298 页。这些协议是通过类别实现的……老实说,它们在您的示例中似乎被过度使用了。

      【讨论】:

      • 这是实现非正式协议的一种方式(Obj-C 2.0 也有新的可选协议方法),但诸如 init 方法、访问器和修改器之类的东西不会成为协议的一部分。
      【解决方案5】:

      关于 John Rudy 的回应——非正式协议确实是作为类别实现的,但它们通常是 NSObject 上的类别。由于非正式协议几乎可以被任何对象使用,它需要是类中的一个类别,采用对象继承自,并且几乎所有内容都将从 NSObject 继承。在特定情况下,您可以将非正式协议作为其他类的一个类别,但这是一种不寻常的方法,绝对不是“可可方式”

      【讨论】:

        【解决方案6】:

        好的,我相信汤姆的回答是迄今为止最有用的。然而,由于每个人似乎都认为这是对类别的过度使用,我再次查看了 NSButton。以下是标题的混蛋版本:

        @interface NSButton : NSControl <NSUserInterfaceValidations>
        
        - (NSString *)title;
        - (void)setTitle:(NSString *)aString;
        ...
        ...
        @end
        
        @interface NSButton(NSKeyboardUI)
        - (void)setTitleWithMnemonic:(NSString *)stringWithAmpersand;
        @end
        
        @interface NSButton(NSButtonAttributedStringMethods)
        - (NSAttributedString *)attributedTitle;
        - (void)setAttributedTitle:(NSAttributedString *)aString;
        - (NSAttributedString *)attributedAlternateTitle;
        - (void)setAttributedAlternateTitle:(NSAttributedString *)obj;
        @end
        
        @interface NSButton(NSButtonBezelStyles)
        - (void) setBezelStyle:(NSBezelStyle)bezelStyle;
        - (NSBezelStyle)bezelStyle;
        @end
        
        @interface NSButton(NSButtonMixedState)
        - (void)setAllowsMixedState:(BOOL)flag;
        - (BOOL)allowsMixedState;
        - (void)setNextState;
        @end
        
        @interface NSButton(NSButtonBorder)
        - (void) setShowsBorderOnlyWhileMouseInside:(BOOL)show;
        - (BOOL) showsBorderOnlyWhileMouseInside;
        @end
        
        @interface NSButton (NSButtonSoundExtensions)
        - (void)setSound:(NSSound *)aSound;
        - (NSSound *)sound;
        @end
        

        因此,如果他们使用类别将 NSButton 组织成部分:上面的 (NSButtonMixedState) (NSButtonBorder),实际上只有几个操作,为什么使用它来组织访问器/修改器的风格不好?当然,我的第一个示例是一个愚蠢的迂腐界面,但分离操作分组的意图是相同的。

        【讨论】:

          【解决方案7】:

          首先,仅仅因为某些内容出现在 Apple 的某个头文件中并不一定意味着您应该将其作为做事的好方法的示例。 Apple 的开发人员也是人,并且与其他人一样受到技能和时间压力的限制。

          以这种方式使用多个类别表明 NSButton 的实现分为几个源文件。这可能是因为 NSButton 的不同方面是由不同的人编写的,或者是在不同的时间编写的,或者可能是其他一些原因。无论如何,划分很可能部分基于开发团队及其流程的组织方式,类别系统为划分工作提供了一种方式。在理想的设置中,您会在逻辑功能边界上分解事物,但在实践中其他因素可能会发挥作用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-26
            相关资源
            最近更新 更多