前言:
為什么叫和我一起學呢?因為從開始寫這系列博客中我就定了一個方向,不從最基本的講,而是挑一些如果從其他語言(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中的慣例以下划線_開頭。
最后,如果你覺得這篇文章有幫助,請點右下角一下推薦,這是我繼續下去的動力,謝謝!