2012-04-17 37 views
5

如何通過拉出每個unichar來枚舉NSString?我可以使用characterAtIndex,但比通過增加unichar *來做要慢。我沒有在Apple的文檔中看到任何不需要將字符串複製到第二個緩衝區的內容。通過指針枚舉NSString字符

像這樣的理想:

for (unichar c in string) { ... } 

unichar* ptr = (unichar*)string; 
+0

如果你擔心性能,你最好使用NSData並訪問它的字節數組。 – joerick 2012-04-17 21:04:09

+0

事實證明,CFString實際上有辦法做到這一點,在CFStringGetCharactersPtr ... – 2012-04-17 21:11:36

+2

「...但這將比......慢」 - 這稱爲**過早優化**。甚至在你知道性能是否會成爲問題之前,你都在對性能做出假設。您應該以顯而易見的方式實現它(使用'characterAtIndex')並且只有在出現性能問題時纔對其進行優化。 – Sulthan 2013-07-31 16:08:00

回答

11

您可以將其轉換爲它加快-characterAtIndex:是IMP形式第一:

NSString *str = @"This is a test"; 

NSUInteger len = [str length]; // only calling [str length] once speeds up the process as well 
SEL sel = @selector(characterAtIndex:); 

// using typeof to save my fingers from typing more 
unichar (*charAtIdx)(id, SEL, NSUInteger) = (typeof(charAtIdx)) [str methodForSelector:sel]; 

for (int i = 0; i < len; i++) { 
    unichar c = charAtIdx(str, sel, i); 
    // do something with C 
    NSLog(@"%C", c); 
} 

編輯:看來,CFString Reference包含以下方法:

const UniChar *CFStringGetCharactersPtr(CFStringRef theString); 

這意味着你可以做如下:

const unichar *chars = CFStringGetCharactersPtr((__bridge CFStringRef) theString); 

while (*chars) 
{ 
    // do something with *chars 
    chars++; 
} 

如果你不想分配m埃默裏爲應對緩衝區,這是要走的路。

+0

好的發現,但從返回值部分:「一個指向Unicode字符的緩衝區的指針,或者如果該字符串的內部存儲不允許有效地返回它,則爲NULL。這將是最快的,但仍然需要備份以防萬一。 – ughoavgfhw 2012-04-17 21:23:25

+0

輝煌,我沒有想到使用CF ... API,但這是一個好主意。工作出色。 – jjxtra 2012-04-17 21:33:46

+0

@ughoavgfhw真的,非常真實,它確實需要備份。但是對於OP想要的,這應該可以正常工作。 – 2012-04-17 21:34:30

0

這將工作:

char *s = [string UTF8String]; 
for (char *t = s; *t; t++) 
    /* use as */ *t; 

[編輯]如果你真的需要Unicode字符,那麼你有沒有選擇,只能使用長度characterAtIndex。從文檔:

NSString類有兩個基本方法 - 長度和characterAtIndex: - 爲其接口中的所有其他方法提供基礎。 length方法返回字符串中的Unicode字符總數。 characterAtIndex:可以訪問每一個字符由索引字符串中,以指數值從0開始。

所以,你的代碼是:

for (int index = 0; index < string.length; index++) 
    { 
     unichar c = [string characterAtIndex: index]; 
     /* ... */ 
    } 

[編輯2]

另外,不要不要忘記,NSString是'免費橋接'到CFString,因此所有非Objective-C,直接的C代碼接口函數都可用。相關的將是CFStringGetCharacterAtIndex

+0

這隻適用於小於128的unicode代碼點。只要遇到高位字符,它就會中斷。另外,它很可能會創建數據的第二個副本,提交者正試圖避免。 – grahamparks 2012-04-17 20:55:48

+0

我認爲這需要以某種方式複製utf-8字節?那個指針在哪裏生活?下面是NSString utf-8嗎? – jjxtra 2012-04-17 20:55:52

+0

C字符串已創建。 UTF8String的文檔:返回的C字符串會自動釋放,就像返回的對象被釋放一樣。如果需要將C字符串存儲在創建C字符串的autorelease上下文之外,則應該複製該C字符串._ – GoZoner 2012-04-17 21:19:17

0

我不認爲你可以做到這一點。 NSString是許多類的抽象接口,它們不保證字符數據的內部存儲,所以完全有可能沒有字符數組來獲取指針。

如果您的問題中提到的兩個選項都不適合您的應用,我建議您爲此創建自己的字符串類,或者使用原始malloc'ed unichar數組而不是字符串對象。

4

您唯一的選擇是將字符複製到新的緩衝區中。這是因爲NSString類不能保證可以使用內部緩衝區。最好的方法是使用getCharacters:range:方法。

如果您使用的可能非常長的字符串,它會更好地分配一個固定大小的緩衝區和枚舉塊字符串(這實際上是如何快速列舉的作品)。

+0

嗯。我想知道characterAtIndex是否更快,因爲它不需要複製內存......想法? – jjxtra 2012-04-17 20:57:40

+3

這是可能的,但不太可能。隨着緩衝區大小的增加,爲每個字符調用方法的開銷將快速傳遞寫入內存的開銷。除非你使用的自定義NSString類不提供優化的'getCharacters:range:'方法。 – ughoavgfhw 2012-04-17 21:00:34

+0

@PsychoDad如果繞過了objc運行時的開銷,並簡單地使用C函數,我會認爲使用'-characterAtIndex:'*可以更快。 – 2012-04-17 21:06:33

1

我按照ughoavgfhw在他的回答中的建議,創建了一個塊風格的枚舉方法,該方法使用getCharacters:range:和固定大小的緩衝區。它避免了CFStringGetCharactersPtr返回null並且不需要malloc大緩衝區的情況。你可以把它放到一個NSString類別中,或者修改它以將字符串作爲參數。

-(void)enumerateCharactersWithBlock:(void (^)(unichar, NSUInteger, BOOL *))block 
{ 
    const NSInteger bufferSize = 16; 
    const NSInteger length = [self length]; 
    unichar buffer[bufferSize]; 
    NSInteger bufferLoops = (length - 1)/bufferSize + 1; 
    BOOL stop = NO; 
    for (int i = 0; i < bufferLoops; i++) { 
     NSInteger bufferOffset = i * bufferSize; 
     NSInteger charsInBuffer = MIN(length - bufferOffset, bufferSize); 
     [self getCharacters:buffer range:NSMakeRange(bufferOffset, charsInBuffer)]; 
     for (int j = 0; j < charsInBuffer; j++) { 
      block(buffer[j], j + bufferOffset, &stop); 
      if (stop) { 
       return; 
      } 
     } 
    } 
} 
+0

這個工作,但不會像原始指針迭代一樣快 – jjxtra 2014-02-20 22:20:10

+0

是的,但正如我所說,這處理了CFStringGetCharactersPtr返回null的情況。 – Aaron 2014-02-20 22:29:34