Какова видимость переменных экземпляра @synthesized?

Если в вашем общедоступном интерфейсе есть свойство, подобное следующему

@interface MyClass : NSObject
@property(strong) NSString *myProp;
@end

А затем синтезируйте его, фактически синтезируя переменную:

@implementation MyClass
@synthesize myProp = _myProp; // or just leave it at the default name..
@end

Какая видимость переменной экземпляра _myProp? То есть считается ли это @public, @protected или @private? Я предполагаю, поскольку MySubClass может наследовать от MyClass, тогда он также получит свойства (естественно), но будет ли он также наследовать видимость переменной экземпляра?

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


person jbrennan    schedule 14.12.2011    source источник


Ответы (5)


Синтезированный ivar полностью невидим для всего кода, который не видит строку @synthesize (что в основном означает все, что находится за пределами файла .m). Это не @protected, это не @private, это просто неизвестно. С @private ivar другому коду, пытающемуся получить к нему доступ, будет сказано, что он частный, но с синтезированным ivar другому коду, пытающемуся получить доступ, будет сказано, что это поле просто не существует.

В качестве мысленного эксперимента попробуйте представить ситуацию, в которой ивар действует так, как будто он был @protected. Вы делаете подкласс и возитесь там с ivar. Теперь вы вернетесь к суперклассу и измените @synthesize myProp на @synthesize myProp=foo. Что происходит в подклассе? Когда компилятор обрабатывает подкласс, он не видит строку @synthesize, поэтому он не догадывается, что вы только что изменили имя ivar. Фактически, он даже не может сказать, поддерживается ли свойство ivar или реализовано ли оно с помощью специально написанных методов доступа. Я надеюсь, что очевидно, почему это означает, что подкласс не может получить доступ к ivar, как и любой другой класс.

Тем не менее, я не совсем уверен, что делает компилятор, если вы пишете код в том же файле .m, который пытается получить доступ к ivar. Я ожидаю, что он будет рассматривать ivar как @private (поскольку компилятор действительно может видеть, что ivar существует).

Кроме того, ничто из этого не имеет отношения к методам выполнения. Другие классы могут по-прежнему использовать методы выполнения obj-c для динамического поиска списка ivar вашего класса и возиться с ним.

person Lily Ballard    schedule 14.12.2011

Если он объявлен в вашем интерфейсе, он фактически становится общедоступным при использовании декларативного @property. Если вы хотите использовать @property декларативы и сохранить их свойство по-настоящему частным, вам следует создать частную категорию в своей реализации.

MyClass.h

@interface MyClass : NSObject {
@private
    NSObject* foo;
}
@end

MyClass.m

#import "ClassWithPrivateProperty.h"

@interface MyClass ()
    @property (nonatomic,retain) NSObject* foo; 
@end

@implementation MyClass
@synthesize foo;
// class implementation...
@end
person Chris Wagner    schedule 14.12.2011
comment
Хороший совет, но ничего не говорит о видимости самого ивара. - person Lily Ballard; 15.12.2011
comment
Ваш ответ на самом деле не касается видимости поддерживающего ivar самого. - person jscs; 15.12.2011

Синтезированная переменная действует так, как если бы она была объявлена ​​@private:

@interface Garble : NSObject
@property (copy) NSString * s; 
@end
@implementation Garble
@synthesize s;
@end

@interface Bargle : Garble
@end

@implementation Bargle

- (void) useS {
    NSLog(@"%@", s);    // error: instance variable 's' is private
}

@end

Клянусь, я видел это в документы, но я не могу найти его прямо сейчас. Будет обновлено, если я его найду.

person jscs    schedule 14.12.2011

Вы можете создать динамическое свойство и указать компилятору, что его экземпляр будет создан во время выполнения.

А затем в своем подклассе напишите свой собственный геттер или синтезируйте свойство.

@interface BaseClass: NSObject

@property (неатомарный, сильный) NSString * ThisWillBeSynthesizedInRespectiveSubclasses;

@конец

@implementation BaseClass

@dynamic ThisWillBeSynthesizedInRespectiveSubclasses;

@конец

В подклассах

Подкласс @interface: BaseClass

@конец

@implementation Subclass @synthesize ThisWillBeSynthesizedInRespectiveSubclasses = _ThisWillBeSynthesizedInRespectiveSubclasses;

@конец

или вы пишете свои собственные методы установки / получения.

Надеюсь это поможет !

person SRP-Achiever    schedule 22.04.2014

Остальные классы имеют доступ ко всему, что они #include. Другими словами, ко всему, что находится внутри вашего заголовка.

Если что-то появляется только в вашем файле реализации, другие классы (включая подклассы) не знают об этом. Таково синтезируемое свойство. Другие классы знают только о свойстве (свойство означает метод получения и установки), но они ничего не знают о внутренней реализации его методов.

Обратите внимание, что спецификаторы доступа (общедоступный / частный / защищенный) в obj-c - это только намек для компилятора, что даже если что-то появляется в файле заголовка, к нему нельзя получить доступ. Среда выполнения никак это не проверяет.

Что произойдет, если вы поместите его в расширение класса? Обратите внимание, что свойство - это набор из двух методов. Вы просто скрываете методы из каждого класса, который включает основной заголовок вашего класса, но не заголовок расширения класса.

Мы используем это, например, чтобы объявить свойство как доступное только для чтения, а в продолжении класса мы объявляем его как readwrite. Тогда мы можем использовать сеттер только внутри класса.

person Sulthan    schedule 14.12.2011
comment
Ваши первые три абзаца несколько неточны; синтезированная переменная по-прежнему недоступна, даже если она определена в том же файле (см. мой пост). Во-вторых, директивы scope - это больше, чем просто подсказки; при попытке доступа к @private ivar возникнет ошибка компилятора. Вы правы в том, что это можно обойти с помощью функций времени выполнения. - person jscs; 15.12.2011
comment
Вы правы, в одном файле может быть две реализации, и тогда он будет вести себя как @private. В любом случае, первая подсказка, почему к нему нельзя получить доступ, - это то, что он не включен. - person Sulthan; 15.12.2011