前言:
為什麼叫和我一起學呢?因為從開始寫這系列部落格中我就定了一個方向,不從最基本的講,而是挑一些如果從其他語言(C、C#、 Java、 Javascript等)轉過來的程式員容易出錯的地方。
假設你是有幾年其他語言的開發經驗,對我說的上述基礎語言有了不錯的瞭解。這其實也是我當初學這門語言時最希望別人告訴我的地方。
獨特的@符號
首先,ObjectC是C的超集,為了不和C中已有的東西衝突,ObjectC中特有的東西前面都帶有@符號
比如聲明字串 ,可以這麼聲明,這時就是一個C類型的字串
char *name = "langxue";
這麼聲明出來的字串不是一個ObjectC對象,因此不會帶有一些特定的方法,ObjejctC中字面量字串是這麼聲明的
NSString *name = @"langxue"
文法的差異
一、方法名
ObjectC中的方法名由多個段組成。
比如我們想初始化一個controller,最常用的是這個方法
initWithNibName:bundle:
看起來很奇怪是吧?方法具體簽名如下
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
參數更緊接著調用的謂語,這樣看起來更符合自然文法,在擁有多個參數的情況下即使沒有對參數的說明,也非常容易記住。
中括號運算式:
一個中括弧代表一次調用,看起來比較清晰。
具體調用如下:
[[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
我們先是根據MyViewController類所需的大小分配了一塊記憶體,然後發送訊息去初始化這塊記憶體。
因為方法名是包括:符號的,所以在通過selector選擇方法的時候,:符號不能忘記
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
二、id
這類似於C中的通用指標void*,可以用來儲存任何類的對象。也像C#中的dynamic關鍵字,就是告訴編譯器在編譯時間無需檢查這個變數的類型,啟動並執行時候再檢查確定。換句話說就是動態綁定。由於對象總是帶著isa指標,所以即使我們無法從指標類型得出資訊,也總能從對象本身擷取類型資訊。
在ObjectC中哪裡會用到呢?
1.初始化的時候
ObjectC一個對象不能有兩個名字相同的方法,即使他們的參數和傳回型別不同。因此子類的init方法既不能返回子類,否則無法調用父類相同的init方法,當然也不能返回父類。
這時候就需要用到id這個通用的指標了。
2.不需要具體類型,只需要知道這個執行個體實現了某些方法。類似於C#中對介面編程的用法,常用聲明delegate屬性,以便使用不同的策略提供對象。
@property (nonatomic, weak) id<RequestDelegate> delegate;
需要提醒的是,雖然它可以儲存任何類的對象,如果濫用它,就會失去靜態類型時編譯器所提供的好處。
三、強弱引用( strong, week(ARC); retain, assign(非ARC) )
是否加入引用計數的一種方式。iOS中沒有自動記憶體回收機制,記憶體回收是根據引用計數來決定的。一個對象的引用計數為0的時候會被認為是垃圾馬上被銷毀,這和GC機制的有延遲的回收不一樣的地方。記憶體能更高效的被使用。
在ARC的項目中
@property (strong, nonatomic) MyController *myController;
四、協議 (protocol)
類似於C#中的Interface,區別在於protocol中定義的方法可以選擇不需要實現,也就是@optional的。當然如果沒有標記那麼就是必須實現的。
@protocol BoardActionDelegate<NSObject>@required- (void) selectPressed:(UIView *)sender;@optional- (void) cancelPressed:(UIView *)sender;@end
五、範疇(Categories)
1.命名範疇,類似於C#中的擴充方法,用於給已有的類擴充自訂的方法,區別在於ObjectC中不限制方法的類型,可以是執行個體或者是類方法。但是不能帶成員變數。
比如說我們擴充UIColor類
@interface UIColor (Extract)- (void)extract_getRed; @end
這類檔案的命名最好為原類名 + 擴充類名,如 UIColor+Extract.h 、UIColor+Extract.m
2.類擴充(class extension),看起來就像一個沒有名字的範疇。可以帶變數成員,且必須實現。
如果根據良好的代碼劃分,我想放在標頭檔.h的往往是對外公開的方法和屬性,而自己內部使用的私人方法就不需要放在標頭檔裡。在像C#這種沒有標頭檔的語言裡,我們仍然會按照約定先寫Public 方法,然後用#region 把它標記起來,方便別人查看。
假設我們有一個CarStock類,在CarStock.m中有如下代碼,會遇到這麼一個問題,如果init中調用的方法聲明如果在init的位置下面,那麼編譯器就會報錯,因為它是從上往下解析的.
- (id)init { if ((self = [super init])) { [self refreshStock]; } return self; }- (void)refreshStock { // ... }
這時有幾種解決方式
1.refreshStock方法上移
2.refreshStock聲明在標頭檔CarStock.h裡
但這些都不符合我想要寫一個私人方法的想法。ObjectC中方法是不帶範圍聲明的,也就是你無法像下面這樣聲明
private - (void)refreshStoc
所以可以在CarStock.m中,通過一個無命名的範疇,來解決這個問題
@interface CarStock ()- (void)refreshStock; @end
六、@dynamic
告訴編譯器我們這個屬性會在其他地方產生相應的get和set方法(在ObjectC中是通過@synthesize(合成)關鍵字來合成屬性)
Super class中對這個屬性合成:
@property (nonatomic, retain) NSButton *someButton;...@synthesize someButton;
Subclass如果不對*someButton合成或者自己提供get,set方法,那麼編譯器就會提示有問題:
@property (nonatomic, retain) IBOutlet NSButton *someButton;...@dynamic someButton;
我們想用Super class中的合成方法來合成Subclass,我們這時候就可以通過@dynamic關鍵字來關閉編譯器的警告。
這在通常代碼中非常少用,往往在使用Core data(ObjectC中的ORM架構)中使用。我們繼承了NSManagedObject,希望相應的訪問器方法由它來完成。
七、@class
在標頭檔.h裡我們往往不需要知道所引用類的具體方法資訊,而只是需要知道有這麼一個類,便於我們聲明變數類型,我們這時候就可以用@class關鍵字來代替#import,這樣告訴編譯器我們肯定會有這麼一個類,你就不用檢查了。這樣在一些大型項目中可以加快編譯速度。也可以解決迴圈import可能帶來的問題。
當具體使用的使用我們才在.m裡import所需要的標頭檔。
八、nil
nil 類似於 null,區別在於給nil發送訊息並不會產生錯誤,它的預設實現是忽視這條資訊。在C#和其他語言中則會產生類似NullReference的錯誤。
原因是在底層C的實現中,nil不帶self指標,發送訊息調用selector的時候如果檢測self為空白,則直接返回。
我們通常在使用NSError的時候,通過檢測NSError是否為nil來判斷方法調用是否出錯
NSError *error = nil; self.responseString = [NSString stringWithContentsOfFile:zipFileName_ encoding:NSUTF8StringEncoding error:&error]; if (error)
還有就是在dealloc方法中把不用的變數設定為nil,可以防止在release後指標指向無效記憶體而導致錯誤。
Xcode 4.4版本(LLVM4編譯器)後編譯器新增的一些文法糖
Xcode是免費的,所以咱們可以升級到新版本來享受一下新編譯器的一些好處
1.更加多的字面量支援
原來我們在ObjectC中建立一個NSString類型的對象時,可以
NSString *myName=@"langxue";
現在我們建立其他對象也可以字面量文法了
NSNumber *myNumber =@3;NSNumber *yesValue =@YES;NSArray *array =@[@1,@2,@3,@4];NSDictionary *dictionary =@[@"key1":@"value1" ,@"key2":@"value2"]
2.下標訪問
我們可以通過下標來直接存取我們需要的元素,這在原來是不可以的。
int element3 = array[3]; int elementAt3 = dictionary [@"key3"]
3.自動合成@property
我們聲明了property以後
@property (strong, nonatomic) MyController *myController;@property (nonatomic, copy) void (^completionHandler)();
原來的情況下訪問器是通過@synthesize關鍵字合成的。
@synthesize myController = _myController;
@synthesize completionHandler = _completionHandler;
有了新的LLVM編譯器,就可以省略這些用於合成執行個體變數的代碼了。LLVM 4編譯器會自動合成這些執行個體變數。當然如果明確地寫了get,set方法,LLVM 4就不再自動產生@synthesize指令了。需要記住的是,自動合成的執行個體變數會按照ObjectC中的慣例以底線_開頭。
最後,如果你覺得這篇文章有協助,請點右下角一下推薦,這是我繼續下去的動力,謝謝!