Каким образом частные и общественные члены будут внедрены в objective-c?
У меня было некоторое обсуждение, связанное с использованием свойств и переменных экземпляра на работе, поэтому я хотел бы найти для них ответ вики. Теперь я знаю, что в objective-c нет реального частного типа члена, все довольно широко. Тем не менее, я немного обеспокоен тем, как мы должны разрабатывать наши классы, а также соблюдать принципы ООП. Я хотел бы услышать мнения об этих трех подходах к разработке:
а. Согласно различным статьям и даже новым курсам развития iPhone в Стэнфордском университете, вы всегда должны использовать свойства везде, где можете. Однако IMHO, этот подход тормозит принципы проектирования ООП, поскольку в этом случае все участники становятся общедоступными. Зачем мне публиковать все внутренние/локальные переменные экземпляра снаружи? Кроме того, есть некоторые очень небольшие (но все же) накладные расходы, если вы используете синтезированные сеттеры через свойства, вместо этого используете локальный ivar напрямую. Здесь образец:
//==== header file =====//
@interface MyClass : NSObject
@property (nonatomic, retain) NSString *publicMemberWithProperty;
@property (nonatomic, retain) NSString *propertyForPrivateMember;
@end
В. Другой подход заключается в объявлении ivars в файле заголовка (без объявления относительных свойств) для частных членов и в том же заголовочном файле для объявления чистых свойств (без объявления относительных ivars) для публичных членов. В таком случае, ivars будут использоваться непосредственно в классе. Этот подход имеет смысл, но не использует все преимущества от свойств, потому что мы вручную выставляем старые значения перед установкой новых. Здесь образец:
//==== header file =====//
@interface MyClass : NSObject{
NSString *_privateMember;
}
@property (nonatomic, retain) NSString *publicMemberWithProperty;
@end
С. Объявлять чистые свойства (без объявления относительных ivars) для публичных членов в файле заголовка и объявлять чистые свойства (без объявления относительных ivars) для частных членов в частном интерфейсе в файле реализации. Такой подход ИМХО более ясен, чем первый, но остается тот же вопрос: почему у нас должны быть свойства для внутренних/локальных членов? Здесь образец:
//==== header file =====//
@interface MyClass : NSObject
@property (nonatomic, retain) NSString *publicMemberWithProperty;
@end
//==== implementation file =====//
@interface MyClass()
@property (nonatomic, retain) NSString *propertyForPrivateMember;
@end
Это решение свободы меня немного раздражает, и я хотел бы найти подтверждение от соответствующих источников о том, как все должно быть сделано. Тем не менее, я не смог найти такие строгие заявления в документах Apple по этому поводу, поэтому, пожалуйста, разместите ссылку на яблочные документы, если они есть, или на любую другую теорию, которая очищает это.
Ответы
Ответ 1
Используя расширения классов, вы можете иметь частные свойства.
Синтаксис расширения класса прост:
Внутри .m файла, который имеет класс, создайте безымянную категорию:
.час
@interface OverlayViewController : UIViewController <VSClickWheelViewDelegate>
- (IBAction)moreButtonClicked:(id)sender;
- (IBAction)cancelButtonClicked:(id)sender;
@end
.m
#import "OverlayViewController.h"
@interface OverlayViewController ()
@property(nonatomic) NSInteger amount;
@property(retain,nonatomic)NSArray *colors;
@end
@implementation OverlayViewController
@synthesize amount = amount_;
@synthesize colors = colors_;
//…
@end
Теперь вы получили все аспекты свойств для частных пользователей, не раскрывая их публично. Не должно быть никаких накладных расходов на синтезированные свойства записанных методов получения/установки, так как компилятор будет создавать более или менее то же самое во время компиляции.
Обратите внимание, что этот код использует синтезированные ивары. Нет необходимости в объявлении ivar в заголовке.
Есть хорошая статья об этом подходе.
Вы также спрашиваете, почему использовать свойства для частных иваров. Есть несколько веских причин:
Поскольку LLVM 3 также возможно, объявить ivars в расширениях класса
@interface OverlayViewController (){
NSInteger amount;
NSArray *colors;
}
@end
или даже в блоке реализации
@implementation OverlayViewController{
NSInteger amount;
NSArray *colors;
}
//…
@end
см. "WWDC2011: Сессия 322 - Достижения Objective-C в глубине" (~ 03: 00)
Ответ 2
На самом деле нет чистого, безопасного, нулевого накладного расхода, это решение, которое напрямую поддерживается языком. Многие люди довольны текущими функциями видимости, в то время как многие считают, что им не хватает.
Время выполнения может (но не делает) сделать это различие с ivars и методами. Поддержка первого класса была бы лучшей, ИМО. До тех пор у нас есть некоторые абстракции:
Вариант A
Плохо - все видно. Я не согласен с тем, что это хороший подход, и это не OOD (IMO). Если все видно, то ваш класс должен либо:
- поддерживает все случаи, когда клиент может использовать ваш класс (обычно необоснованный или нежелательный)
- или вы предоставляете им массу правил через документацию (обновления doc, вероятно, останутся незамеченными)
- или у аксессуаров не должно быть побочных эффектов (а не OOD и часто переводится как "не переопределять аксессоры" )
Вариант B
Имеют ли недостатки опции A, и как вариант A, члены могут быть доступны с помощью ключа.
Вариант C
Это немного безопаснее. Как и все остальные, вы все равно можете использовать доступ с ключами, а подклассы могут переопределять ваши аксессуры (даже если они неосознанно).
Вариант D
Один из подходов к этому заключается в том, чтобы написать ваш класс как оболочку поверх типа реализации. Для этого вы можете использовать тип ObjC или тип С++. Вы можете одобрить С++, где скорость важна (это упоминалось в OP).
Простым подходом к этому может быть одна из форм:
// inner ObjC type
@class MONObjectImp;
@interface MONObject : NSObject
{
@private
MONObjectImp * imp;
}
@end
// Inner C++ type - Variant A
class MONObjectImp { ... };
@interface MONObject : NSObject
{
@private
MONObjectImp imp;
}
@end
// Inner C++ type - Variant B
class MONObjectImp;
@interface MONObject : NSObject
{
@private
MON::t_auto_pointer<MONObjectImp> imp;
}
@end
(Примечание: поскольку это было изначально написано, появилась возможность объявлять ivars в блоке @implementation. Вы должны объявить свои типы С++ там, где нет необходимости поддерживать старые инструментальные цепочки или "хрупкие" 32- бит OS X ABI).
С++ Вариант A не является "безопасным", как другие, поскольку для него требуется объявление класса, видимое клиенту. В других случаях вы можете объявить и определить класс Imp в файле реализации, скрывая его от клиентов.
Затем вы можете открыть интерфейс, который вы выберете. Конечно, клиенты могут получить доступ к вашим членам, если они действительно хотят через среду выполнения. Для них было бы проще сделать безопасную работу с типом ObjC Imp - время выполнения objc не поддерживает семантику С++ для членов, поэтому клиенты будут запрашивать UB (IOW все это POD для среды выполнения).
Стоимость выполнения для реализации ObjC заключается в том, чтобы написать новый тип, создать новый экземпляр Imp для каждого экземпляра и хорошее удвоение обмена сообщениями.
Тип С++ практически не будет стоить ничего, кроме выделения (вариант B).
Вариант E
Другие подходы часто отделяют ivars от интерфейсов. Хотя это хорошо, это также очень необычно для типов ObjC. Типы/проекты ObjC часто поддерживают тесные отношения с их иварами и аксессуарами, поэтому вы столкнетесь с сопротивлением некоторых других разработчиков.
Ответ 3
Аналогично С++ Objective C предоставляет общедоступные, частные и защищенные области. Он также предоставляет область пакета, которая похожа на область пакета, определенную на Java.
Публичные переменные классов могут быть ссылками в любом месте программы.
Частные переменные могут ссылаться только на сообщения класса, объявляющего его. Он может использоваться в сообщениях, которые принадлежат к любому экземпляру того же класса.
Объем пакета аналогичен области видимости в пределах одного и того же изображения, то есть исполняемого файла или библиотеки. Согласно документации Apple, на 64-битных архитектурах переменные области пакета, определенные в другом изображении, должны рассматриваться как частные.
Область переменной определяется переменными @public, @private, @protected, @package. Эти модификаторы могут использоваться как способом, аналогичным С++ или Java. Все переменные, перечисленные в объявлении области, относятся к одной и той же области. Кроме того, переменные могут быть указаны в той же строке, где объявлена область действия.
@interface VariableScope : NSObject {
@public
int iVar0;
@protected
int iVar1;
@private
int iVar2;
@package
int iVar3;
@public int iVar01, iVar02;
@protected int iVar11, iVar12;
@private int iVar21, iVar22;
@package int iVar31, iVar32;
}
@end
Для получения дополнительной информации используйте приведенную ниже ссылку
http://cocoacast.com/?q=node/100