iOS - Thread不会回到主线程

时间:2012-05-03 12:15:50

标签: iphone objective-c ios multithreading objective-c-blocks

我的线程出现问题。 在线程忙碌之后,我在2个屏幕之间发生了几次。线程不执行每一行..,断点只是在必须返回主线程时消失。 有人可以帮帮我吗?

我在卸载视图时释放线程。

谢谢,

- (void)fetchFeedDataIntoDocument
{
    NSString * labelString = [NSString stringWithFormat:@"Feed Fetcher %@", self.pageTitle];
    const char *label = [labelString UTF8String];

    self.fetchF = dispatch_queue_create(label, NULL);
    dispatch_async(self.fetchF, ^{

        NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;

        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

        NSManagedObjectContext *context = [appDelegate getManagedObjectContext];

        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}

        dispatch_async(dispatch_get_main_queue(), ^{
            [self setupFetchedResultsController];
            [self.tableView reloadData];
            [self downloadImagesForFeeds:feeds];
        });

    });

4 个答案:

答案 0 :(得分:11)

您正在从创建它的其他线程访问managedObjectContext。这是核心数据线程规则#1。

您正在从应用代表处获取MOC。如果它是正常的Xcode生成的MOC,那么它是使用线程限制并发创建的。你甚至不能用它调用performBlock。您只能从主线程访问该MOC。期。其他任何事情都充满了火,充其量。

如果您想在单独的线程中完成所有工作,您还需要一个单独的MOC。像这样(只是键入 - 未编译)......

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.parentContext = appDelegate.managedObjectContext;
[moc performBlock:^{
    // Go get your remote data and whatever you want to do

    // Calling save on this MOC will push the data up into the "main" MOC
    // (though it is now in the main MOC it has not been saved to the store).
    [moc save:&error];
}];

哪会转化为这样......

- (void)fetchFeedDataIntoDocument
{
    NSString * labelString = [NSString stringWithFormat:@"Feed Fetcher %@", self.pageTitle];
    const char *label = [labelString UTF8String];

    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *mainContext = [appDelegate getManagedObjectContext];
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    context.parentContext = mainContext;
    [context performBlock:^{    
        NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;


        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}
DO you really want to continue on error?
        dispatch_async(dispatch_get_main_queue(), ^{
            // Data has been pushed into main context from the background
            // but it still needs to be saved to store...
            // Do not forget to perform error handling...
            NSError *error = nil;
            [mainContext save:&error];
            [self setupFetchedResultsController];
            [self.tableView reloadData];
            [self downloadImagesForFeeds:feeds];
        });

    });

修改

Xcode生成的用于创建MOC的代码使用init,它使用NSConfinementConcurrencyType。您可以使用MainConcurrency替换它,没有任何问题,但可以获得多种好处。

在您的app委托文件中,替换...

    __managedObjectContext = [[NSManagedObjectContext alloc] init];

有了......

    __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

现在,您的主要MOC可以是“父级”,您也可以在其上调用performBlock。

答案 1 :(得分:0)

更改以下代码..

   dispatch_queue_t queue1 = dispatch_queue_create("com.MyApp.AppTask",NULL);
   dispatch_queue_t main = dispatch_get_main_queue();
   dispatch_async(queue1, 
               ^{
                   dispatch_async(main, 
                                  ^{
                                      [self setupFetchedResultsController];
                                      [self.tableView reloadData];
                                      [self downloadImagesForFeeds:feeds];

                                  });

               });  

希望,这会对你有所帮助

答案 2 :(得分:0)

这样做...

-(void)functionToCallFetch {

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

} 

- (void)fetchFeedDataIntoDocument
{

     NSArray *feeds = [FeedFetcher getDataForJson:self.pageTitle downloadBy:@"up"];

        NSDictionary *lastfeed;

        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

        NSManagedObjectContext *context = [appDelegate getManagedObjectContext];

        if ([feeds count] > 0)
        {
            lastfeed = [feeds objectAtIndex:0];

            [FeedFetcher setLastUpdateIdToCatgorie:self.pageTitle WithId:[lastfeed objectForKey:@"id"] AndPulishDate:[lastfeed objectForKey:@"publish_up"]];
        }

        for (NSDictionary *feedInfo in feeds) {
            [Feed FeedWithInfo:feedInfo InManageObject:context];
        }

        NSError *error = nil;

        [context save:&error];

        if (error){
            NSLog(@"Error save : %@", error);}

        //dispatch_async(dispatch_get_main_queue(), ^{
        //    [self setupFetchedResultsController];
        //    [self.tableView reloadData];
        //    [self downloadImagesForFeeds:feeds];
        //});
        [self performSelectorOnMainThread:@selector(setupFetchedResultsController) withObject:nil waitUntilDone:NO];
        [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
        [self performSelectorOnMainThread:@selector(downloadImagesForFeeds:) withObject:feeds waitUntilDone:NO];


}

也许这会更好用吗?

答案 3 :(得分:0)

您是否尝试构建如下方法:

- (void)methodToBePerformedOnMainThread{
        [self setupFetchedResultsController];
        [self.tableView reloadData];
        [self downloadImagesForFeeds:feeds];
}

并用

调用它
[self performSelectorOnMainThread:@selector(methodToBePerformedOnMainThread) withObject:nil waitUntilDone:NO];

fetchFeedDataIntoDocument

的末尾

编辑:

您是否尝试用NSOperationQueue代替dispatch_queue来包装它?

喜欢:

NSOperationQueue *operationQueue = [NSOperationQueue new];

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fetchFeedDataIntoDocument) object:nil];

if(operation != nil){
    [operationQueue addOperation:operation];