在Objective-C,經常會用到常量字符串,常量字符串和一般的字符串還是有一定區別的.本節將介紹一些常量字符串的特性,用來加強對常量字符串的理解.請看下面一段代碼.
NSSting *sting1 = @"Hello";
NSSting *sting2 = @"Hello";
if (sting1 == sting2) {
NSLog(@"They are same address");
}
對字符串常量sting1和sting2的地址值進行比較,就會發現二者竟然是相等的,產生這樣的結果要歸咎于編譯器優化的結果.
由于常量會占用一塊特殊的代碼段,加載到內存是會映射到一塊常量存儲區,以加快訪問速度,編譯器在編譯時發現sting1和sting2的內容是相同的常量字符串,會把它們都指向一個相同的區域,而不是在開辟出一塊額外的空間.因此,它們是相同的地址值.
NSSting *sting1 = @"Hello";
NSSting *sting2 = [NSSting alloc]; NSSting *sting3 = [sting2 initWithSting:sting1]; if (sting1 == sting2) {
NSLog(@"sting2 are not same to sting3!");
}
if (sting1 == sting3) {
NSLog(@"sting1 are not same to sting3!");
}
首先,申明上面這一段代碼不是一段合法的代碼,因此在第2行alloc之后沒有立即init.雖然這種做法是非常不推薦的,但這次為了更加清晰地說明問題,不得而已為之.
通過程序比較分析,就會發現sting2和sting3的地址竟然不相等,而sting1和sting3竟然相等.通過這些可以看出,如果使用一個常量字符串來初始化另一個字符串,另一個字符串會直接通過地址賦值為常量字符串,alloc的內存也會立即釋放.再看看下面這段代碼:
NSSting *sting1 = [[NSSting alloc]initWithSting:@"Hello"];
[sting1 release];
[sting1 release];
[sting1 release];
NSLog("%@", sting1);
sting1經過多次release竟然還能繼續訪問,由此說明常量字符串不會release
在Objective-C語言中,集合是最常用的數據類型.而對與集合的訪問,要優先考慮使用快速枚舉.使用快速枚舉,要盡可能使用枚舉新的寫法.
盡可使用枚舉新的寫法
使用Objective-C新的枚舉寫法,編寫更簡潔的代碼,同時避免一些常見的陷阱.更重要的是,這些語法特性是完全向下兼容的,使用新特性編寫后形成的二進制程序可以運行之前發布的OS中.
在使用枚舉新的寫法之前,先梳理一下最近幾年來枚舉類型幾次功能的改進.
枚舉在OS X10.5之前的版本中,如何在Objective-C中定義一個枚舉類型呢?定義方法如下:
typedef enum {
ObjectiveC,
Java,
Ruby,
Python,
Erlang
}
Language;
這種寫法簡單明了,用起來也不復雜,但是有一個問題,就是其枚舉值得數據范圍是模糊的,這個數值可能非常大,可是負數,無法界定.
在OSX 10.5之后 的版本中,就可以這樣寫:
enum{
ObjectiveC,
Java,
Ruby,
Python,
Erlang
};
typedef NSUInteger Language;
這種寫法的好處是,首先這個枚舉的數據類型是確定的,無符號整數;其次,由于采用了NSUInteger,就可以不用考慮32位和64位的問題.但這樣寫所帶來的問題是,數據類型和枚舉常量沒有顯示的關聯在Xcode4.4,就可以這樣寫枚舉:
typedef enum Language:NSUInteger {
ObjectiveC,
Java,
Ruby,
Python,
Erlang
}Language;
在列出枚舉內容的同時綁定了枚舉數據類型NSUInteger,這樣帶來的好處是增強了類型檢查和更好的代碼可讀性.
當然,對于普通開發者來說,枚舉類型可能不會涉及復雜的數據,使用之前的兩種寫法也不會用什么大問題,但還是建議使用枚舉的寫法,以增強現在寫的代碼在未來中有更強的適用性.
上面介紹了為何盡可能使用枚舉新的寫法,下面將介紹枚舉在集合中的應用.
Objective-C和Cocoa或Cocoa Touch提供了多種方式來枚舉集合的內容.雖然它可以使用傳統的C循環來遍歷內容,像這樣:
int count = [array count];
for(int index = 0;index < count; index++) {
id eachObject = [array objectAtIndex:index];
...
}
這時最好的做法是使用本節中描述的其他技術之一.
理解快速枚舉
快速枚舉是一種語言中用于快速安全的枚舉一個集合的表達式,下面將從快速枚舉的定義及其應用來分別介紹.
for...in
表達式,這種快速枚舉表達式是如下定義的:
for(existingItem in expression) {
statements
}
這兩種表達式中,expression 是一個遵守NSFastEnmeration協議的對象.每次循環被迭代的對象都會返回一個對象并賦給一個循環變量,同時statements 中定義的代碼被執行一次.當被迭代的對象中已經沒有數據可以取出時,循環變量被設為nil,如果循環在這之前被停止,那么循環變量會指向最后一次返回的值.
使用快速枚舉可以很容易地枚舉集合
許多集合類遵照了NSFastEnumeration協議,如Foundation中的集合類NSArray、NSDictionary和NSSet.顯而易見,枚舉操作可以遍歷NSArray和NSSet的內容.對于其他類,相關文檔會說明哪些內容會被枚舉.例如,NSDictionary和NSManagedObjectModel也支持快速枚舉但是NSDictionary枚舉的是它的鍵值,NSManagedObjectModel枚舉的是它的實體.
作為一個例子,可以使用快速枚舉,像這樣在每一個數組記錄每個對象的描述:
for(id eachObject in array){
id object = dictionary[eachKey];
NSLog(@"object:%@for key:%@",object,eachKey);
}
快速枚舉的行為很像一個標準C循環,這樣就可以使用打破關鍵字來中斷迭代,或繼續前進到下一個元素.如果枚舉有序的集合,枚舉按順序來進行.對于一個NSArray,這意味著第一次將索引為0,第二個對象在索引1處等等.如果需要跟蹤當前索引,只需迭代計數,因為它們發生:
int index = 0;
for(id eachObject in array) {
NSLog(@"Object at index %i is:%@",index,eachObject);
index++
}
在快速枚舉集合期間,不能變異集合,即使集合是可變的.如果從循環內,嘗試添加或刪除集合的對象,則會生成運行時異常.
大多數集合也支持枚舉對象
也可以通過使用NSEnumerator對象枚舉Cocoa和Cocoa Touch中的許多集合.例如,對于objectEnmerator 或reverseObjectEnumerator,可以查詢NSArray.快速枚舉可以使用這些對象,像這樣:
for(id eachObject in[array reverseObjectEnumerator]) {
...
}
從此示例中,循環將按照相反的順序來讓變數可以遞回取得集合的對象,所以最后一個對象將是第一個,等等.通過反復調用枚舉的nextObject的方法,它也可以遍歷這些內容,像這樣:
id eachObject;
while ((eachObject = [enumerator nextObject])) {
NSLog(@"Current object is:%@",eachObject);
}
在這個例子中,一個 while 循環用于將eachObject變量設置為每次循環的下一給對象.當沒有更多的對象保留時間,nextObject方法將返回nil.評估為false的邏輯值,以使循環停止.