(轉(zhuǎn))iOS開發(fā)之玩轉(zhuǎn)字符串

原地址http://segmentfault.net/a/1190000000444620

iOS開發(fā)之玩轉(zhuǎn)字符串

目錄結(jié)構(gòu)

在每個應(yīng)用里我們都大量使用字符串。下面我們將快速看看一些常見的操作字符串的方法,過一遍常見操作的最佳實踐。

字符串的比較、搜索和排序

排序和比較字符串比第一眼看上去要復(fù)雜得多。不只是因為字符串可以包含代理對(surrogate pairs )(詳見 Ole 寫的這篇關(guān)于 Unicode 的文章) ,而且比較還與字符串的本地化相關(guān)。在某些極端情況下相當(dāng)棘手。

蘋果文檔中 String Programming Guide 里有一節(jié)叫做 “字符與字形集群(Characters and Grapheme Clusters)”,里面提到一些陷阱。例如對于排序來說,一些歐洲語言將序列“ch”當(dāng)作單個字母。在一些語言里,“?”被認為等同于 ‘a(chǎn)’ ,而在其它語言里它卻被排在 ‘z’ 后面。

而 NSString 有一些方法來幫助我們處理這種復(fù)雜性。首先看下面的方法:

- (NSComparisonResult)compare:(NSString *)aString

options:(NSStringCompareOptions)mask

range:(NSRange)range

locale:(id)locale

它帶給我們充分的靈活性。另外,還有很多“便捷函數(shù)”都使用了這個方法。

與比較有關(guān)的可用參數(shù)如下:

NSCaseInsensitiveSearch

NSLiteralSearch

NSNumericSearch

NSDiacriticInsensitiveSearch

NSWidthInsensitiveSearch

NSForcedOrderingSearch

它們都可以用邏輯或運算組合在一起。

NSCaseInsensitiveSearch :“A”等同于“a”,然而在某些地方還有更復(fù)雜的情況。例如,在德國,“?” 和 “SS”是等價的。

NSLiteralSearch :Unicode 的點對 Unicode 點比較。它只在所有字符都用相同的方式組成的情況下才會返回相等。LATIN CAPITAL LETTER A 加上 COMBINING RING ABOVE 并不等同于 LATIN CAPITAL LETTER A WITH RING ABOVE.

譯注:這個要解釋一下,首先,每一個Unicode都是有官方名字的!LATIN CAPITAL > LETTER A是一個大寫“A”,COMBINING RING ABOVE是一個? ?,LATIN CAPITAL > LETTER A WITH RING ABOVE,這是?前兩者的組合不等同于后者。

NSNumericSearch:它對字符串里的數(shù)字排序,所以 “Section 9” \< “Section 20” \< “Section 100.”

NSDiacriticInsensitiveSearch : “A” 等同于 “?” 等同于 “?.”

NSWidthInsensitiveSearch : 一些東亞文字(平假名 和 片假名)有全寬與半寬兩種形式。

很值得一提的是 - (NSComparisonResult)localizedStandardCompare: ,它排序的方式和 Finder 一樣。它對應(yīng)的選項是 NSCaseInsensitiveSearch 、 NSNumericSearch 、NSWidthInsensitiveSearch 以及 NSForcedOrderingSearch 。如果我們要在UI上顯示一個文件列表,用它就最合適不過了。

大小寫不敏感的比較和音調(diào)符號不敏感的比較都是相對復(fù)雜和昂貴的操作。如果我們需要比較很多次字符串那這就會成為一個性能上的瓶頸(例如對一個大的數(shù)據(jù)集進行排序),一個常見的解決方法是同時存儲原始字符串和折疊字符串。例如,我們的 Contact? 類有一個正常的 name? 屬性,在內(nèi)部它還有一個foldedName? 屬性,它將自動在 name變化時更新。那么我們就可以使用 NSLiteralSearch? 來比較 name? 的折疊版本。 NSString? 有一個方法來創(chuàng)建折疊版本:

- (NSString *)stringByFoldingWithOptions:(NSStringCompareOptions)options

locale:(NSLocale *)locale

搜索

