[英]Confusion with header and Implementation files in Objective-C
首先,請原諒這個問題的愚蠢,但我不是來自C / C ++背景。 我有點不清楚.h和.m文件在屬性方面的角色差異。
我理解接口的概念,我看到.h文件部分是實現的接口,但我不清楚的是:
當我寫這樣的東西時,我在括號中定義了什么:
IBOutlet UITextField * numberField;
這是接口中的字段定義嗎?
當我將.Property行添加到.h文件時,這些是auto屬性的實際實現還是只是一個接口藍圖? 如果是@syntesis那么實際的實現呢?
我想我最大的困惑似乎是,如果我想要一個屬性,我在接口支撐中的三個不同位置(1)定義我需要的東西,(2)在大括號外面的@property和(3)在@synthesis中定義.m文件。 這看起來很長,但如果我能弄清楚這三個部分的作用,那就好了。
干杯,克里斯。
我將在下面回答您的問題,但也許學習這些內容的最佳方法是閱讀一些針對該語言新手的用戶友好的注釋,例如cocoadevcentral上 的Learn Objective-C教程 。
一個例子
我想以一個例子幫助回答你的問題(我喜歡通過實例學習)。 假設你是一名教師,他正在編寫一個程序,向學生詢問一個特定的是/否問題,並記錄有多少是正確的,以及有多少學生要求。
這是這個類的可能接口:
@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
做了什么
這些基本上是無操作詞,它們只是一種告訴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
但是當你這樣做時,幕后沒有方法調用,不像點符號。 使用點表示法,您將調用setter(或者如果之后沒有=則獲取getter),並且這兩行是相同的:
question.questionStr = @"Chocolate?";
[question setQuestionStr:@"Chocolate?"];
避免使用箭頭符號支持點表示法通常是個好主意,因為點符號可以強制執行有效狀態 - 例如,您的類具有的指針始終保留。 您甚至可以通過將實例變量聲明為@private
來禁止其他人使用箭頭符號; 如果你為它聲明@property
,他們仍然可以使用getter和setter來訪問它。
@synthesize的作用
現在,當你開始實際實現你的類時,@ @synthesize
說“確保為這個屬性實現getter和setter。” 它沒有說“為我實現這兩個”,因為編譯器很有禮貌,可以先檢查你自己的實現,只填寫你錯過的部分。 您根本不需要使用@synthesize
,即使您使用@property
輸出了wazoo - 如果您涉及到這類內容,您可以隨時為您的setter和getter提供實現。
您可能在上面的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
知道numWrongAnswers
+ numCorrectAnswers
= numTimesAsked
,我們只需要存儲這三個數據點中的任意兩個,我們總是可以通過使用我們知道的兩個值來考慮另一個數據點。 這里的要點是要理解@property
聲明實際上只是聲明一個setter和getter方法,它通常對應於一個實際的實例變量 - 但並非總是如此。 該@synthesize
默認關鍵字不符合實際的實例變量,這樣很容易為編譯器為您的實現填寫。
有單獨的.h
和.m
文件的原因
順便說一句,在一個文件( .h
頭文件)中聲明方法並在另一個文件( .m
或方法文件)中定義它們的實現的全部要點是幫助解耦代碼。 例如,如果您只更新項目中的一個.m
文件,則不必重新編譯其他.m
文件,因為它們的目標代碼將保持不變 - 這樣可以節省時間。 另一個優點是你可以使用一個只包含頭文件和預編譯目標代碼的庫,甚至是你需要頭文件的動態庫,這樣編譯器就知道存在哪些方法,但這些方法甚至沒有鏈接到與您的可執行文件。 當您第一次開始編碼時,很難理解這些優點,但只是在短時間內實現的邏輯分解和封裝變得有用。
我希望這對你有所幫助!
方法是在大括號之外定義的,因為大括號意在封裝對象的狀態,可以爭論的不包括實例或類方法。
您在大括號中定義的是可以作為self.ivar引用的實例變量
@property和@synthesize指令只是為您的實例變量設置訪問器,因此您可以通過執行self.ivar = someVar來設置它們。 換句話說,它設置了“點語法”供您使用。
並回答你的結局問題:要定義屬性或實例變量,只需在.h文件中將其聲明為大括號內的變量。 要在同一屬性上設置訪問器方法,您需要同時執行@property和@synthesize。
嗯,這只是客觀C語法,方法和{}以外的@property以及{}中的變量。
@property是告訴你要寫getter和setter(強制執行它)的方式,但你可以編寫getter / setter而不設置它們@property。 @property在.h文件中是因為它的聲明。 為什么它在{}以外,正如我之前所說的語法,我們能做什么?
@synthesis將實際實現getter和setter,如果你沒有合成但是你已經設置了@property,你必須用手實現那些getter和setter。 而@synthesis是在.m文件中,因為它的實現。
您可以在此處找到有關此主題的更多信息。
括號內的變量定義了類的物理結構 。 這些是存儲信息的實際實例變量。
括號外的東西構成了類的接口 - 方法和屬性。 一個屬性本身並不保留任何存儲空間或影響任何變量 - 它只是聲明一個用於訪問某些東西的通用接口。 請記住,屬性不必具有基礎實例變量 - 例如,ShoppingCart類中的totalPrice屬性可以動態地對購物車中所有項目的價格求和。
在實現文件中,您告訴類如何實際執行其工作。 顯然,對於方法,您只需提供一個實現。 對於屬性,您可以自己提供訪問器實現,也可以要求它為實例變量合成訪問器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.