Подтвердить что ты не робот

Путаница с заголовком и файлами реализации в Objective-C

Прежде всего, пожалуйста, простите глупость этого вопроса, но я не из C/С++ фона. Я немного не понимаю, какова разница в ролях между файлами .h и .m, когда дело касается свойств.

Я понимаю концепцию интерфейсов, и я вижу, что файл .h является интерфейсом для реализации, но я не понимаю, что это:

  • Почему свойства/методы определены за пределами {} фигурных скобок?
  • Что я определяю в фигурных скобках, когда пишу что-то вроде этого:

    IBOutlet UITextField * numberField;

    Это определение поля в интерфейсе?

  • Когда я добавляю строки @Property к файлам .h, это фактические реализации свойства n auto или просто схемы интерфейса? Если это так, @syntesis - фактическая реализация?

Я думаю, что моя самая большая путаница, похоже, заключается в том, что если я хочу свойство, я определяю, что мне нужно в трех разных местах (1) в привязках интерфейсов, (2) как @property вне фигурных скобок и (3) с @synthesis в файле .m. Это кажется длинным, но все равно, если я смогу понять, что делают эти три части.

Привет, Крис.

4b9b3361

Ответ 1

Я отвечу на ваши вопросы ниже, но, возможно, лучший способ изучить этот материал - прочитать некоторые удобные для пользователя ноты, предназначенные для людей, знакомых с языком, таких как обучаемый Objective-C на cocoadevcentral.

Пример

Я хотел бы помочь ответить на ваши вопросы с помощью примера (я люблю учиться на примере). Скажем, вы учитель, пишущий программу, которая задает студентам конкретный вопрос "да/нет", и отслеживает, сколько из них правильное и сколько учеников оно задало.

Вот возможный интерфейс для этого класса:

@interface Question : NSObject {
  NSString* questionStr;
  int numTimesAsked;
  int numCorrectAnswers;
}

@property (nonatomic, retain) NSString* questionStr;
@property (nonatomic, readonly) int numTimesAsked;
@property (nonatomic) int numCorrectAnswers;
@property (nonatomic) int numWrongAnswers;

- addAnswerWithTruthValue: (BOOL) isCorrect;
@end

Три переменных внутри фигурных скобок являются переменными экземпляра, и каждый экземпляр вашего класса будет иметь свои собственные значения для каждой из этих переменных. Все, что находится за пределами фигурных скобок, но до @end является объявлением метода (включая объявления @property).

(Обратите внимание: для многих объектов полезно иметь свойства retain, так как вы хотите избежать накладных расходов на копирование объекта и убедиться, что он не выпущен во время его использования. retain a NSString, как в этом примере, но часто считается хорошей практикой использовать copy вместо retain, так как NSString* может на самом деле укажите объект NSMutableString, который позже может измениться, когда ваш код ожидает, что он останется прежним.)

Что @property делает

Когда вы объявляете @property, вы делаете две вещи:

  • Объявление метода setter и getter в интерфейсе класса и
  • Указание того, как ведут себя сеттер и геттер.

Для первого достаточно знать, что эта строка:

@property (nonatomic, retain) NSString* questionStr;

в основном такой же:

- (NSString*) questionStr;                           // getter
- (void) setQuestionStr: (NSString) newQuestionStr;  // setter

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

"В основном" часть "в основном такая же" - дополнительная информация, заданная такими ключевыми словами, как nonatomic и retain.

Ключевое слово nonatomic указывает, что они не обязательно являются потокобезопасными. Общее ключевое слово retain указывает, что объект сохраняет любое заданное значение и освобождает предыдущие значения по мере их отпускания.

Например:

// The correct answer to both questions is objectively YES.
Question* myQuestion = [[Question alloc] init];
NSString* question1 = [[NSString alloc] initWithString:@"Is pizza tasty?"];
// question1 has retain count of 1, from the call to alloc
myQuestion.questionStr = question1;
// question1 now has a retain count of 2
NSString* question2 = [[NSString alloc] initWithString:@"Free iPhone?"];
myQuestion.questionStr = question2;
// question1 has a retain count of 1, and question2 has retain count of 2

Если объявление @property для questionStr было assign вместо этого, то все операторы myQuestion.questionStr = вообще не внесли никаких изменений в счетчики сохранения.

Вы можете прочитать немного подробнее о свойствах здесь.

Что IBOutlet и IBAction сделать

Это в основном слова no-op, которые действуют только как способ сказать Interface Builder, на какие части файла заголовка обратить внимание. IBOutlet буквально становится пустой строкой, когда компилятор смотрит на нее, а IBAction становится возвращаемым значением void. Нам действительно нужны они для работы с Interface Builder, поэтому они важны - просто не для компилятора.

Быстрая заметка о C-структурах и стрелках с меткой ноты

Кстати, часть данных объекта Objective-C очень похожа на структуру C. Если у вас есть указатель на структуру C, вы можете использовать обозначение стрелки -> для ссылки на определенную часть структуры, например:

struct MyStructType {
  int i;
  BOOL b;
};
struct MyStructType* myStruct;
myStruct->i = 3;
myStruct->b = TRUE;  // or YES in Objective-C.

Этот же синтаксис работает аналогично в Objective-C:

Question* question = [[Question alloc] init];
question->questionStr = @"Is this a long answer?";  // YES

Но когда вы это делаете, не происходит вызова метода за кулисами, в отличие от точечной нотации. С помощью точечной нотации вы вызываете сеттер (или getter, если нет = после), и эти две строки одинаковы:

