iPad后台线程NSAutoReleasePool关于[对象发布]的困惑

时间:2010-09-04 21:17:12

标签: iphone objective-c ipad

我有一个应用程序,我导入一堆数据,我想在后台线程上发生。这对于少量数据运行良好,但现在看来我在解析并将大量数据导入Core Data的过程中遇到了这个错误:

Program received signal:  “EXC_BAD_ACCESS”.

以下是对后台主题的调用:

[self performSelectorInBackground:@selector(importAllData) withObject:nil];

以下是我在代码中所做的一个示例:

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

    // load manufactuers from DB
    NSFetchRequest *crequest    = [[NSFetchRequest alloc] init];
    NSEntityDescription *manufacturer = [NSEntityDescription entityForName:@"Manufacturer" inManagedObjectContext:managedObjectContext];
    [crequest setEntity:manufacturer];
    NSError *cerror=nil;;
    NSArray *manufacturers = [[managedObjectContext executeFetchRequest:crequest error:&cerror]mutableCopy];
    [crequest release];

for (int m=0; m < [manufacturers count]; m++) { 
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:kClientListURL, [[manufacturers objectAtIndex:m]ManufacturerID]]];

            ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
            [request setNumberOfTimesToRetryOnTimeout:2];
            [request startSynchronous];
            NSError *error = [request error];
            if (!error) {

                NSString *responseString = [request responseString];
                NSArray *items = [responseString JSONValue];

                NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
                NSNumberFormatter *dec  = [[NSNumberFormatter alloc]init];
                [dec setNumberStyle:NSNumberFormatterDecimalStyle];

                for (int i = 0; i < [items count]; i++) 
                {

                    Client *entity = (Client*) [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext];
                    [entity setCompanyName:[[items objectAtIndex:i] objectForKey:@"CompanyName"]];
                    // set a bunch of other properties
                    [entity setManufacturer:[manufacturers objectAtIndex:m]];

                    statusMessage = [NSString stringWithFormat:@"importing client: %@", entity.CompanyName];
                    [self performSelectorOnMainThread:@selector(setStatus) withObject:nil waitUntilDone:YES];
                }

                [f release];
                [dec release];

            } else {
                NSLog(@"%@",[NSString stringWithFormat:@"JSON parsing failed: %@", [error localizedDescription]]);
            }

            NSError *entityerror;
            if (![managedObjectContext save:&entityerror]) {
                //  //Handle the error.
                NSLog(@"\n\n\n Error saving clients: %@ \n\n\n\n",entityerror);
            }
    }
    //More data importing code

    [pool release];

正如我所说,这对于少量数据工作正常但现在在模拟器中它会在随机点引发错误的访问错误。

然后我读到这篇困惑我的文章: http://www.cocoadev.com/index.pl?DebuggingAutorelease

如果它们在NSAutoReleasePool中,我是否需要释放它?我是否会让他们在某个时候被释放两次?

如果删除发布语句,使用Command-Shift-A构建时会出现潜在的泄漏错误?

2 个答案:

答案 0 :(得分:1)

规则是:

  • 如果您是所有者,则需要致电[object release][object autorelease]。当池被刷新或释放时,后者会将释放调用委托给NSAutoReleasePool。
  • 如果您致电[object retain],或者您通过名称中包含alloccopy的方法获得了对象,则您是所有者。
  • 如果您从名称中既没有alloc也不copy的方法中获取对象而您没有保留它,则不得致电releaseautorelease在它上面(除了非常罕见的情况,文件说明你 所有者)。

所以,是的,如果你有一个自动释放的对象并且你在它上面调用release,你就会过度释放它。效果各不相同,但几乎总是导致崩溃,通常使用EXC_BAD_ACCESS。您可以通过设置NSZombieEnabled environment variable来调试它。这样,每当一个对象被释放时,它就被一个虚拟代替,该虚拟代码知道该对象之前存在什么对象,然后可以告诉你哪个对象被过度释放。

但是通过你的代码阅读我并没有发现任何直接明显的问题,但我不知道Core Data(你似乎正在使用它)。另一种可能性是线程/时序问题。另外,你说那不是整个方法吗?也许错误在缺失部分。

答案 1 :(得分:0)

我假设importAllData是您正在使用的方法。如果是这样,你就错过了:在通话结束时。

[self performSelectorInBackground:@selector(importAllData) withObject:nil];

应该是

[self performSelectorInBackground:@selector(importAllData:) withObject:nil];

希望有所帮助!