2010-08-12 30 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