2010-08-12 58 views
0

我絕對是Objective-C的初學者!任何幫助將非常感激。NSFastEnumeration類的正確構建

這段代碼對我很有用,但我真的覺得它在將來肯定會炸燬我。例如,如果某人在for循環的中間調用autorelease drain,該怎麼辦。另外,itemPtr和stackbuf之間的區別是什麼?在蘋果網站NSFastEnumeration的文檔非常薄弱,我的代碼是不是表現爲描述:

stackbuf 
    A C array of objects over which the sender is to iterate. 
itemsPtr 
    A C array of objects

這不是非常有幫助。我只使用itemsPtr,它的工作原理。我應該怎樣處理stackbuf,以及如何處理stackbuf和itemsPtr的內存分配/取消分配?我在macosx-dev上閱讀了這個討論(2009年10月)實現NSFastEnumeration,並且覺得我沒有任何想法正在發生。

所以...幫助!這是正確的嗎?我如何讓它變得更好?我應該用stackBuf做什麼?休息時如何避免麻煩?

代碼作爲源文件:http://vislab-ccom.unh.edu/~schwehr/Classes/2010/mbnutsandbolts/simple-fast-enum2.m(我共同教授這門課程在C++中,而是試圖爲自己做的一切在ObjC)

001: #import <Foundation/Foundation.h> 
002: #include <assert.h> 
003: 
004: @interface Datagram : NSObject 
005: { 
006:   int dgId; 
007: } 
008: -(id)initWithDatagramType:(int)datagramType; 
009: -(void)dealloc; 
010: -(NSString *)description; 
011: @property (readonly) int dgId; 
012: @end 
013: 
014: @implementation Datagram 
015: @synthesize dgId; 
016: - (NSString *)description { 
017:   return [NSString stringWithFormat: @"Datagram: dgId:", dgId]; 
018: } 
019: 
020: -(id)initWithDatagramType:(int)datagramType { 
021:   self = [super init]; 
022:   if (!self) return self; 
023:   dgId = datagramType; 
024:   return self; 
025: } 
026: 
027: -(void)dealloc { 
028:   NSLog(@"dealloc datagram: %d",dgId); 
029:   [super dealloc]; 
030: } 
031: @end 
032: 
033: // Pretend sequence of packet ID's coming from a sonar 
034: int testSeq[] = { 
035:   3, 12, 4, 19, 8, 
036:   2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 
037:   2, 2, 2, 2, 9, 
038:   2,2,2,2,9, 
039:   1,2,3,4,5,6,7,8,9, 
040:   11,12,13,14,15,16,17,18,19, 
041:   3, 
042:   0 // End of sequence/array sentinal 
043: }; 
044: 
045: @interface DatagramFile : NSObject <NSFastEnumeration> 
046: { 
047:  // No ivars 
048: } 
049: -(id)init; 
050: @end 
051: 
052: @implementation DatagramFile 
053: -(id)init { 
054:  self = [super init]; 
055:  if (!self) return self; 
056:  // NOP 
057:  return self; 
058: } 
059: 
060: - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len 
061: { 
062:   NSLog(@"In countByEnumeratingWithState: stackbuf: %p, count: %d", stackbuf, len); 
063:   NSLog(@"\t state struct: state=%d %p %p", state->state, state->itemsPtr, state->mutationsPtr); 
064:   if (stackbuf) { 
065:     NSLog(@"***INSPECTING STACKBUF\n"); 
066:     for(int i=0;i<1000 && stackbuf[i]!=0;i++) { 
067:       NSLog(@"Stackbuf %d: %p",i,stackbuf[i]); // What should I do with stackbuf[i]? 
068:     } 
069:   } 
070:   if (0 == state->state) {   
071:     NSLog(@"Initializing loop"); 
072:     assert(0==state->itemsPtr); 
073:     state->itemsPtr = malloc(sizeof(id)*16); 
074:     memset(state->itemsPtr, 0, sizeof(id)*16); 
075:   } elseif (0==len) { 
076:     // Will this get called if the call uses break inside the for loop? 
077:     NSLog(@"Finished loop. cleanup"); 
078:     free(state->itemsPtr); 
079:     state->itemsPtr = 0; 
080:   return 0; 
081:   } 
082:   state->mutationsPtr = (unsigned long *)self; // Tell the caller that the file has not changed 
083:   
084:   NSUInteger count=0; 
085:   for (; count < len && testSeq[state->state]!=0; count++, state->state++) { 
086:     NSLog(@"Creating datagram of type %d state: %d count %d",testSeq[state->state], state->state, count); 
087:     Datagram *dg = [[Datagram alloc] initWithDatagramType:testSeq[state->state]]; 
088:     state->itemsPtr[count] = dg; 
089:     [dg autorelease]; 
090:   } 
091:   NSLog(@"countByEnumeratingWithState read %d datagrams. state->state: %d",count, state->state); 
092:   return count; 
093: } 
094: @end // implementation DatagramFile 
095: 
096: int main (int argc, const char * argv[]) { 
097:  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
098:   
099:  DatagramFile *df = [[DatagramFile alloc] init]; 
100:  for (Datagram *dg in df) { 
101:   NSLog(@"About to read datagram in for"); 
102:   NSLog(@" Datagram type: %d", [dg dgId]); 
103:  } 
104:   
105:  NSLog(@"about to drain pool"); 
106:  [pool drain]; 
107:  NSLog(@"pool drained. ready for winter"); 
108:  return 0; 
109: }

回答

1

奇;我確定The Objective-C Programming Language曾經有過更全面的文檔。無論如何,要回答你的具體問題:state->itemsPtr是你把實際結果放在哪裏。 stackbuflen提供您可以用於此目的的臨時空間,但您不需要。例如,如果您的集合使用直接C對象引用數組,則可以直接將它放在state->itemsPtr中,從而一次返回所有對象。

至於你實現:

  • 這是看的stackbuf初始內容永遠有用。
  • 而不是malloc()緩衝區,使用stackbuf。使用len而不是硬編碼的16.
  • 「如果調用在for循環中使用break,它會被調用嗎?」不,它不會。沒有可靠的清理機會。
  • 正如你所說,調用者可能會在循環中消耗一個自動釋放池。在這種情況下,沒有完全「安全」的方式來處理臨時物體。你所能做的就是記錄調用者不應該在迭代中排除在迭代之外創建的池。在實踐中,這不太可能是一個問題。
+0

謝謝。我現在發佈了一個清潔版本的代碼: http://schwehr.org/blog/attachments/2010-08/using-NSFastEnumeration.m – 2010-08-12 15:54:37

+0

「直接C對象引用數組」的確切數據類型是?它是'unsigned long'的數組還是可以使用objective-c'id'類型實際創建一個c數組? – 2011-01-14 19:44:49

+0

@大衛:是的,你可以做到這一點。事實上,你必須實現'NSFastEnumeration'。這就是'stackBuf'和'state-> itemsPtr'。 – 2011-01-14 22:11:28