要在一個字符串中搜索子字符串,最靈活性的方法是:

- (NSRange)rangeOfString:(NSString *)aString

options:(NSStringCompareOptions)mask

range:(NSRange)searchRange

locale:(NSLocale *)locale

同時,還有一些“便捷方法”,它們在最終都會調(diào)用上面這個方法,我們可以傳入上面列出的參數(shù),以及以下這些額外的參數(shù):

NSBackwardsSearch

NSAnchoredSearch

NSRegularExpressionSearch

NSBackwardsSearch :在字符串的末尾開始反向搜索。

NSAnchoredSearch : 只考慮搜索的起始點(單獨使用)或終止點(當(dāng)與 NSBackwardsSearch? 結(jié)合使用時)。這個方法可以用來檢查前綴或者后綴,以及大小寫不敏感(case-insensitive)或者音調(diào)不敏感(diacritic-insensitive)的比較。

NSRegularExpressionSearch :使用正則表達式搜索,要了解更多與使用正則表達式有關(guān)的信息,請關(guān)注 Chris’s 的 String Parsing 。

另外,還有一個方法:

- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet

options:(NSStringCompareOptions)mask

range:(NSRange)aRange

與前面搜索字符串不同的是, 它只搜索給定字符集的第一個字符。即使只搜索一個字符,但如果由于此字符是由元字符組成的序列(composed character sequence),所以返回范圍的長度也可能大于1。

大寫與小寫

一定不要使用 NSString? 的 -uppercaseString? 或者 -lowercaseString? 的方法來處理 UI 顯示的字符串,而應(yīng)該使用 -uppercaseStringWithLocale? 來代替, 比如:

NSString *name = @"Tómas";

cell.text = [name uppercaseStringWithLocale:[NSLocale currentLocale]];

格式化字符串

同C語言中的 sprintf 函數(shù)( ANSI C89 中的一個函數(shù) )類似, Objective C 中的 NSString 類也有如下的3個方法:

-initWithFormat:

-initWithFormat:arguments:

+stringWithFormat:

需要注意這些格式化方法都是 非本地化 的 。所以這些方法得到的字符串是不能直接拿來顯示在用戶界面上的。如果需要本地化,那我們需要使用下面這些方法:

-initWithFormat:locale:

-initWithFormat:locale:arguments:

+localizedStringWithFormat:

Florian 有一篇關(guān)于 字符串的本地化 的文章更詳細地討論了這個問題。

printf(3)的man頁面有關(guān)于它如何格式化字符串的全部細節(jié)。除了所謂的轉(zhuǎn)換格式(它以%字符開始),格式化字符串會被逐字復(fù)制:

double a = 25812.8074434;

float b = 376.730313461;

NSString *s = [NSString stringWithFormat:@"%g :: %g", a, b];

// "25812.8 :: 376.73"

我們格式化了兩個浮點數(shù)。注意單精度浮點數(shù)和雙精度浮點數(shù)共同了一個轉(zhuǎn)換格式。

對象

除了來自 printf(3) 的轉(zhuǎn)換規(guī)范,我們還可以使用 %@? 來輸出一個對象。在對象描述那一節(jié)中有述,如果對象響應(yīng) -descriptionWithLocale:? 方法,則調(diào)用它,否則調(diào)用 -description 。? %@? 被結(jié)果替換。

整數(shù)

使用整形數(shù)字時,有些需要注意的細節(jié)。首先,有符號數(shù)(d和i)和無符號數(shù)(o、u、x和X)分別有轉(zhuǎn)換規(guī)范。需要使用者選擇具體的類型。

如果我們使用的東西是 printf不知道的,我們必須要做類型轉(zhuǎn)換。 NSUInteger? 正是這樣一個例子,它在64位和32位平臺上是不一樣的。下面的例子可以同時工作在32位和64位平臺。

uint64_t p = 2305843009213693951;

NSString *s = [NSString stringWithFormat:@"The ninth Mersenne prime is %llu", (unsigned long long) p];

// "The ninth Mersenne prime is 2305843009213693951"

Modifier? ? ? d, i? ? ? ? ? ? o, u, x, X

