Iphone 在分离的NSThread中分配内存以在后台加载NSDictionary?

Iphone 在分离的NSThread中分配内存以在后台加载NSDictionary?,iphone,nsthread,nsautoreleasepool,Iphone,Nsthread,Nsautoreleasepool,我正在尝试启动一个后台线程来从web服务检索XML数据。我同步开发了它-没有线程,所以我知道该部分可以工作。现在,我已经准备好创建一个非阻塞服务,方法是生成一个线程来等待响应和解析 我在线程内创建了一个NSAutoreleasePool,并在解析结束时释放它。要生成的代码和线程如下所示: 从主循环代码生成: . . [NSThread detachNewThreadSelector:@selector(spawnRequestThread:)

我正在尝试启动一个后台线程来从web服务检索XML数据。我同步开发了它-没有线程,所以我知道该部分可以工作。现在,我已经准备好创建一个非阻塞服务,方法是生成一个线程来等待响应和解析

我在线程内创建了一个NSAutoreleasePool,并在解析结束时释放它。要生成的代码和线程如下所示:

从主循环代码生成:

 .
 .
 [NSThread detachNewThreadSelector:@selector(spawnRequestThread:) 
                          toTarget:self withObject:url];
 .
 .
-(void) spawnRequestThread: (NSURL*) url  {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

 parser = [[NSXMLParser alloc] initWithContentsOfURL:url];

 [self parseContentsOfResponse];

 [parser release];
 [pool release];
}
线程(在“self”内部)

 .
 .
 [NSThread detachNewThreadSelector:@selector(spawnRequestThread:) 
                          toTarget:self withObject:url];
 .
 .
-(void) spawnRequestThread: (NSURL*) url  {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

 parser = [[NSXMLParser alloc] initWithContentsOfURL:url];

 [self parseContentsOfResponse];

 [parser release];
 [pool release];
}
方法
parseContentsOfResponse
用解析的文档内容填充NSMutableDictionary。我希望避免大量移动数据,并将其分配回产生线程的主循环中,而不是复制。首先,这是可能的,如果不是,我可以简单地从主线程传入一个分配的指针,并使用“dictionaryWithDictionary”方法进行分配吗?这看起来太没效率了

parseContentsOfResponse

-(void)parseContentsOfResponse {

    [parser setDelegate:self];
    [parser setShouldProcessNamespaces:YES];
    [parser setShouldReportNamespacePrefixes:YES];

    [parser parse];

    NSError *parseError = [parser parserError];
    if (parseError) {
        NSLog(@"%@", parseError);
        NSLog(@"publicID: %@", [parser publicID]);
        NSLog(@"systemID: %@", [parser systemID]);
        NSLog(@"line:%d column:%d", [parser lineNumber], [parser columnNumber]);
    }

    ready = YES;
}
第一个解析部分

每个节在其elementStart发出信号时创建元素字符串。elementEnd将把对象添加到字典中并释放元素。剩下的细节是多余的,我认为需要注意的是,分配不是针对NSZone的,因此它们应该驻留在线程的内存池中

- (void)parserDidStartDocument:(NSXMLParser *)parser {
    NSLog(@"%s", __FUNCTION__);
    currentChars    = [NSMutableString stringWithString:@""];
    elementQuestion = [NSMutableString stringWithString:@""];
    elementAnswer   = [NSMutableString stringWithString:@""];
    elementKeyword  = [NSMutableString stringWithString:@""];
}

最简单的方法是在单独的线程中创建字典,然后将其设置为主线程上的属性,如下所示:

- (void)spawnRequestThread: (NSURL*) url  {
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    //do stuff with dict
    [self performSelectorOnMainThread:@selector(doneWithThread:) withObject:dict waitUntilDone:NO];
}

- (void)doneWithThread:(NSDictionary *)theDict {
    self.dict = theDict; //retaining property, can be an NSDictionary
}
你需要随时间改变字典的内容吗?如果是这样,在主线程上分配并更改其他线程中的内容是可能的,但您必须担心线程安全问题--
NSMutableDictionary
不是线程安全的,因此您必须使用原子属性和锁:

//.h
@property (retain) NSMutableDictionary *dict; //don't use "nonatomic" keyword
@property (retain) NSLock *dictLock;

//.m
- (id) init {
    //blah blah
    dict = [[NSMutableDictionary alloc] init];
    dictLock = [[NSLock alloc] init];
    return self;
}

- (void)spawnRequestThread: (NSURL*) url  {
    //whenever you have to update the dictionary
    [self.dictLock lock];
    [self.dict setValue:foo forKey:bar];
    [self.dictLock unlock];
}
锁定在任何情况下都非常昂贵且效率低下,因此我倾向于选择第一种方法(确切地说,我不确定哪种方法更昂贵,但第一种方法更简单,可以避免线程安全问题)


另外,看看您的代码,您的
NSXMLParser
似乎是一个可以直接访问的ivar。这是一个坏主意,因为
NSXMLParser
——我建议将其作为局部变量实现。如果确实需要它作为ivar,请使用原子属性和锁,并且只能通过访问器访问它

你能展示更多的代码吗?您说过
parseContentsOfResponse
移动数据,但您没有显示它。我为文档添加了parseContents和第一个“元素解析”。剩下的基本上是锅炉板编码,并将结果元素填充到字典中,以节点的名称作为键。太好了——因此performSelectorOnMainThread将使我从后台线程返回到主线程,这样我就可以将字典更新存储到它的内存空间中——这是对您的正确理解吗推荐?是的,我最终会对字典进行“更新”。目前,它只是应用程序开始时的一个blob。下一步是保存到用户空间,重新加载并检查更新。因此,我必须管理锁。关于NSXMLParser作为ivar的好建议。我想通过对线程模型的重构,我可以很容易地将它放在本地范围内。同时,我只使用访问器——但我不能相信自己在将来修改应用程序时会记住这一点:)是的,
performselectornmainthread
将您放回主线程(所有线程的内存空间都是相同的,但有些操作对于主线程以外的线程来说是不安全的,例如访问非线程安全对象或更新UI)。想想看,答案中的第二种方法仍然不安全——无论何时更改字典的内容,都必须实现锁定(我将更新这个示例)。该死——现在我不得不想:(我真的想避免锁定并依赖模态视图和原子“就绪”标志。这非常有帮助,也很有教育意义。我希望你的答案得到很多赞成票。