【IOS】java 與oc之間的比較

Leo.cheng發表於2014-03-08
  1. Cocoa是什麼Cocoa是使用OC語言編寫的工具包,裡面有大量的類庫、結構體,其實就相當於java中的標準API、C++中的標準庫。OC中沒有名稱空間的概念,所以使用加字首來防止命名衝突,因此你會看到大量的以NS 為字首的類名、結構體、列舉等。
  2. Cocoa框架由Foundation Kit、App Kit兩部分組成,前者是基礎工具庫,後者主要是UI庫、高階物件等。
  3. static 標識的類變數定義在介面的外面,類變數只能本類訪問,除非提供類方法給外部訪問這個類變數
  4. @語法是OC特有的一種語法,C是沒有的
  5. OC中只有類的成員變數才有訪問許可權控制,@public、@protected、@private,預設是@protected,類變數、類方法、成員方法是沒有訪問修飾符的,所有的方法都是public的,所有的類變數都是私有的。
  6. OC中的類方法只能類呼叫,如果使用物件呼叫就會報錯,而java這只是一個警告而已。
  7. OC中定義類的@interface和java中的interface不是一回事,OC的@protocol和java中的interface才是一碼事。@interface跟java和C++ class關鍵字差不多,在OC和C++中,都習慣將類定義寫在標頭檔案裡,而java卻根本沒有標頭檔案的概念,至於為什麼有這樣的差別,想是為了OC和C++編譯器方便,大多數時候編譯器只需要知道標頭檔案類的定義就知道這個類有哪些屬性和方法了。
  8. get為字首的方法在OC中有特殊的意義,所以儘量使用成員變數名稱來作為getter方法的函式名。
  9. OC是動態語言,所以即便是@interface中沒有定義的方法在.m檔案中實現了也是OK的,一般我們都是給別人提供標頭檔案,這種情況就相當於把方法變相的私有化了,對於我這種喜歡在寫程式碼過程中隨時加方法但又不想動標頭檔案的人來說還是比較爽的。
  10. 在OC中所有物件的存取都是用指標,C++中還有引用,OC中沒有,我寧願你強迫我使用一種方法,也不願你提供給我兩種方法我卻不知道使用哪一種方法的好。
  11. OC的方法定義和呼叫算是一大“特色”:
    1. 方法的定義:-(返回型別) 方法名:(引數1型別)引數1變數 引數2標籤:(引數2型別)引數2變數...
    2. [類或者例項的指標方法名: 引數1 標籤2: 引數2… …],
    3. 為了好看,第一個引數一般不加標籤名,當然,標籤名都可以隱藏的,但不建議這樣做,因為當你接手了一個離職的人程式,其中的JAVA 程式呼叫了一個有五個甚至更多的引數的方法,但是你手裡沒有這個方法的API,那麼你很難猜得出來這五個引數到底都幹什麼用的,但是Objective-C呼叫的時候,每個引數前面都必須有方法的標籤名,這樣你便能很容易的從標籤名看出這個引數是什麼意思。
  12. 呼叫類方法:
    1. [Fraction t];
    2. [[Fraction class] t];
    3. Class clazz=[Fraction class];[clazz t];
    4. class 來自於NSObject,相當於JAVA 中的getClass()方法,也就是獲取這個類的Class 物件,
    5. clazz 前面沒有*,這是因為Class 已經是一個指標。另外這種巢狀呼叫的方式,也要習慣,這就和JAVA 中的A.b().c()沒有什麼區別。
    6. 獲取Class 有如下幾種方法:
      1. [類或者物件 class]
      2. [類或者物件 superclasss]
      3. NSClassFromString(類名的字串形式)
    7. 你也可以通過如下的函式把Class 轉換為字串形式:
      1. NSStringFromClass(Class)
  13. OC中自定義類最好顯示繼承NSObject,可以獲得alloc、init、release等方法。
  14. OC中使用nil表示null,但跟java中的null還是有區別的,java中呼叫null的方法,會報臭名昭著的空指標異常,而OC不會,所以在OC你不必再為空指標而煩惱。
  15. Cocoa中提供的很多函式和java中的api有很大的不一樣,java是純物件導向的,所有的方法都必須在類中定義,但OC是相容C語言的,所以C中的程式導向思想在OC中也是行得通的,因此Cocoa中很多東西都不是物件,而是C語言的函式、結構體,比如NSLog()。
  16. 物件的初始化
    1. 前面看到例項化物件最多的方法是:Fraction *frac=[[Fraction alloc] init];
    2. 這跟java不一樣,java物件建立只需要new一下,同時呼叫構造方法(實際上虛擬機器層面分為兩個步驟:new物件,執行<init>方法(包含構造方法)),但是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被呼叫:
    1. Fraction *frac=[[Fraction alloc] init];
    2. 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 的字元型別增加了正規表示式的功能。
  • 字串
    1. -(BOOL) isEqualToString: (NSString*) s
    2. 比較兩個字串是否相等,與JAVA 一致的地方是==比較指標,比較物件是否相同要用到equal 方法。
    3. -(NSMutableString*) appendString: (NSString*) s
    4. 這與JAVA 的StringBuffer 的append 沒什麼區別。
  • 陣列
    1. Cocoa 使用NSArray 表示陣列,但是它不能儲存基本資料型別、enum、struct、nil,只能儲存Objective-C 的物件。
    2. NSArray *array=[NSArray arrayWithObjects: @"One", @"Two", @"Three", nil];
    3. 從這個類方法arrayWithObjects 的定義可以看出,它使用nil 表示陣列元素結束,這也是nil 不能儲存在NSArray 中的原因。
    4. NSMutableArray 為長度可變的陣列,相當於JAVA 中的List:
    5. NSMutableArray *mArray=[NSMutableArray arrayWithCapacity: 10];
    6. [mArray addObject: @"Apple"];//新增陣列元素
    7. NSEnumerator *e = [mArray objectEnumerator];//獲取陣列的迭代器,相當於JAVA 中的Iterator,reserveObjectEnumerator 用於獲取反轉之後的陣列迭代器。與JAVA 一致的地方是你在使用迭代器時,不能對陣列進行新增、刪除操作。
  • 字典(雜湊表)
    1. NSDictionary 用於儲存key-value 的資料結構,與JAVA 中的Map 類似。
    2. NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys: @"Apple", @"A", @"Google",@"G", nil];//dictionaryWithObjectAndKeys 後的可變引數,每兩個為一個value-key,以nil 表示結束。
    3. NSLog(@"%@",[dic objectForKey: @"A"]);//按照指定的key 查詢value
    4. 同樣的有NSMutableDictionary 表示長度可變的字典。
    5. NSMutableDictionary *mDic=[NSMutableDictionary dictionaryWithCapacity: 10];
    6. [mDic setObject: @"Apple" forKey: @"A"];//新增value-key 對
  • 雜湊Set
    1. NSSet 表示以hash 方式計算儲存位置的集合,與JAVA 中的HashSet 是一致的。
    2. 在NSSet 中的每個物件都有一個唯一的hash 值,重複的物件將只能保留一個。因此,這引出了Objective-C中的物件比較問題,這需要你實現從NSObject 繼承而來的如下兩個方法:
      1. - (BOOL) isEqual: (id) anObject;
      2. - (NSUInteger) hash;
    3. 這與JAVA 的物件比較沒有什麼區別,兩個相等的物件必須有相同的hashCode,所以這兩個方法必須同時實現。
    4. 同樣的,NSMutableSet 表示長度可變的雜湊Set。
  • 封裝類(相當於java的包裝器)
    1. 前面的幾個容器類的物件都不能存放基本資料結構、enum、struct、nil,怎麼辦呢?
    2. 在JAVA中我們知道所有的基本資料型別都有對應的封裝類,例如:int---Integer、boolean---Boolean,使用封裝類可以把基本資料型別包裝為物件,而封裝類本身又有方法可以返回原來的基本資料型別。Cocoa 使用NSValue 作為封裝類。
    3. NSRect rect=NSMakeRect(1,2,30,50);//這個是在前面提到過的一個表示矩形的結構體
    4. NSValue *v=[NSValue valueWithBytes: &rect objCType: @encode(NSRect)];
    5. //java與oc之間的比較 valueWithBytes 要求傳入包裝資料的地址,也就是變數中儲存的資料的首地址,我們依然使用C 語言的&作為地址運算子,計算指標儲存的首地址。
    6. //objCType 要求傳入用於描述資料的型別、大小的字串,使用@encode 指令包裝資料所屬的型別。
    7. 對於基本資料型別,你可以使用簡便的NSNumber 來封裝,它是NSValue 的子類。
    8. 如果你想儲存空值到集合類,可以使用NSNull,它使用NSNull *n =[NSNull null];的方式建立。
  • 日期型別
    • Cocoa 中使用NSDate 型別表示日期。
  • 資料緩衝區(其實就是java中的位元組陣列)
    • Cocoa 中使用NSData 型別來實現緩衝區,用於儲存二進位制的資料型別,譬如:從網路下載回來的檔案等。
    • NSData 是長度不可變的資料緩衝區,還有一個NSMutableData 用來儲存長度可變的資料緩衝區。
  • 寫入和讀取屬性(簡直就是java序列化和反序列化的翻版)
    1. 在iPhone 的*.ipa 檔案中,經常可以看到*.plist 檔案,它儲存了程式的相關屬性,叫做屬性列表。其實它就是NSArray、NSDictionary、NSString、NSData 持久化之後的檔案。
    2. 這幾個型別都有一個成員方法 writeToFile: (NSString*) file atomically: BOOL 用於將自己寫入到一個檔案。atomically 為YES 表示檔案先儲存到臨時檔案區,如果檔案儲存成功,再替換掉原始檔案,這樣的好處是可以防止在儲存檔案過程中出錯,但是僅適用於小檔案,因為你相當於是在臨時檔案儲存區也放了一份,再加上原始檔案,就是兩份檔案,如果檔案比較大,將會佔用較多的使用者的磁碟空間。
    3. 如果你要持久化的型別不是上述的陣列、字典、緩衝區,那該怎麼辦呢?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 需要我們去實現。
  • 多執行緒
    1. Objective-C 的多執行緒程式設計與JAVA 語言極其類似分為原始的執行緒操作、執行緒池兩種,後者其實就是使用佇列等機制對前者的封裝。
    2. JAVA 也一樣,原始的執行緒操作使用Thread 類,執行緒池的操作使用java.util.concurrent.*中的類庫。
    3. Objective-C 中NSThread 型別表示執行緒,NSCondition 用於執行同步操作,在功能和用法上相當於JAVA 中的java.util.concurrent.*包中的Lock 物件。
    4. 另外,Objective-C 也支援@synchronized 指令做程式碼同步,寫法也和JAVA 中的很相似。
    5. 另外比較有趣的是,如果你想更新UI 上的某一個部件,就需要在發起的新執行緒裡呼叫UI 所在的主執行緒上的一個方法,新執行緒不能直接訪問主執行緒的方法,需要在run 方法中使用如下的方法:
    6. - (void) performSelectorOnMainThread: (SEL) aSelector withObject: (id) arg waitUntilDone: (BOOL) wait
    7. 如果對java的swing熟悉的話,應該知道在UI界,大多數更新介面的都必須在一個單執行緒中執行,因為如果更新介面是多執行緒的話會很難處理的,java swing處理的方法是更新介面的程式碼放在專門更新UI的執行緒下執行,這裡IOS也是這麼一個思想。
    8. Objective-C 使用 NSOperation、NSOperationQueue 兩個型別實現執行緒池的操作,NSOpertion就好比JAVA 中的Runnable,其中的main(名字有點兒奇怪)方法也就是你要執行的操作,NSOperationQueue 是一個執行緒池佇列,你只需要把NSOperation 新增到NSOperationQueue,佇列就會retain 你的NSOperation,然後執行其中的操作,直到執行完畢。
    9. 佇列中會有多個操作,佇列會按照你新增的順序逐個執行,也就說佇列中的任務是序列的。
    10. 跟java一樣,如果執行緒池可以滿足你的業務需求,儘量使用執行緒池,而不是原始的NSThread,因為使用執行緒池遮蔽了許多執行緒自身需要處理的問題,程式碼也更加簡潔。
  • KVC 與KVO(這個話題真高階)
    1. KVC 是 NSKeyValueCoding 的縮寫,它是Foundation Kit 中的一個NSObject 的Category,作用可以類比JAVA 中的反射機制,就是動態訪問一個物件中的屬性。
    2. KVC 在解析key 的字串的時候,是會比正常呼叫setter、getter 要慢的,而且編譯器無法在編譯器對你的方法呼叫做出檢查(因為你的屬性名都是字串,只有執行時才會知道你有沒有寫錯),出錯的機率也會提高,所以請不要隨意使用KVC,而省去setter、getter 方法。KVC 一般用於動態繫結,也就是執行時才能確定誰呼叫哪個方法,編譯期並不確定。
    3. KVO就是 NSKeyValueObserving的縮寫,它也是Foundation Kit中的一個NSObject的Category,KVO 基於KVC 實現,基於觀察者設計模式(Observer Pattern)實現的一種通知機制,可以類比JAVA 中的JMS,通過訂閱的方式,實現了兩個物件之間的解耦,但又可以讓他們相互呼叫。
  • 謂詞 NSPredicateCocoa 
      • 提供了NSPredicate 用於指定過濾條件
      • 謂詞是指在計算機中表示計算真假值的函式,它使用起來有點兒像SQL 的查詢條件,主要用於從集合中分揀出符合條件的物件,也可以用於字串的正則匹配。

    參考文章

    • http://blog.sina.com.cn/s/blog_93742d0d010165qi.html

     

    相關文章