-------------- --------------- ----------------------

hh? ? ? ? ? ? ? signed char? ? unsigned char

h? ? ? ? ? ? ? ? short? ? ? ? ? unsigned short

(none)? ? ? ? ? int? ? ? ? ? ? unsigned int

l (ell)? ? ? ? ? long? ? ? ? ? ? unsigned long

ll (ell ell)? ? long long? ? ? unsigned long long

j? ? ? ? ? ? ? ? intmax\_t? ? ? uintmax\_t

t? ? ? ? ? ? ? ? ptrdiff\_t

z? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size\_t

適用于整數(shù)的轉(zhuǎn)換規(guī)則有:

int m = -150004021;

uint n = 150004021U;

NSString *s = [NSString stringWithFormat:@"d:%d i:%i o:%o u:%u x:%x X:%X", m, m, n, n, n, n];

// "d:-150004021 i:-150004021 o:1074160465 u:150004021 x:8f0e135 X:8F0E135"

%d? 和 %i? 具有一樣的功能,它們都打印出有符號十進制數(shù)。 %o? 就較為晦澀了:它使用八進制表示。 %u? 輸出無符號十進制數(shù)——它是我們常用的。最后 %x? 和 %X? 使用十六進制表示——后者使用大寫字母。

對于 x%? 和 X%? ,我們可以在 0x 前面添加 “#” 井字符前綴看,增加可讀性。

我們可以傳入特定參數(shù),來設(shè)置最小字段寬度和最小數(shù)字位數(shù)(默認兩者都是0),以及左/右對齊。請查看man頁面獲取詳細信息。下面是一些例子:

int m = 42;

NSString *s = [NSString stringWithFormat:@"'%4d' '%-4d' '%+4d' '%4.3d' '%04d'", m, m, m, m, m];

// ‘42’ ‘42 ’ ‘ +42’ ‘ 042’ ‘0042’

m = -42;

NSString *s = [NSString stringWithFormat:@"'%4d' '%-4d' '%+4d' '%4.3d' '%04d'", m, m, m, m, m];

// ‘ -42’ ‘-42 ’ ‘ -42’ ‘-042’ ‘-042’

%p? 可用于打印出指針——它和 %#x? 相似但可同時在32位和64位平臺上正常工作。

浮點數(shù)

關(guān)于浮點數(shù)的轉(zhuǎn)換規(guī)則有8個:eEfFgGaA。但除了 %f 和 %g 外我們很少使用其它的。對于指數(shù)部分,小寫的版本使用小寫 e,大寫的版本就使用大寫 E。

通常 %g? 是浮點數(shù)的全能轉(zhuǎn)換符 ,它與 %f? 的不同在下面的例子里顯示得很清楚:

double v[5] = {12345, 12, 0.12, 0.12345678901234, 0.0000012345678901234};

NSString *s = [NSString stringWithFormat:@"%g %g %g %g %g", v[0], v[1], v[2], v[3], v[4]];

// "12345 12 0.12 0.123457 1.23457e-06"

NSString *s = [NSString stringWithFormat:@"%f %f %f %f %f", v[0], v[1], v[2], v[3], v[4]];

// "12345.000000 12.000000 0.120000 0.123457 0.000001"

和整數(shù)一樣,我們依然可以指定最小字段寬度和最小數(shù)字?jǐn)?shù)。

指定位置

格式化字符串允許使用參數(shù)來改變順序:

[NSString stringWithFormat:@"%2$@ %1$@", @"1st", @"2nd"];

// "2nd 1st"

我們只需將從1開始的參數(shù)與一個$接在%后面。這種寫法在進行本地化的時候極其常見,因為在不同語言中,各個參數(shù)所處的順序位置可能不盡相同。

NSLog()

NSLog() 函數(shù)與? +stringWithFormat: 的工作方式一樣。我們可以調(diào)用:

int magic = 42;

NSLog(@"The answer is %d", magic);

下面的代碼可以用同樣的方式構(gòu)造字符串:

int magic = 42;

NSString *output = [NSString stringWithFormat:@"The answer is %d", magic];

