正确构建NSFastEnumeration类

时间:2010-08-12 13:48:41

标签: objective-c cocoa

对于Objective-C,我绝对是初学者!任何帮助都将非常感激。

这段代码对我有用,但我觉得将来肯定会对我有所影响。例如,如果有人在for循环中调用autorelease drain,该怎么办?另外,itemPtr和stackbuf之间的区别是什么? Apple网站上的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:         } else if (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 个答案:

答案 0 :(得分:1)

奇;我确信在The Objective-C Programming Language中曾经有过更全面的文档。无论如何,要回答您的具体问题:state->itemsPtr是您放置实际结果的地方。 stackbuflen提供了可用于此目的的临时空间,但您无需这样做。例如,如果您的集合使用直接C对象引用数组,则可以直接将其放在state->itemsPtr中,从而一次性返回所有对象。

至于你的实施:

  • 查看stackbuf的初始内容永远不会有用。
  • 使用malloc()而不是stackbuf缓冲区。使用len代替硬编码16。
  • “如果调用在for循环中使用break,会调用它吗?”不,它不会。没有可靠的清理机会。
  • 正如您所说,调用者可能会在循环中耗尽自动释放池。在这种情况下,没有一种完全“安全”的方式来处理临时对象。您所能做的就是记录调用者不得在迭代内消耗迭代之外创建的池。在实践中,这不太可能是一个问题。