Как частные и публичные члены должны быть реализованы в target-c? - PullRequest
23 голосов
/ 20 ноября 2011

У меня было некоторое обсуждение, связанное с использованием свойств и переменных экземпляра на работе, поэтому я хотел бы найти вики-ответ для этого.Теперь я знаю, что в target-c нет никакого реального закрытого типа члена, все в значительной степени открыто.Тем не менее, я немного обеспокоен тем, как мы должны разрабатывать наши классы, а также соблюдать принципы ООП.Я хотел бы услышать мнения об этих трех подходах к проектированию:

A.Согласно различным статьям и даже новым курсам по разработке iPhone в Стэнфордском университете, вы всегда должны использовать свойства везде, где только можете.Однако, ИМХО, такой подход нарушает принципы проектирования ООП, потому что в этом случае все члены становятся публичными.Почему мне нужно публиковать все мои внутренние / локальные переменные экземпляра снаружи?Кроме того, есть некоторые очень небольшие (но все же) накладные расходы, если вы используете синтезированные сеттеры через свойства, а не используете локальный ivar напрямую.Вот пример:

//==== header file =====//
@interface MyClass : NSObject

@property (nonatomic, retain) NSString *publicMemberWithProperty;
@property (nonatomic, retain) NSString *propertyForPrivateMember;

@end

B.Другой подход заключается в объявлении ivars в файле заголовка (без объявления относительных свойств) для закрытых элементов и в том же файле заголовка для объявления чистых свойств (без объявления относительных ivars) для открытых членов.В таком случае ивары будут использоваться непосредственно в классе.Этот подход имеет смысл, но не использует все преимущества свойств, потому что мы должны вручную освобождать старые значения перед установкой новых.Вот пример:

//==== header file =====//
@interface MyClass : NSObject{
  NSString *_privateMember;
}

@property (nonatomic, retain) NSString *publicMemberWithProperty;

@end

C.Объявить чистые свойства (без объявления относительных ivars) для открытых членов в заголовочном файле и объявить чистые свойства (без объявления относительных ivars) для закрытых членов в приватном интерфейсе в файле реализации.Этот подход, IMHO, более понятен, чем первый, но остается тот же вопрос: почему у нас должны быть свойства для внутренних / локальных членов?Вот пример:

//==== header file =====//
@interface MyClass : NSObject

@property (nonatomic, retain) NSString *publicMemberWithProperty;

@end

//==== implementation file =====//
@interface MyClass()

@property (nonatomic, retain) NSString *propertyForPrivateMember;

@end

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

Ответы [ 3 ]

21 голосов
/ 20 ноября 2011

Используя расширения класса, вы можете иметь частные свойства.

Синтаксис расширения класса прост:

Внутри .m-файла, содержащего класс, создайте безымянную категорию:

.h

@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 в заголовке.

Есть хорошая статья cocoawithlove об этом подходе.

Вы также спрашиваете, почему использовать свойства для частных иваров.Есть несколько веских причин: свойства


Начиная с LLVM 3, можно также объявить ivars в расширениях класса

@interface OverlayViewController (){
    NSInteger amount;
    NSArray *colors;
}
@end

или даже в блоке реализации

@implementation OverlayViewController{
    NSInteger amount;
    NSArray *colors;
}
//…
@end

см.«WWDC2011: Сессия 322 - Достижения Objective-C в глубине» (~ 03:00)

3 голосов
/ 20 ноября 2011

На самом деле не существует чистого, безопасного решения с нулевыми издержками, которое напрямую поддерживается языком. Многие люди довольны текущими функциями видимости, в то время как многие считают, что им не хватает.

Среда выполнения может (но не делает) сделать это различие с помощью иваров и методов. Первоклассная поддержка будет лучшей, ИМО. До этого у нас есть несколько абстракций:

Вариант A

Плохо - все видно. Я не согласен, что это хороший подход, и это не OOD (IMO). Если все видно, то ваш класс должен либо:

  • поддерживают все случаи, когда клиент может использовать ваш класс (обычно неразумно или нежелательно)
  • или вы предоставляете им массу правил через документацию (обновления документации, скорее всего, останутся незамеченными)
  • или средства доступа не должны иметь побочных эффектов (не OOD, и часто переводятся как «не отменять средства доступа»)

Вариант B

Имеет недостатки варианта A ,, и, как и вариант A, члены могут быть доступны по ключу.

Вариант C

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

Вариант D

Один из подходов к этому - написать свой класс как обертку над типом реализации. Для этого вы можете использовать тип ObjC или тип C ++. Вы можете отдать предпочтение C ++, где важна скорость (это упоминалось в 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

(Примечание: с тех пор, как это было изначально написано, появилась возможность объявлять ивары в блоке @implementation. Вы должны объявлять ваши типы C ++ там, если нет необходимости поддерживать более старые цепочки инструментов или «хрупкие» бит OS X ABI).

C ++ Вариант A не так «безопасен», как другие, потому что он требует объявления класса, видимого для клиента. В других случаях вы можете объявить и определить класс Imp в файле реализации, скрывая его от клиентов.

Затем вы можете выставить интерфейс по вашему выбору. Конечно, клиенты могут получить доступ к вашим членам, если они действительно хотят через среду выполнения. Это было бы проще для них безопасно сделать с типом ObjC Imp - среда выполнения objc не поддерживает семантику C ++ для членов, поэтому клиенты будут запрашивать UB (IOW, это все POD для среды выполнения).

Стоимость выполнения для реализации ObjC заключается в написании нового типа, создании нового экземпляра Imp для каждого экземпляра и значительном удвоении количества сообщений.

Тип C ++ практически ничего не будет стоить, кроме размещения (вариант B).

Вариант E

Другие подходы часто отделяют ivars от интерфейсов. Хотя это хорошая вещь , это также очень необычно для типов ObjC. Типы / конструкции ObjC часто поддерживают тесную связь со своими иварами и аксессуарами, поэтому вы столкнетесь с сопротивлением со стороны других разработчиков.

2 голосов
/ 20 ноября 2011

Подобно C ++, Objective C предоставляет публичные, частные и защищенные области.Он также предоставляет область действия пакета, аналогичную области действия пакета, определенной в Java.Публичные переменные классов могут быть ссылками в любом месте программы.На частные переменные можно ссылаться только в сообщениях класса, который их объявляет.Он может использоваться в сообщениях, принадлежащих ЛЮБОМУ экземпляру того же класса.Область видимости пакета аналогична общедоступной области видимости в том же образе, т.е. исполняемом файле или библиотеке.Согласно документации Apple, на 64-битных архитектурах переменные области видимости пакета, определенные в другом образе, должны рассматриваться как частные.Область действия переменной определяется модификаторами @public, @private, @protected, @package.Эти модификаторы могут использоваться как в C ++, так и в 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...