顯然? NSLog()會輸出字符串,并且它會加上時間戳、進程名、進程ID以及線程ID作為前綴。

實現(xiàn)能接受格式化字符串的方法

有時在我們自己的類中提供一個能接受格式化字符串的方法會很方便使用。假設(shè)我們要實現(xiàn)的是一個 To Do 應(yīng)用,它包含一個 Item 類。我們想要提供:

+ (instancetype)itemWithTitleFormat:(NSString *)format, ...

如此我們就可以使用:

Item *item = [Item itemWithFormat:@"Need to buy %@ for %@", food, pet];

這種類型的方法可以接受可變數(shù)量的參數(shù),所以被稱為可變參數(shù)方法。我們必須使用一個定義在stdarg.h里的宏來使用可變參數(shù)。上面方法的實現(xiàn)代碼可能會像下面這樣:

+ (instancetype)itemWithTitleFormat:(NSString *)format, ...;

{

va_list ap;

va_start(ap, format);

NSString *title = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:ap];

va_end(ap);

return [self itemWithTitle:title];

}

進一步,我們要添加 NS_FORMAT_FUNCTION 到方法的定義里(在頭文件中),如下所示:

+ (instancetype)itemWithTitleFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);

NS_FORMAT_FUNCTION 展開為一個方法 __attribute__,它會告訴編譯器在索引1處的參數(shù)是一個格式化字符串,而實際參數(shù)從索引2開始。這將允許編譯器檢查格式化字符串而且會像 NSLog() 和 -[NSString stringWithFormat:] 一樣輸出警告信息。

字符與字符串組件

如有一個字符串 “bird” ,找出組成它的獨立字母是很簡單的。第二個字母是“i”(Unicode: LATIN SMALL LETTER I)。而對于像?se這樣的字符串就沒那么簡單了。看起來像三個字母的組合可有多種方式,例如:

A LATIN CAPITAL LETTER A

? COMBINING RING ABOVE

s LATIN SMALL LETTER S

e LATIN SMALL LETTER E

或者

? LATIN CAPITAL LETTER A WITH RING ABOVE

s LATIN SMALL LETTER S

e LATIN SMALL LETTER E

從 Ole 寫的這篇關(guān)于 Unicode 的文章 里可以讀到更多關(guān)于聯(lián)合標(biāo)記(combining marks)的信息,其他語言文字有更多復(fù)雜的代理對(complicated surrogate pairs)。

如果我們要在字符層面處理一個字符串,那我們就要小心翼翼。蘋果官方文檔中 String Programming Guide 有一節(jié)叫做 “Characters and Grapheme Clusters”,里面有更多關(guān)于這一點的細節(jié)。

NSString有兩個方法:

-rangeOfComposedCharacterSequencesForRange:

-rangeOfComposedCharacterSequenceAtIndex:

上面這兩個方法在有的時候很有幫助,例如,分開一個字符串時保證我們不會分開被稱為代理對(surrogate pairs)的東西。

如果我們要在字符串的字符上做工作, NSString 有個叫做 -enumerateSubstringsInRange:options:usingBlock: 的方法。

將 NSStringEnumerationByComposedCharacterSequences 作為選項傳遞,我們就能掃描所有的字符。例如,用下面的方法,我們可將字符串 “International Business Machines” 變成 “IBM”。

- (NSString *)initials;

{

NSMutableString *result = [NSMutableString string];

[self enumerateSubstringsInRange:NSMakeRange(0, self.length)

options:NSStringEnumerationByWords | NSStringEnumerationLocalized

usingBlock:^(NSString *word, NSRange wordRange, NSRange enclosingWordRange, BOOL *stop1) {

__block NSString *firstLetter = nil;

[self enumerateSubstringsInRange:NSMakeRange(0, word.length)

options:NSStringEnumerationByComposedCharacterSequences

usingBlock:^(NSString *letter, NSRange letterRange, NSRange enclosingLetterRange, BOOL *stop2) {

firstLetter = letter;

*stop2 = YES;

}];

if (letter != nil) {

[result appendString:letter];

};

}];

return result;

}