question.questionStr = @"Chocolate?";
[question setQuestionStr:@"Chocolate?"];

Часто рекомендуется избегать обозначений стрелок в пользу точечной нотации, поскольку точечная нотация позволяет вам применять правильное состояние - например, что указатели, которые имеет ваш класс, всегда сохраняются. Вы даже можете запретить другим использовать нотацию стрелки, объявив переменные вашего экземпляра как @private; они могут использовать getter и setter для доступа к нему, если вы объявите для него @property.

Что @synthesize делает

Теперь, когда вы обойдетесь с реализацией своего класса, @synthesize говорит что-то вроде "убедитесь, что getter и setter реализованы для этого свойства". Он не говорит "реализовать оба из них для меня", потому что компилятор достаточно вежлив, чтобы сначала проверить вашу собственную реализацию и заполнить только те части, которые вы пропустили. Вам вообще не нужно использовать @synthesize, даже если вы используете @property из wazoo - вы всегда можете просто предоставить свои реализации для ваших сеттеров и геттеров, если вы в этом заняты.

Вы, вероятно, заметили в интерфейсе Question выше, что есть свойство, которое не является переменной экземпляра (numWrongAnswers), что прекрасно, потому что вы просто объявляете методы. В приведенном здесь примере кода вы можете увидеть, как это работает:

@implementation Question

@synthesize questionStr, numTimesAsked, numCorrectAnswers;

- (void) setNumCorrectAnswers: (int) newCorrectAnswers {
  // We assume the # increases, and represents new answers.
  int numNew = newCorrectAnswers - numCorrectAnswers;
  numTimesAsked += numNew;
  numCorrectAnswers = newCorrectAnswers;
}

- (int) numWrongAnswers {
  return numTimesAsked - numCorrectAnswers;
}

- (void) setNumWrongAnswers: (int) newWrongAnswers {
  int numNew = newWrongAnswers - self.numWrongAnswers;
  numTimesAsked += numNew;
}

- (void) addAnswerWithTruthValue: (BOOL) isCorrect {
  if (isCorrect) {
    self.numCorrectAnswers++;
  } else {
    self.numWrongAnswers++;
  }
}

@end

Одна вещь, которая происходит здесь, - это подделка переменной экземпляра под названием numWrongAnswers, которая будет избыточной информацией, если мы сохраним ее в классе. Поскольку мы всегда знаем numWrongAnswers + numCorrectAnswers= numTimesAsked, нам нужно хранить только два из этих трех точек данных, и мы всегда можем думать в терминах другого, используя два значения, которые мы знаем, Дело здесь в том, чтобы понять, что объявление @property - это просто объявление метода setter и getter, который обычно соответствует фактической переменной экземпляра, но не всегда. Ключевое слово @synthesize по умолчанию соответствует фактической переменной экземпляра, так что компилятор легко заполнит вашу реализацию.

Причины наличия отдельных файлов .h и .m

Кстати, вся точка объявления методов в одном файле (заголовочный файл .h) и определение их реализации в другом (файл .m или методов) помогает развязать код. Например, если вы обновляете только один файл .m в своем проекте, вам не нужно перекомпилировать другие файлы .m, так как их объектный код останется прежним - это экономит время. Еще одно преимущество заключается в том, что вы можете использовать библиотеку, которая включает только файлы заголовков и предварительно скомпилированный объектный код или даже динамические библиотеки, где вам нужен файл заголовка, чтобы компилятор знал о том, какие методы существуют, но эти методы даже не связаны в с вашим исполняемым файлом. Эти преимущества трудно оценить при первом запуске кодирования, но только логическое разбиение и инкапсуляция реализации становятся полезными через некоторое время.

Я надеюсь, что это полезно!

Ответ 2

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

  • То, что вы определяете в фигурных скобках, это переменные экземпляра, на которые можно ссылаться как self.ivar

  • Директивы @property и @synthesize просто настраивают аксессоры для переменных экземпляра, поэтому вы можете установить их, выполнив self.ivar = someVar. Так, другими словами, он устанавливает "точечный синтаксис" для вас.

и ответить на ваш финал: для определения переменной свойства или экземпляра просто объявите его в вашем .h файле как переменную внутри фигурных скобок. Чтобы настроить методы доступа к этому же свойству, вам нужно сделать BOTH @property и @synthesize.

Ответ 3

  • Ну, это просто синтаксис Objective C, методы и @property вне {} и переменные внутри {}.

  • @property - это способ сказать, что вы собираетесь писать геттер и сеттеры (например, принудительное исполнение), но вы можете писать getter/setter, не устанавливая их @property. @property находится в файле .h, потому что его объявление. И почему он находится вне {}, как я уже говорил перед его просто синтаксисом, что мы можем сделать?

  • @synthesis будет в реальных реализациях getter и seters, если вы не синтезируете, но вы установили их @property, вы должны реализовать эти getter и сеттеры вручную. И @synthesis находится в файле .m, потому что его реализация.

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

http://theocacao.com/document.page/510

Ответ 4

Переменные внутри скобок определяют физическую структуру вашего класса. Это фактические переменные экземпляра, которые хранят информацию.

Материалы вне скобок составляют интерфейс класса - методы и свойства. Свойство само по себе не резервирует места для хранения или не влияет на какую-либо переменную - оно просто объявляет общий интерфейс для доступа к чему-либо. Помните, что свойство не должно иметь базовую переменную экземпляра - например, свойство totalPrice в классе ShoppingCart может динамически суммировать цены всех элементов в корзине.

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