Cocoa是什么,Cocoa是使用OC語言編寫的工具包,里面有大量的類庫、結構體,其實就相當于java中的標準API、C++中的標準庫。OC中沒有命名空間的概念,所以使用加前綴來防止命名沖突,因此你會看到大量的以NS 為前綴的類名、結構體、枚舉等。
Cocoa框架由Foundation Kit、App Kit兩部分組成,前者是基礎工具庫,后者主要是UI庫、高級對象等。
static 標識的類變量定義在接口的外面,類變量只能本類訪問,除非提供類方法給外部訪問這個類變量
@語法是OC特有的一種語法,C是沒有的。
OC中只有類的成員變量才有訪問權限控制,@public、@protected、@private,默認是@protected,類變量、類方法、成員方法是沒有訪問修飾符的,所有的方法都是public的,所有的類變量都是私有的。
OC中的類方法只能類調用,如果使用對象調用就會報錯,而java這只是一個警告而已。
OC中定義類的@interface和java中的interface不是一回事,OC的@protocol和java中的interface才是一碼事。@interface跟java和 C++ class關鍵字差不多,在OC和C++中,都習慣將類定義寫在頭文件里,而java卻根本沒有頭文件的概念,至于為什么有這樣的差別,想是為了OC和 C++編譯器方便,大多數時候編譯器只需要知道頭文件類的定義就知道這個類有哪些屬性和方法了。
get為前綴的方法在OC中有特殊的意義,所以盡量使用成員變量名稱來作為getter方法的函數名。
OC是動態語言,所以即便是@interface中沒有定義的方法在.m文件中實現了也是OK的,一般我們都是給別人提供頭文件,這種情況就相當于把方法變相的私有化了,對于我這種喜歡在寫代碼過程中隨時加方法但又不想動頭文件的人來說還是比較爽的。
在OC中所有對象的存取都是用指針,C++中還有引用,OC中沒有,我寧愿你強迫我使用一種方法,也不愿你提供給我兩種方法我卻不知道使用哪一種方法的好。
OC的方法定義和調用算是一大“特色”:
方法的定義:-(返回類型) 方法名:(參數1類型)參數1變量 參數2標簽:(參數2類型)參數2變量…
[類或者實例的指針方法名: 參數1 標簽2: 參數2… …],
為了好看,第一個參數一般不加標簽名,當然,標簽名都可以隱藏的,但不建議這樣做, 因為當你接手了一個離職的人程序,其中的JAVA 程序調用了一個有五個甚至更多的參數的方法,但是你手里沒有這個方法的API,那么你很難猜得出來這五個參數到底都干什么用的,但是Objective- C調用的時候,每個參數前面都必須有方法的標簽名,這樣你便能很容易的從標簽名看出這個參數是什么意思。
調用類方法:
[Fraction t];
[[Fraction class] t];
Class clazz=[Fraction class];[clazz t];
class 來自于NSObject,相當于JAVA 中的getClass()方法,也就是獲取這個類的Class 對象,
clazz 前面沒有,這是因為Class 已經是一個指針。另外這種嵌套調用的方式,也要習慣,這就和JAVA 中的A.b().c()沒有什么區別。
獲取Class 有如下幾種方法:
[類或者對象 class]
[類或者對象 superclasss]
NSClassFromString(類名的字符串形式)
你也可以通過如下的函數把Class 轉換為字符串形式:
NSStringFromClass(Class)
OC中自定義類最好顯示繼承NSObject,可以獲得alloc、init、release等方法。
OC中使用nil表示null,但跟java中的null還是有區別的,java中調用null的方法,會報臭名昭著的空指針異常,而OC不會,所以在OC你不必再為空指針而煩惱。
Cocoa中提供的很多函數和java中的api有很大的不一樣,java是純面向對象的,所有的方法都必須在類中定義,但OC是兼容C語言的,所以C中的面向過程思想在OC中也是行得通的,因此Cocoa中很多東西都不是對象,而是C語言的函數、結構體,比如NSLog()。
對象的初始化
前面看到實例化對象最多的方法是:Fraction frac=[[Fraction alloc] init];
這跟java不一樣,java對象創建只需要new一下,同時調用構造方法(實際上虛擬機層面分為兩個步驟:new對象,執行方法(包含構造方法)),但是OC中分為兩步:分配內存(同時給變量賦初值)、初始化。
alloc 是從NSObject 繼承而來的類方法,用于給對象分配存儲空間,所有的成員變量在此時對確定了自己的內存位置,并被賦初值,整數類型為0,浮點數為0.0,BOOL 為NO,對象類型為nil,alloc 方法返回對象的指針。
init這個方法是從NSObject繼承而來的,你可以覆蓋它,當然init不是構造方法,只是一個普通方法而已,你甚至可以換成其他方法,只不過我們習慣在方法名前綴加上init。
還記得java中的構造方法沒有返回值吧,所以為了達到java這種效果:Test t = new Test(2);OC中需要你手動在普通方法(相當于構造方法)中return self。
NSObject中的description方法相當于java中Object的toString方法,用于獲取對象的字符串表示。唯一不同的是OC中需要使用格式化字符串%@,才會觸發description被調用:
Fractionfrac=[[Fraction alloc] init];
NSLog(@”%@”,frac);
Objective- C的異常都繼承自NSException,當然NSException 本身也繼承自NSObject。總體來說,OC中的異常體系和java中的異常體系差不多,唯一的差別就是java中有業務異常,意思就是說你必須捕獲 他,如果不捕獲就會出現編譯錯誤,OC中的異常基本上都屬于java中的運行時異常,如果你沒有捕獲,發生了異常程序就會崩潰,在一些循環批任務中尤其要注意。
OC中還有一個特色那就是他的弱類型id,他可以表示任何對象,實際上應該是利用了指針地址的通用性,由于OC中操作對象都必須是指針,而指針本身又是一串地址,所以通過指針可以指向一切不同的對象。
繼承(感覺和java差不多啊)
反射(這個和異常一樣,感覺跟java也很像)
選擇器(其實就是C中的函數指針,以及java中的Method類)
類別(Category),擴充類的功能,但不可以聲明新的成員變量。
類別的應用比較廣泛,譬如:第三方的Objective-C庫RegexKitLite 就是對NSString、NSMutableString做的類別,為Objective-C的字符類型增加了正則表達式的功能。
字符串
-(BOOL) isEqualToString: (NSString) s
比較兩個字符串是否相等,與JAVA 一致的地方是==比較指針,比較對象是否相同要用到equal 方法。
-(NSMutableString) appendString: (NSString) s
這與JAVA 的StringBuffer 的append 沒什么區別。
數組
Cocoa 使用NSArray 表示數組,但是它不能存儲基本數據類型、enum、struct、nil,只能存儲Objective-C 的對象。
NSArrayarray=[NSArray arrayWithObjects: @”One”, @”Two”, @”Three”, nil];
從這個類方法arrayWithObjects 的定義可以看出,它使用nil 表示數組元素結束,這也是nil 不能存儲在NSArray 中的原因。
NSMutableArray 為長度可變的數組,相當于JAVA 中的List:
NSMutableArray?mArray=[NSMutableArray arrayWithCapacity: 10];
[mArray addObject: @”Apple”];//添加數組元素
NSEnumeratore = [mArray objectEnumerator];//獲取數組的迭代器,相當于JAVA 中的Iterator,reserveObjectEnumerator 用于獲取反轉之后的數組迭代器。與JAVA 一致的地方是你在使用迭代器時,不能對數組進行添加、刪除操作。
字典(哈希表)
NSDictionary?用于存儲key-value 的數據結構,與JAVA 中的Map 類似。
NSDictionary dic=[NSDictionary dictionaryWithObjectsAndKeys: @”Apple”, @”A”, @”Google”,@”G”, nil];//dictionaryWithObjectAndKeys 后的可變參數,每兩個為一個value-key,以nil 表示結束。
NSLog(@”%@”,[dic objectForKey: @”A”]);//按照指定的key 查詢value
同樣的有NSMutableDictionary?表示長度可變的字典。
NSMutableDictionarymDic=[NSMutableDictionary dictionaryWithCapacity: 10];
[mDic setObject: @”Apple” forKey: @”A”];//添加value-key 對
哈希Set
NSSet 表示以hash 方式計算存儲位置的集合,與JAVA 中的HashSet 是一致的。
在NSSet 中的每個對象都有一個唯一的hash 值,重復的對象將只能保留一個。因此,這引出了Objective-C中的對象比較問題,這需要你實現從NSObject 繼承而來的如下兩個方法:
- (BOOL)?isEqual: (id) anObject;
- (NSUInteger)?hash;
這與JAVA 的對象比較沒有什么區別,兩個相等的對象必須有相同的hashCode,所以這兩個方法必須同時實現。
同樣的,NSMutableSet?表示長度可變的哈希Set。
封裝類(相當于java的包裝器)
前面的幾個容器類的對象都不能存放基本數據結構、enum、struct、nil,怎么辦呢?
在JAVA中我們知道所有的基本數據類型都有對應的封裝類,例如:int—-Integer、boolean—-Boolean,使用封裝類可以把基本數據類型包裝為對象,而封裝類本身又有方法可以返回原來的基本數據類型。Cocoa 使用NSValue?作為封裝類。
NSRect rect=NSMakeRect(1,2,30,50);//這個是在前面提到過的一個表示矩形的結構體
NSValue v=[NSValue valueWithBytes: &rect objCType: @encode(NSRect)];
//java與oc之間的比較 valueWithBytes 要求傳入包裝數據的地址,也就是變量中存儲的數據的首地址,我們依然使用C 語言的&作為地址運算符,計算指針存儲的首地址。
//objCType 要求傳入用于描述數據的類型、大小的字符串,使用@encode 指令包裝數據所屬的類型。
對于基本數據類型,你可以使用簡便的NSNumber?來封裝,它是NSValue 的子類。
如果你想存儲空值到集合類,可以使用NSNull,它使用NSNulln =[NSNull null];的方式創建。
日期類型
Cocoa 中使用NSDate 類型表示日期。
數據緩沖區(其實就是java中的字節數組)
Cocoa 中使用NSData 類型來實現緩沖區,用于存儲二進制的數據類型,譬如:從網絡下載回來的文件等。
NSData?是長度不可變的數據緩沖區,還有一個NSMutableData?用來存儲長度可變的數據緩沖區。
寫入和讀取屬性(簡直就是java序列化和反序列化的翻版)
在iPhone 的.ipa 文件中,經常可以看到.plist 文件,它保存了程序的相關屬性,叫做屬性列表。其實它就是NSArray、NSDictionary、NSString、NSData 持久化之后的文件。
這幾個類型都有一個成員方法?writeToFile: (NSString) file atomically: BOOL 用于將自己寫入到一個文件。atomically 為YES 表示文件先存儲到臨時文件區,如果文件保存成功,再替換掉原始文件,這樣的好處是可以防止在保存文件過程中出錯,但是僅適用于小文件,因為你相當于是在臨 時文件存儲區也放了一份,再加上原始文件,就是兩份文件,如果文件比較大,將會占用較多的用戶的磁盤空間。
如果你要持久化的類型不是上述的數組、字典、緩沖區,那該怎么辦呢?Objective-C 中也有和JAVA 一樣的序列化、反序列化支持,使用NSCoding?協議。NSCoding 協議定義了如下兩個方法:
-(void)?encodeWithCoder: (NSCoder) coder;
-(id) initWithCoder: (NSCoder) decoder;
第一個方法相當于編碼對象,第二個方法是解碼回對象,也就相當于給類型定義了一個新的init 方法而已。
對象的復制
對象的復制就相當于JAVA 中的clone()方法,也就是對象的深度復制,所謂深度復制就是重新分配一個存儲空間,并將原對象的內容都復制過來,從這些描述可以看出,復制也會分配空間,那就是你要對復制出來的對象release,就是前面所說的alloc、new、copy 操作創建的對象,要手工release。
Objective-C 中的一個對象是否可以被復制,要看它的類型是否遵循NSCopying 協議,這個協議中有個復制方法-(id) copyWithZone: (NSZone) zone 需要我們去實現。
多線程
Objective-C 的多線程編程與JAVA 語言極其類似分為原始的線程操作、線程池兩種,后者其實就是使用隊列等機制對前者的封裝。
JAVA 也一樣,原始的線程操作使用Thread 類,線程池的操作使用java.util.concurrent.中的類庫。
Objective-C 中NSThread?類型表示線程,NSCondition?用于執行同步操作,在功能和用法上相當于JAVA 中的java.util.concurrent.*包中的Lock 對象。
另外,Objective-C 也支持@synchronized 指令做代碼同步,寫法也和JAVA 中的很相似。
另外比較有趣的是,如果你想更新UI 上的某一個部件,就需要在發起的新線程里調用UI 所在的主線程上的一個方法,新線程不能直接訪問主線程的方法,需要在run 方法中使用如下的方法:
- (void)?performSelectorOnMainThread: (SEL) aSelector withObject: (id) arg waitUntilDone: (BOOL) wait
如果對java的swing熟悉的話,應該知道在UI界,大多數更新界面的都必須在一個單線程中執行,因為如果更新界面是多線程的話會很難處理的,java swing處理的方法是更新界面的代碼放在專門更新UI的線程下執行,這里IOS也是這么一個思想。
Objective-C 使用?NSOperation、NSOperationQueue 兩個類型實現線程池的操作,NSOpertion就好比JAVA 中的Runnable, 其中的main(名字有點兒奇怪)方法也就是你要執行的操作,NSOperationQueue 是一個線程池隊列,你只需要把NSOperation 添加到NSOperationQueue,隊列就會retain 你的NSOperation,然后執行其中的操作,直到執行完畢。
隊列中會有多個操作,隊列會按照你添加的順序逐個執行,也就說隊列中的任務是串行的。
跟java一樣,如果線程池可以滿足你的業務需求,盡量使用線程池,而不是原始的NSThread,因為使用線程池屏蔽了許多線程自身需要處理的問題,代碼也更加簡潔。
KVC 與KVO(這個話題真高級)
KVC 是 NSKeyValueCoding 的縮寫,它是Foundation Kit 中的一個NSObject 的Category,作用可以類比JAVA 中的反射機制,就是動態訪問一個對象中的屬性。
KVC 在解析key 的字符串的時候,是會比正常調用setter、getter 要慢的,而且編譯器無法在編譯器對你的方法調用做出檢查(因為你的屬性名都是字符串,只有運行時才會知道你有沒有寫錯),出錯的幾率也會提高,所以請不要 隨意使用KVC,而省去setter、getter 方法。KVC 一般用于動態綁定,也就是運行時才能確定誰調用哪個方法,編譯期并不確定。
KVO就是 NSKeyValueObserving的縮寫,它也是Foundation Kit中的一個NSObject的Category,KVO 基于KVC 實現,基于觀察者設計模式(Observer Pattern)實現的一種通知機制,可以類比JAVA 中的JMS,通過訂閱的方式,實現了兩個對象之間的解耦,但又可以讓他們相互調用。