如文檔所示,詞和句的分界可能基于地區(qū)的變化而變化。因此有 NSStringEnumerationLocalized選項。

多行文字字面量

編譯器的確有一個隱蔽的特性:把空格分隔開的字符串銜接到一起。這是什么意思呢?下面兩段代碼是完全等價的:

NSString *limerick = @"A lively young damsel named Menzies\n"

@"Inquired: ?Do you know what this thenzies??\n"

@"Her aunt, with a gasp,\n"

@"Replied: "It's a wasp,\n"

@"And you're holding the end where the stenzies.\n";

NSString *limerick = @"A lively young damsel named Menzies\nInquired: ?Do you know what this thenzies??\nHer aunt, with a gasp,\nReplied: "It's a wasp,\nAnd you're holding the end where the stenzies.\n";

前者看起來更舒服,但是有一點要注意千萬不要在任意一行末尾加入逗號或者分號。

同時也可以這樣做:

NSString * string = @"The man " @"who knows everything " @"learns nothing" @".";

譯者注:上面這行代碼原文是有誤的,原文是 NSString * @"The man " @"who knows everything " @"learns nothing" @"."? ,讀者可以嘗試一下,如果這樣寫是無法通過編譯的。

編譯器只是為我們提供了一個便捷的方式,將多個字符串在編譯期組合在了一起。

可變字符串

可變字符串有兩個常見的使用場景:(1)拼接字符串(2)替換部分字符串

創(chuàng)建字符串

可變字符串可以很輕易地把多個字符串在你需要的時候組合起來。

- (NSString *)magicToken

{

NSMutableString *string = [NSMutableString string];

if (usePrefix) {

[string appendString:@">>>"];

}

[string appendFormat:@"%d--%d", self.foo, self.bar];

if (useSuffix) {

[string appendString:@">>>"];

}

return string;

}

這里要注意的是,雖然原本返回值應(yīng)該是一個 NSString? 類型的對象,我們只是簡單地返回一個NSMutableString 類型的對象。

替換字符串

可變字符串除了追加組合之外,還提供了以下4個方法:

-deleteCharactersInRange:

-insertString:atIndex:

-replaceCharactersInRange:withString:

-replaceOccurrencesOfString:withString:options:range:

這些方法和 NSString 的類似:

-stringByReplacingOccurrencesOfString:withString:

-stringByReplacingOccurrencesOfString:withString:options:range:

-stringByReplacingCharactersInRange:withString:

但是它沒有創(chuàng)建新的字符串僅僅把當(dāng)前字符串變成了一個可變的類型,這樣讓代碼更容易閱讀,以及提升些許性能。

NSMutableString *string; // 假設(shè)我們已經(jīng)有了一個名為 string 的字符串

// 現(xiàn)在要去掉它的一個前綴,做法如下:

NSString *prefix = @"WeDon’tWantThisPrefix"

NSRange r = [string rangeOfString:prefix

options:NSAnchoredSearch

range:NSMakeRange(0, string.length)

locale:nil];

if (r.location != NSNotFound) {

[string deleteCharactersInRange:r];

}

連接組件

一個看似微不足道但很常見的情況是字符串連接。比如現(xiàn)在有這樣幾個字符串:

Hildr

Heidrun

Gerd

Guerún

Freya

Nanna

Siv

Skaei

Gróa(chǎn)

我們想用它們來創(chuàng)建下面這樣的一個字符串:

Hildr, Heidrun, Gerd, Guerún, Freya, Nanna, Siv, Skaei, Gróa(chǎn)

那么就可以這樣做:

NSArray *names = @["Hildr", @"Heidrun", @"Gerd", @"Guerún", @"Freya", @"Nanna", @"Siv", @"Skaei", @"Gróa(chǎn)"];

NSString *result = [names componentsJoinedByString:@", "];

如果我們將其顯示給用戶,我們就要使用本地化表達,確保將最后一部分替換相應(yīng)語言的 , and :

@implementation NSArray (ObjcIO_GroupedComponents)

- (NSString *)groupedComponentsWithLocale:(NSLocale *)locale;

