和我一起來學iOS(一)ObjectC的語法


前言:
為什么叫和我一起學呢?因為從開始寫這系列博客中我就定了一個方向,不從最基本的講,而是挑一些如果從其他語言(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中的慣例以下划線_開頭。

 


 最后,如果你覺得這篇文章有幫助,請點右下角一下推薦,這是我繼續下去的動力,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM