Cocoa是什么路捧,Cocoa是使用OC語言編寫的工具包,里面有大量的類庫传黄、結(jié)構(gòu)體杰扫,其實就相當于java中的標準API、C++中的標準庫膘掰。OC中沒有命名空間的概念章姓,所以使用加前綴來防止命名沖突,因此你會看到大量的以NS 為前綴的類名识埋、結(jié)構(gòu)體凡伊、枚舉等。
Cocoa框架由Foundation Kit惭聂、App Kit兩部分組成窗声,前者是基礎工具庫,后者主要是UI庫辜纲、高級對象等笨觅。
static 標識的類變量定義在接口的外面,類變量只能本類訪問耕腾,除非提供類方法給外部訪問這個類變量
@語法是OC特有的一種語法见剩,C是沒有的。
OC中只有類的成員變量才有訪問權(quán)限控制扫俺,@public苍苞、@protected、@private狼纬,默認是@protected羹呵,類變量、類方法疗琉、成員方法是沒有訪問修飾符的冈欢,所有的方法都是public的,所有的類變量都是私有的盈简。
OC中的類方法只能類調(diào)用凑耻,如果使用對象調(diào)用就會報錯,而java這只是一個警告而已柠贤。
OC中定義類的@interface和java中的interface不是一回事香浩,OC的@protocol和java中的interface才是一碼事。@interface跟java和 C++ class關鍵字差不多臼勉,在OC和C++中邻吭,都習慣將類定義寫在頭文件里,而java卻根本沒有頭文件的概念宴霸,至于為什么有這樣的差別镜盯,想是為了OC和 C++編譯器方便岸裙,大多數(shù)時候編譯器只需要知道頭文件類的定義就知道這個類有哪些屬性和方法了。
get為前綴的方法在OC中有特殊的意義速缆,所以盡量使用成員變量名稱來作為getter方法的函數(shù)名。
OC是動態(tài)語言恩闻,所以即便是@interface中沒有定義的方法在.m文件中實現(xiàn)了也是OK的艺糜,一般我們都是給別人提供頭文件,這種情況就相當于把方法變相的私有化了幢尚,對于我這種喜歡在寫代碼過程中隨時加方法但又不想動頭文件的人來說還是比較爽的破停。
在OC中所有對象的存取都是用指針,C++中還有引用尉剩,OC中沒有真慢,我寧愿你強迫我使用一種方法,也不愿你提供給我兩種方法我卻不知道使用哪一種方法的好理茎。
OC的方法定義和調(diào)用算是一大“特色”:
方法的定義:-(返回類型) 方法名:(參數(shù)1類型)參數(shù)1變量 參數(shù)2標簽:(參數(shù)2類型)參數(shù)2變量…
[類或者實例的指針方法名: 參數(shù)1 標簽2: 參數(shù)2… …]黑界,
為了好看,第一個參數(shù)一般不加標簽名皂林,當然朗鸠,標簽名都可以隱藏的,但不建議這樣做础倍, 因為當你接手了一個離職的人程序烛占,其中的JAVA 程序調(diào)用了一個有五個甚至更多的參數(shù)的方法,但是你手里沒有這個方法的API沟启,那么你很難猜得出來這五個參數(shù)到底都干什么用的忆家,但是Objective- C調(diào)用的時候,每個參數(shù)前面都必須有方法的標簽名德迹,這樣你便能很容易的從標簽名看出這個參數(shù)是什么意思芽卿。
調(diào)用類方法:
[Fraction t];
[[Fraction class] t];
Class clazz=[Fraction class];[clazz t];
class 來自于NSObject,相當于JAVA 中的getClass()方法浦辨,也就是獲取這個類的Class 對象蹬竖,
clazz 前面沒有,這是因為Class 已經(jīng)是一個指針流酬。另外這種嵌套調(diào)用的方式币厕,也要習慣,這就和JAVA 中的A.b().c()沒有什么區(qū)別芽腾。
獲取Class 有如下幾種方法:
[類或者對象 class]
[類或者對象 superclasss]
NSClassFromString(類名的字符串形式)
你也可以通過如下的函數(shù)把Class 轉(zhuǎn)換為字符串形式:
NSStringFromClass(Class)
OC中自定義類最好顯示繼承NSObject旦装,可以獲得alloc、init摊滔、release等方法阴绢。
OC中使用nil表示null店乐,但跟java中的null還是有區(qū)別的,java中調(diào)用null的方法呻袭,會報臭名昭著的空指針異常眨八,而OC不會,所以在OC你不必再為空指針而煩惱左电。
Cocoa中提供的很多函數(shù)和java中的api有很大的不一樣廉侧,java是純面向?qū)ο蟮模械姆椒ǘ急仨氃陬愔卸x篓足,但OC是兼容C語言的段誊,所以C中的面向過程思想在OC中也是行得通的,因此Cocoa中很多東西都不是對象栈拖,而是C語言的函數(shù)连舍、結(jié)構(gòu)體,比如NSLog()涩哟。
對象的初始化
前面看到實例化對象最多的方法是:Fraction frac=[[Fraction alloc] init];
這跟java不一樣索赏,java對象創(chuàng)建只需要new一下,同時調(diào)用構(gòu)造方法(實際上虛擬機層面分為兩個步驟:new對象染簇,執(zhí)行方法(包含構(gòu)造方法))参滴,但是OC中分為兩步:分配內(nèi)存(同時給變量賦初值)、初始化锻弓。
alloc 是從NSObject 繼承而來的類方法砾赔,用于給對象分配存儲空間,所有的成員變量在此時對確定了自己的內(nèi)存位置青灼,并被賦初值暴心,整數(shù)類型為0,浮點數(shù)為0.0杂拨,BOOL 為NO专普,對象類型為nil,alloc 方法返回對象的指針弹沽。
init這個方法是從NSObject繼承而來的檀夹,你可以覆蓋它,當然init不是構(gòu)造方法策橘,只是一個普通方法而已炸渡,你甚至可以換成其他方法,只不過我們習慣在方法名前綴加上init丽已。
還記得java中的構(gòu)造方法沒有返回值吧蚌堵,所以為了達到java這種效果:Test t = new Test(2);OC中需要你手動在普通方法(相當于構(gòu)造方法)中return self。
NSObject中的description方法相當于java中Object的toString方法,用于獲取對象的字符串表示吼畏。唯一不同的是OC中需要使用格式化字符串%@督赤,才會觸發(fā)description被調(diào)用:
Fractionfrac=[[Fraction alloc] init];
NSLog(@”%@”,frac);
Objective- C的異常都繼承自NSException,當然NSException 本身也繼承自NSObject泻蚊《闵啵總體來說,OC中的異常體系和java中的異常體系差不多性雄,唯一的差別就是java中有業(yè)務異常孽糖,意思就是說你必須捕獲 他,如果不捕獲就會出現(xiàn)編譯錯誤毅贮,OC中的異常基本上都屬于java中的運行時異常尘奏,如果你沒有捕獲滩褥,發(fā)生了異常程序就會崩潰,在一些循環(huán)批任務中尤其要注意炫加。
OC中還有一個特色那就是他的弱類型id瑰煎,他可以表示任何對象,實際上應該是利用了指針地址的通用性俗孝,由于OC中操作對象都必須是指針酒甸,而指針本身又是一串地址,所以通過指針可以指向一切不同的對象赋铝。
繼承(感覺和java差不多安迩凇)
反射(這個和異常一樣,感覺跟java也很像)
選擇器(其實就是C中的函數(shù)指針革骨,以及java中的Method類)
類別(Category)农尖,擴充類的功能,但不可以聲明新的成員變量良哲。
類別的應用比較廣泛盛卡,譬如:第三方的Objective-C庫RegexKitLite 就是對NSString、NSMutableString做的類別筑凫,為Objective-C的字符類型增加了正則表達式的功能滑沧。
字符串
-(BOOL) isEqualToString: (NSString) s
比較兩個字符串是否相等,與JAVA 一致的地方是==比較指針巍实,比較對象是否相同要用到equal 方法滓技。
-(NSMutableString) appendString: (NSString) s
這與JAVA 的StringBuffer 的append 沒什么區(qū)別。
數(shù)組
Cocoa 使用NSArray 表示數(shù)組蔫浆,但是它不能存儲基本數(shù)據(jù)類型殖属、enum、struct瓦盛、nil洗显,只能存儲Objective-C 的對象外潜。
NSArrayarray=[NSArray arrayWithObjects: @”O(jiān)ne”, @”Two”, @”Three”, nil];
從這個類方法arrayWithObjects 的定義可以看出,它使用nil 表示數(shù)組元素結(jié)束挠唆,這也是nil 不能存儲在NSArray 中的原因处窥。
NSMutableArray 為長度可變的數(shù)組,相當于JAVA 中的List:
NSMutableArray?mArray=[NSMutableArray arrayWithCapacity: 10];
[mArray addObject: @”Apple”];//添加數(shù)組元素
NSEnumeratore = [mArray objectEnumerator];//獲取數(shù)組的迭代器玄组,相當于JAVA 中的Iterator滔驾,reserveObjectEnumerator 用于獲取反轉(zhuǎn)之后的數(shù)組迭代器。與JAVA 一致的地方是你在使用迭代器時俄讹,不能對數(shù)組進行添加哆致、刪除操作。
字典(哈希表)
NSDictionary?用于存儲key-value 的數(shù)據(jù)結(jié)構(gòu)患膛,與JAVA 中的Map 類似摊阀。
NSDictionary dic=[NSDictionary dictionaryWithObjectsAndKeys: @”Apple”, @”A”, @”Google”,@”G”, nil];//dictionaryWithObjectAndKeys 后的可變參數(shù),每兩個為一個value-key踪蹬,以nil 表示結(jié)束胞此。
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中的對象比較問題,這需要你實現(xiàn)從NSObject 繼承而來的如下兩個方法:
- (BOOL)?isEqual: (id) anObject;
- (NSUInteger)?hash;
這與JAVA 的對象比較沒有什么區(qū)別愿卸,兩個相等的對象必須有相同的hashCode灵临,所以這兩個方法必須同時實現(xiàn)。
同樣的趴荸,NSMutableSet?表示長度可變的哈希Set儒溉。
封裝類(相當于java的包裝器)
前面的幾個容器類的對象都不能存放基本數(shù)據(jù)結(jié)構(gòu)、enum发钝、struct顿涣、nil,怎么辦呢酝豪?
在JAVA中我們知道所有的基本數(shù)據(jù)類型都有對應的封裝類涛碑,例如:int—-Integer、boolean—-Boolean孵淘,使用封裝類可以把基本數(shù)據(jù)類型包裝為對象蒲障,而封裝類本身又有方法可以返回原來的基本數(shù)據(jù)類型。Cocoa 使用NSValue?作為封裝類。
NSRect rect=NSMakeRect(1,2,30,50);//這個是在前面提到過的一個表示矩形的結(jié)構(gòu)體
NSValue v=[NSValue valueWithBytes: &rect objCType: @encode(NSRect)];
//java與oc之間的比較 valueWithBytes 要求傳入包裝數(shù)據(jù)的地址揉阎,也就是變量中存儲的數(shù)據(jù)的首地址庄撮,我們依然使用C 語言的&作為地址運算符,計算指針存儲的首地址毙籽。
//objCType 要求傳入用于描述數(shù)據(jù)的類型洞斯、大小的字符串,使用@encode 指令包裝數(shù)據(jù)所屬的類型坑赡。
對于基本數(shù)據(jù)類型烙如,你可以使用簡便的NSNumber?來封裝,它是NSValue 的子類毅否。
如果你想存儲空值到集合類亚铁,可以使用NSNull,它使用NSNulln =[NSNull null];的方式創(chuàng)建螟加。
日期類型
Cocoa 中使用NSDate 類型表示日期刀闷。
數(shù)據(jù)緩沖區(qū)(其實就是java中的字節(jié)數(shù)組)
Cocoa 中使用NSData 類型來實現(xiàn)緩沖區(qū),用于存儲二進制的數(shù)據(jù)類型仰迁,譬如:從網(wǎng)絡下載回來的文件等。
NSData?是長度不可變的數(shù)據(jù)緩沖區(qū)顽分,還有一個NSMutableData?用來存儲長度可變的數(shù)據(jù)緩沖區(qū)徐许。
寫入和讀取屬性(簡直就是java序列化和反序列化的翻版)
在iPhone 的.ipa 文件中,經(jīng)匙湔海可以看到.plist 文件雌隅,它保存了程序的相關屬性,叫做屬性列表缸沃。其實它就是NSArray恰起、NSDictionary、NSString趾牧、NSData 持久化之后的文件检盼。
這幾個類型都有一個成員方法?writeToFile: (NSString) file atomically: BOOL 用于將自己寫入到一個文件。atomically 為YES 表示文件先存儲到臨時文件區(qū)翘单,如果文件保存成功吨枉,再替換掉原始文件,這樣的好處是可以防止在保存文件過程中出錯哄芜,但是僅適用于小文件貌亭,因為你相當于是在臨 時文件存儲區(qū)也放了一份,再加上原始文件认臊,就是兩份文件圃庭,如果文件比較大,將會占用較多的用戶的磁盤空間。
如果你要持久化的類型不是上述的數(shù)組剧腻、字典拘央、緩沖區(qū),那該怎么辦呢恕酸?Objective-C 中也有和JAVA 一樣的序列化堪滨、反序列化支持,使用NSCoding?協(xié)議蕊温。NSCoding 協(xié)議定義了如下兩個方法:
-(void)?encodeWithCoder: (NSCoder) coder;
-(id) initWithCoder: (NSCoder) decoder;
第一個方法相當于編碼對象袱箱,第二個方法是解碼回對象,也就相當于給類型定義了一個新的init 方法而已义矛。
對象的復制
對象的復制就相當于JAVA 中的clone()方法发笔,也就是對象的深度復制,所謂深度復制就是重新分配一個存儲空間凉翻,并將原對象的內(nèi)容都復制過來了讨,從這些描述可以看出,復制也會分配空間制轰,那就是你要對復制出來的對象release前计,就是前面所說的alloc、new垃杖、copy 操作創(chuàng)建的對象男杈,要手工release。
Objective-C 中的一個對象是否可以被復制调俘,要看它的類型是否遵循NSCopying 協(xié)議伶棒,這個協(xié)議中有個復制方法-(id) copyWithZone: (NSZone) zone 需要我們?nèi)崿F(xiàn)。
多線程
Objective-C 的多線程編程與JAVA 語言極其類似分為原始的線程操作彩库、線程池兩種肤无,后者其實就是使用隊列等機制對前者的封裝。
JAVA 也一樣骇钦,原始的線程操作使用Thread 類宛渐,線程池的操作使用java.util.concurrent.中的類庫。
Objective-C 中NSThread?類型表示線程眯搭,NSCondition?用于執(zhí)行同步操作皇忿,在功能和用法上相當于JAVA 中的java.util.concurrent.*包中的Lock 對象。
另外坦仍,Objective-C 也支持@synchronized 指令做代碼同步鳍烁,寫法也和JAVA 中的很相似。
另外比較有趣的是繁扎,如果你想更新UI 上的某一個部件幔荒,就需要在發(fā)起的新線程里調(diào)用UI 所在的主線程上的一個方法糊闽,新線程不能直接訪問主線程的方法,需要在run 方法中使用如下的方法:
- (void)?performSelectorOnMainThread: (SEL) aSelector withObject: (id) arg waitUntilDone: (BOOL) wait
如果對java的swing熟悉的話爹梁,應該知道在UI界右犹,大多數(shù)更新界面的都必須在一個單線程中執(zhí)行,因為如果更新界面是多線程的話會很難處理的姚垃,java swing處理的方法是更新界面的代碼放在專門更新UI的線程下執(zhí)行念链,這里IOS也是這么一個思想。
Objective-C 使用?NSOperation积糯、NSOperationQueue 兩個類型實現(xiàn)線程池的操作掂墓,NSOpertion就好比JAVA 中的Runnable, 其中的main(名字有點兒奇怪)方法也就是你要執(zhí)行的操作看成,NSOperationQueue 是一個線程池隊列君编,你只需要把NSOperation 添加到NSOperationQueue,隊列就會retain 你的NSOperation川慌,然后執(zhí)行其中的操作吃嘿,直到執(zhí)行完畢。
隊列中會有多個操作梦重,隊列會按照你添加的順序逐個執(zhí)行兑燥,也就說隊列中的任務是串行的。
跟java一樣琴拧,如果線程池可以滿足你的業(yè)務需求贪嫂,盡量使用線程池,而不是原始的NSThread艾蓝,因為使用線程池屏蔽了許多線程自身需要處理的問題,代碼也更加簡潔斗塘。
KVC 與KVO(這個話題真高級)
KVC 是 NSKeyValueCoding 的縮寫赢织,它是Foundation Kit 中的一個NSObject 的Category,作用可以類比JAVA 中的反射機制馍盟,就是動態(tài)訪問一個對象中的屬性于置。
KVC 在解析key 的字符串的時候,是會比正常調(diào)用setter贞岭、getter 要慢的,而且編譯器無法在編譯器對你的方法調(diào)用做出檢查(因為你的屬性名都是字符串,只有運行時才會知道你有沒有寫錯)鳖轰,出錯的幾率也會提高洒宝,所以請不要 隨意使用KVC,而省去setter芯侥、getter 方法泊交。KVC 一般用于動態(tài)綁定乳讥,也就是運行時才能確定誰調(diào)用哪個方法,編譯期并不確定廓俭。
KVO就是 NSKeyValueObserving的縮寫云石,它也是Foundation Kit中的一個NSObject的Category,KVO 基于KVC 實現(xiàn)研乒,基于觀察者設計模式(Observer Pattern)實現(xiàn)的一種通知機制汹忠,可以類比JAVA 中的JMS,通過訂閱的方式雹熬,實現(xiàn)了兩個對象之間的解耦宽菜,但又可以讓他們相互調(diào)用。