{

if (self.count < 1) {

return @"";

} else if (self.count < 2) {

return self[0];

} else if (self.count < 3) {

NSString *joiner = NSLocalizedString(@"joiner.2components", @"");

return [NSString stringWithFormat:@"%@%@%@", self[0], joiner, self[1]];

} else {

NSString *joiner = [NSString stringWithFormat:@"%@ ", [locale objectForKey:NSLocaleGroupingSeparator]];

NSArray *first = [self subarrayWithRange:NSMakeRange(0, self.count - 1)];

NSMutableString *result = [NSMutableString stringWithString:[first componentsJoinedByString:joiner]];

NSString *lastJoiner = NSLocalizedString(@"joiner.3components", @"");

[result appendString:lastJoiner];

[result appendString:self.lastObject];

return result;

}

}

@end

那么在本地化的時候,如果是英語,應(yīng)該是:

"joiner.2components" = " and ";

"joiner.3components" = ", and ";

如果是德語,則應(yīng)該是:

"joiner.2components" = " und ";

"joiner.3components" = " und ";

結(jié)合組件的逆過程可以用? -componentsSeparatedByString: ,這個方法會將一個字符串變成一個數(shù)組。例如,將 “12|5|3” 變成 “12”、“5” 和 “3”。

對象描述

在許多面向?qū)ο缶幊陶Z言里,對象有一個叫做 toString() 或類似的方法。在 Objective C 里,這個方法是:

- (NSString *)description

以及它的兄弟方法:

- (NSString *)debugDescription

當(dāng)自定義模型對象時,覆寫 -description 方法是一個好習(xí)慣,在UI上顯示該對象時調(diào)用的就是description方法的返回值。假定我們有一個 Contact類,下面是它的 description方法實現(xiàn)。

- (NSString *)description

{

return self.name;

}

我們可以像下面代碼這樣格式化字符串:

label.text = [NSString stringWithFormat:NSLocalizedString(@"%@ has been added to the group “%@”.", @""), contact, group];

因為該字符串是用來做UI顯示的,我們可能需要做本地化,那么我們就需要覆寫descriptionWithLocale:(NSLocale *)locale方法。

- (NSString *)descriptionWithLocale:(NSLocale *)locale;

%@ 會首先調(diào)用 -descriptionWithLocale,如果沒有返回值,再調(diào)用 -description,在調(diào)試時,打印一個對象,我們用 po這個命令(它是print object的縮寫)

(lldb) po contact

如果在調(diào)試窗口的終端下輸入 po contact, 它會調(diào)用對象的 debugDescription方法。默認情況下debugDescription是直接調(diào)用 description。如果你希望輸出不同的信息,那么就分別覆寫兩個方法。大多數(shù)情況下,尤其是對于非數(shù)據(jù)模型的對象,你只需要覆寫 -description就能滿足需求了。

實際上對象的標(biāo)準(zhǔn)格式化輸出是這樣的:

- (NSString *)description;

{

return [NSString stringWithFormat:@"<%@: %p>", self.class, self];

}

NSObject就是這么干的。當(dāng)你覆寫該方法時,也可以像這樣寫。假定我們有一個DetailViewController,在它的UI上要顯示一個 contact ,我們可能會這樣覆寫該方法:

- (NSString *)description;

{

return [NSString stringWithFormat:@"<%@: %p> contact = %@", self.class, self, self.contact.debugDescription];

}

NSManagedObject子類的描述

我們將特別注意向 NSManagedObject 的子類添加 -description / -debugDescription 的情況。由于 Core Data的惰性加載機制(faulting mechanism)允許未加載數(shù)據(jù)的對象存在,所以當(dāng)我們調(diào)用 -debugDescription 我們并不希望改變我們的應(yīng)用程序的狀態(tài),因此我要確保檢查 isFault? 這個屬性。例如,我們可如下這樣實現(xiàn)它:

- (NSString *)debugDescription;

{

NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p>", self.class, self];

if (! self.isFault) {

[description appendFormat:@" %@ \"%@\" %gL", self.identifier, self.name, self.metricVolume];

}

return description;

}

