Я отвечу на ваши вопросы ниже, но, возможно, лучший способ научиться этому материалу - это прочитать несколько удобных для пользователя заметок, предназначенных для новичков в языке, таких как учебное пособие Learn Objective-C более 100 * * кокоадевцентральный .
Пример
Я хотел бы помочь ответить на ваши вопросы на примере (я люблю учиться на примере). Допустим, вы учитель, пишущий программу, которая задает учащимся конкретный вопрос «да / нет» и отслеживает, сколько из них правильно и сколько учеников задали.
Вот возможный интерфейс для этого класса:
@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
и NSString
, как в этом примере, но часто считается хорошей практикой использовать copy
вместо retain
, поскольку NSString*
может фактически указывать на NSMutableString
объект, который может позже измениться, если ваш код ожидает, что он останется прежним.)
Что @property
делает
Когда вы объявляете @property
, вы делаете две вещи:
- Объявление метода установки и метода получения в интерфейсе класса и
- Указывает, как ведут себя сеттер и геттер.
Для первого достаточно знать, что эта строка:
@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
делают
Это в основном неиспользуемые слова, которые действуют только как способ сообщить Интерфейсному Разработчику, на какие части заголовочного файла обращать внимание. 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
Но когда вы сделаете это, за сценой будет происходить вызов метода no , в отличие от точечной записи. С точечной нотацией вы вызываете установщик (или получатель, если нет = после), и эти две строки одинаковы:
question.questionStr = @"Chocolate?";
[question setQuestionStr:@"Chocolate?"];
Часто хорошей идеей является избегать обозначения стрелки в пользу обозначения точки, так как обозначение точки позволяет вам обеспечить правильное состояние - например, что указатели вашего класса всегда сохраняются. Вы даже можете запретить другим использовать обозначение стрелки, объявив переменные вашего экземпляра как @private
; они все еще могут использовать метод получения и установки для доступа к нему, если вы объявите для него @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
на самом деле просто объявляет метод установки и получения, который обычно соответствует фактической переменной экземпляра - но не всегда. Ключевое слово @synthesize
по умолчанию действительно соответствует фактической переменной экземпляра, так что компилятору легко заполнить реализацию за вас.
Причины иметь отдельные файлы .h
и .m
Кстати, весь смысл объявления методов в одном файле (заголовочный файл .h
) и определения их реализации в другом (.m
или файл методов) состоит в том, чтобы помочь отделить код. Например, если вы обновляете только один файл .m
в своем проекте, вам не нужно перекомпилировать другие файлы .m
, поскольку их объектный код останется прежним - это экономит время. Другое преимущество состоит в том, что вы можете использовать библиотеку, которая включает только файлы заголовков и предварительно скомпилированный объектный код, или даже динамические библиотеки, где вам нужен файл заголовка, чтобы компилятор знал, какие методы существуют, но эти методы даже не связаны в с вашим исполняемым файлом. Эти преимущества трудно оценить, когда вы впервые начинаете кодировать, но через некоторое время становятся полезными только логическая разбивка и инкапсуляция реализации.
Надеюсь, это полезно!