Стиль интерфейса какао - PullRequest
       20

Стиль интерфейса какао

3 голосов
/ 16 декабря 2008

Я работаю над проектом для iPhone. У нас был консультант по какао в течение нескольких недель. Он показал мне интересную идиому Какао, касающуюся интерфейсов, но между нами был сложный языковой барьер, и он не смог объяснить , почему это было сделано или где это было задокументировано, чтобы я мог узнать больше самостоятельно. Я перешел в режим просмотра обезьян и просто использовал стиль, который он предпочитает. Но это чертовски неприятно, что я не знаю больше об истории этого стиля. Это, конечно, НЕ неофициальный протокол. Конечно же, глядя на некоторые заголовков API Какао, я иногда вижу, что он утверждал, что стиль "Какао". Вот пример (средства доступа к заметкам, мутаторы и т. Д., Каждый из которых имеет собственное объявление интерфейса без забавных скобок):

@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. Так это идиома «старая шляпа»? Кроме того, есть ли причина для этого, кроме стиля? Это хороший стиль? Плохой? Какие-нибудь документы, которые идут по этому поводу? Спасибо всем.

Ответы [ 7 ]

13 голосов
/ 16 декабря 2008

Это то, что в Objective-C известно как «категории». Категории позволяют иметь несколько блоков @interface и @implementation для одного и того же класса. Это работает даже в том случае, если вы можете добавлять методы в классы в стандартных платформах Apple, например, добавление категории на NSString для добавления новых методов к ней. Категории могут добавлять методы, но не переменные экземпляра, поэтому в вашем примере первый @interface является объявлением базового класса, а все остальные являются категориями в классе AViewController.

Это ни в коем случае не "старая шляпа", но ваш пример выводит использование категорий в довольно странную крайность. Категории имеют смысл везде, где логично, разбивать реализацию класса на несколько блоков, например, если в классе есть группа методов, которые логически делятся на две или более групп. Они также иногда используются для объявления псевдоприватных методов, помещая категорию @interface с именем "private" в тот же файл, что и @implementation. Динамическая диспетчеризация ObjC означает, что такого понятия, как закрытый метод, не существует, но этот подход позволяет избежать публикации имен методов, которые вы бы предпочли не использовать людям.

Пример, приведенный выше, на самом деле не является неправильным, но это довольно нелепо. Это говорит о том, что подрядчик понял, что каждый новый метод по какой-то причине всегда должен иметь свою собственную категорию, что просто не соответствует действительности.

2 голосов
/ 16 декабря 2008

Этот пример очень странный и обязательно поднимет красный флаг для любого опытного программиста Какао.

Обычной практикой является использование категорий таким же образом для отделения частных методов от публичной реализации, и в прошлом я делал то же самое, чтобы отделить частные потоковые методы от кода, который выполняется в основном потоке. Хотя я не вижу ничего хорошего в том, чтобы отделить все ваши публичные методы таким образом.

Хорошим инструментом для этой ситуации является ключевое слово #pragma mark <label>. который позволяет группировать похожие методы в реализации. Я думаю, что это то, к чему вы стремитесь, хотя вам не нужно идти за борт при создании групп. Например, в классе оконного контроллера у меня будет #pragma mark API, #pragma mark NSWindowController Overrides, #pragma mark NSObject Overrides, #pragma mark NSWindow Delegate Methods и так далее. Это помогает мне быстро найти и перейти к методу, который я ищу в XCode, хотя это просто вопрос стиля, поэтому вы можете использовать его, как считаете нужным.

1 голос
/ 22 апреля 2009

Одна действительно веская причина для использования общедоступной категории, которую Apple часто использует, заключается в расширении класса с помощью функциональности, которая существует в структуре категории, но которая не существует в структуре, которая определяет класс. Например, NSString определен в Foundation.framework, но категории в NSString, определяющие методы рисования NSString на экране, определены в AppKit.framework.

Другое хорошее использование категорий - для сокрытия зависимостей; например, если вам действительно нужно повышение для части класса, это может быть в отдельном заголовке и файле реализации, и пользователь класса, которому нужны части повышения, может импортировать этот заголовок вместе с заголовком, изначально определяющим класс, и только этот файл займет много времени для компиляции. Это более полезно в 64-разрядной среде выполнения, где категория может добавлять переменные экземпляра.

Действительно большая реализация для нескольких исходных файлов (но с одним заголовком) также является хорошим кандидатом, как отмечает Том:)

Я просто хотел бы добавить к первоначальному ответу Тома: как правило, при объявлении частных методов лучше использовать расширение класса, чем категорию класса. Таким образом, вы можете реализовать расширение в том же блоке @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
0 голосов
/ 20 декабря 2008

Прежде всего, то, что что-то появляется в одном из заголовочных файлов Apple, не обязательно означает, что вы должны взять это в качестве примера хорошего способа сделать что-то. Разработчики Apple тоже люди и подвержены тем же ограничениям в умении и времени, что и все остальные.

Использование нескольких категорий таким образом предполагает, что реализация NSButton разделена на несколько исходных файлов. Это может быть потому, что разные аспекты NSButton были закодированы разными людьми, или в разное время, или, возможно, по какой-то другой причине. Независимо от этого, разделение, вероятно, частично основано на том, как организована команда разработчиков и ее процессы, а система категорий обеспечивает способ разделения работы. В идеальной конфигурации вы бы разбили вещи на логические функциональные границы, но на практике другие факторы могут вступить в игру.

0 голосов
/ 17 декабря 2008

Хорошо, я думаю, что ответ Тома - самый полезный. Однако, поскольку все, кажется, думают, что это использование категорий за бортом, я снова взглянул на 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) выше, у которых действительно есть только пара операций, почему это используется для организации методов доступа или мутаторов в плохом стиле? Конечно, мой первый пример был глупым педантичным интерфейсом, но цель разделения групп операций одинакова.

0 голосов
/ 16 декабря 2008

Относительно ответа Джона Руди. Это правда, что неофициальные протоколы реализованы в виде категорий, но они обычно являются категориями в NSObject. Поскольку неформальный протокол может использоваться практически любым объектом, он должен быть категорией в классе, от которого наследуется принимающий объект, и почти все будет наследоваться от NSObject. Вы могли бы обосновать неформальный протокол как категорию в каком-то другом классе в определенных ситуациях, но это необычный подход и определенно не «путь Какао»

0 голосов
/ 16 декабря 2008

я не знаю; для меня они очень похожи на неофициальные протоколы, в основном для делегатов. См. Стр. 297 - 298 из Программирование какао в Mac OS X, 3-е издание . Протоколы реализуются через категории ... И, честно говоря, они, похоже, сильно используются в вашем образце.

...