Простое расширение класса / разъяснение наследования

Я пишу Objective-C уже несколько лет и решил вернуться и изучить самые основы, которые помогут мне писать еще лучший код. Я пытаюсь узнать все о переменных экземпляра, наследовании и расширениях классов. Я читал обо всех трех, но есть одна вещь, которая не может не уловить. У меня есть простое приложение, которое содержит 2 класса: Person, Male (наследуется от Person) и, конечно же, Main (которое импортирует класс Male, поэтому может получить доступ к переменным экземпляра, найденным как в Person, так и в Male).

Код прост, и я не буду публиковать его полностью. Обычно Main берет эти переменные и играет с ними. Это часть, которая поражает меня:

    @interface Person : NSObject {
    float heightInMeters;
    int weightInKilos;
}

@property float heightInMeters;
@property int weightInKilos;

@end

Когда я удаляю скобки и объявления переменных, оставляя это так:

@interface Person : NSObject

@property float heightInMeters;
@property int weightInKilos;

@end

Код по-прежнему наследуется и выполняется нормально.

1. Зачем вообще их там объявлять, если мы можем просто создать два свойства?

2. зачем создавать две переменные экземпляра И свойства, чтобы соответствовать им?

3. Я знаю, что мы можем объявить переменные в .m вместо этого, чтобы сохранить их частными для класса и всего, что является его подклассом. нравится:

    @implementation Person {
    float heightInMeters;
    int weightInKilos;
    }

Какая здесь разница? Я чувствую, что мне не хватает многих основ. Есть ли упрощенный способ представить все это в перспективе?


person Henry F    schedule 31.10.2014    source источник
comment
Автоматический синтез свойств был добавлен в Xcode 4.4, поэтому ваша вторая версия подходит, и вам не нужно объявлять переменные экземпляра в большинстве случаев. См., Например, stackoverflow.com/questions/9368676/.   -  person Martin R    schedule 31.10.2014
comment
@MartinR Это интересно ... спасибо за пример. И еще кое-что о путанице ... heightInMeters и weightInKilos - это переменные экземпляра, а не переменные класса, верно?   -  person Henry F    schedule 31.10.2014
comment
да. Чтобы быть точным, если вы объявите @property float heightInMeters;, компилятор синтезирует соответствующую переменную экземпляра _heightInMeters (с добавленным подчеркиванием). Но уже есть много вопросов и ответов по этой теме, если вы ищете переменные свойства и экземпляра, вы должны найти все, что вам нужно.   -  person Martin R    schedule 31.10.2014
comment
Также в Objective-C 2.0 были добавлены переменные, не являющиеся хрупкими экземплярами. Это устранило необходимость объявлять все в блоке @interface {}.   -  person KirkSpaziani    schedule 31.10.2014


Ответы (1)


Когда вы объявляете @property, компилятор автоматически синтезирует переменную с префиксом подчеркивания, метод получения и метод установки.

@interface MyClass () 

@property(strong, nonatomic) NSString *myString;

@end

В этом примере компилятор будет определять переменную как _myString, геттер как

-(NSString *)myString

и сеттер как

-(void)setMyString:(NSString *)string

Ключевые слова после "@property" (strong, nonatomic) определяют атрибуты свойства. strong, значение по умолчанию, подразумевает владение, что означает, что в этом случае экземпляры MyClass будут нести ответственность за сохранение / освобождение своих соответствующих объектов myString. nonatomic означает, что переменная не всегда является допустимым значением в многопоточной среде, например, если геттер вызывается одновременно с сеттером.

Кроме того, компилятор будет обрабатывать точечный синтаксис, используемый для извлечения / установки переменных экземпляра, как вызовы соответствующих методов получения / установки. Следовательно, учитывая экземпляр MyClass

MyClass *exampleClass = [[MyClass alloc] init];

Оба следующих утверждения эквивалентны:

NSString *string1 = example.myString;   // dot syntax
NSString *string1 = [example myString]; // explicit call to the getter method

Для дальнейшего чтения ознакомьтесь с Programming с Руководством по Objective-C.

Что касается ваших конкретных вопросов:

1. Какой вообще смысл их там объявлять, если мы можем просто создать два свойства?

На самом деле не рекомендуется явно объявлять переменные как общедоступные в ваш MyClass.h файл (или в большинстве других случаев). Вместо этого объявление их как свойств автоматически создает частную переменную (и методы доступа), что немного упрощает соблюдение лучших практик ООП. Так что нет смысла объявлять

// MyClass.h

@interface MyClass : NSObject {
    NSString *myString // public variables not good
}

Также из-за того, что я сказал выше относительно синтаксиса точек, если вы используете self.myString внутри MyClass.m или instanceOfMyClass.myString извне, общедоступная переменная myString никогда не будет затронута, потому что синтезируемая переменная называется _myString.

2. Зачем создавать две переменные экземпляра И свойства, чтобы они соответствовали им?

См. Выше - вам не нужны две переменные экземпляра, только одна.

3. Я знаю, что мы можем объявить переменные в .m вместо этого, чтобы сохранить их частными для класса и всего, что является его подклассом. Какая здесь разница? Я чувствую, что мне не хватает основ. Есть ли упрощенный способ представить все это в перспективе?

Если вы объявляете переменные конфиденциально в @implementation части вашего .m файла, компилятор не сможет помочь вам, синтезируя геттеры и сеттеры. Даже как частные методы, геттеры и сеттеры могут помочь снизить сложность вашего кода, например, проверяя правильность значений переменных. (Примечание: вы можете переопределить методы доступа.)

// MyClass.m

@interface MyClass () // private interface

@property(nonatomic, strong) NSString *myString;

@end


@implementation MyClass {
    // no more need for private variables!
    // compiler will synthesize NSString *_myString and accessors
}

-(void)setMyString:(NSString *)string { // overwrite setter

    // no empty strings allowed in our object (for the sake of example)
    NSAssert([string length] > 0, @"String must not be empty");

    // assign private instance variable in setter
    _myString = string;
}

@end

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

person Morgan Chen    schedule 31.10.2014
comment
Спасибо за помощь, это дает отличное понимание. Я также выделил жирным шрифтом три вопроса, касающихся переменных экземпляра и расширений классов. Не могли бы вы поделиться с ними какими-либо сведениями? - person Henry F; 31.10.2014
comment
Вы классные, большое вам спасибо! Очень полезно. Итак, единственная причина объявить частные переменные в .m - это в основном просто, если вы хотите вручную создать геттеры и сеттеры, не так ли? - person Henry F; 31.10.2014
comment
Нет, вы можете воссоздать геттеры и сеттеры для свойства, объявленного в любом файле. Обычно свойства, объявленные как readonly в файле .h, затем будут повторно объявлены как readwrite в .m. Компилятор не будет синтезировать сеттер для свойства readonly, поэтому повторное объявление его в .m позволяет синтезировать частный сеттер для внутреннего использования. Есть и другие уловки, и на эту тему было написано много сообщений в блогах. - person Morgan Chen; 31.10.2014