再次,因為它們是模型對象,重載 -description 簡單地返回描述實例的屬性名就可以了。

文件路徑

簡單來說就是我們不應(yīng)該使用 NSString來描述文件路徑。對于 OS X 10.7 和 iOS 5, NSURL更便于使用,而且更有效率,它還能緩存文件系統(tǒng)的屬性。

再者, NSURL 有八個方法來訪問被稱為 resource values 的東西。它們提供給我們一個穩(wěn)定的接口來獲取和設(shè)置文件與目錄的多種屬性,例如本地化文件名( NSURLLocalizedNameKey)、文件大?。∟SURLFileSizeKey),以及創(chuàng)建日期( NSURLCreationDateKey),等等。

尤其是在遍歷目錄內(nèi)容時,使用 -[NSFileManagerenumeratorAtURL:includingPropertiesForKeys:options:errorHandler:] 附帶一個關(guān)鍵詞列表,然后用 -getResourceValue:forKey:error: 檢索它們,能帶來顯著的性能提升。

下面是一個簡短的例子展示了如何將它們組合在一起:

NSError *error = nil;

NSFileManager *fm = [[NSFileManager alloc] init];

NSURL *documents = [fm URLForDirectory:NSDocumentationDirectory

inDomain:NSUserDomainMask

appropriateForURL:nil

create:NO

error:&error];

NSArray *properties = @[NSURLLocalizedNameKey, NSURLCreationDateKey];

NSDirectoryEnumerator *dirEnumerator = [fm enumeratorAtURL:documents

includingPropertiesForKeys:properties

options:0

errorHandler:nil];

for (NSURL *fileURL in dirEnumerator) {

NSString *name = nil;

NSDate *creationDate = nil;

if ([fileURL getResourceValue:&name

forKey:NSURLLocalizedNameKey

error:NULL] &&

[fileURL getResourceValue:&creationDate

forKey:NSURLCreationDateKey

error:NULL])

{

NSLog(@"'%@' was created at %@", name, creationDate);

}

}

我們把屬性的鍵傳給? -enumeratorAtURL: 方法中,在遍歷目錄內(nèi)容時,這個方法能確保用非常高效的方式獲取它們。在循環(huán)中,調(diào)用 -getResourceValue:… 能簡單地從 NSURL 得到已緩存的值,而不用去訪問文件系統(tǒng)。

傳遞路徑到UNIX API

因為 Unicode 非常復(fù)雜,同一個字母有多種表示方式,所以我們需要很小心地傳遞路徑給UNIX API。在這些情況里,一定不能使用 UTF8String ,正確地做法是使用 -fileSystemRepresentation 方法,如下:

NSURL *documentURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory

inDomain:NSUserDomainMask

appropriateForURL:nil

create:NO

error:NULL];

documentURL = [documentURL URLByAppendingPathComponent:name];

int fd = open(documentURL.fileSystemRepresentation, O_RDONLY);

與 NSURL 類似,同樣的情況也發(fā)生在 NSString 上。如果我們不這么做,在打開一個文件名或路徑名包含合成字符的文件時我們將看到隨機錯誤。在 OS X 上,當(dāng)用戶的短名剛好包含合成字符時就會顯得特別糟糕。

我們需要一個 char const * 版本的路徑的一些常見情況是UNIX open() 和 close() 指令。但這也可能發(fā)生在 GCD / libdispatch 的 I/O API 上。

dispatch_io_t

dispatch_io_create_with_path(dispatch_io_type_t type,

const char *path, int oflag, mode_t mode,

dispatch_queue_t queue,

void (^cleanup_handler)(int error));

如果我們要使用 NSString 來做,那我們要保證像下面這樣做:

NSString *path = ... // 假設(shè)我們已經(jīng)有一個名為 path 的字符串

io = dispatch_io_create_with_path(DISPATCH_IO_STREAM,

path.fileSystemRepresentation,

O_RDONLY, 0, queue, cleanupHandler);

-fileSystemRepresentation 所做的是它首先將這個字符串轉(zhuǎn)換成文件系統(tǒng)的規(guī)范形式然后用UTF-8編碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容