Какова видимость переменных экземпляра @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, тогда он также получит свойства (естественно), но также ли он наследует видимость переменных экземпляра?

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

Ответы

Ответ 1

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

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

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

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

Ответ 2

Если он объявлен в вашем интерфейсе, он практически общедоступен при использовании декларации @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

Ответ 3

Синтезированная переменная действует как объявленная @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

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

Ответ 4

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

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

@interface BaseClass: NSObject

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

@end

@implementation BaseClass

@dynamic ThisWillBeSynthesizedInRespectiveSubclasses;

@end

В подклассах

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

@end

@implementation Подкласс @synthesize ThisWillBeSynthesizedInRespectiveSubclasses = _ThisWillBeSynthesizedInRespectiveSubclasses;

@end

или вы пишете свои собственные методы setter/getter.

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

Ответ 5

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

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

Обратите внимание, что спецификаторы доступа (public/private/protected) в obj-c являются лишь подсказкой для компилятора, даже если что-то появляется в файле заголовка, к нему нельзя получить доступ. Время выполнения не проверяет его каким-либо образом.

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

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