iOS -线程不会回到主线程。

时间:2021-12-30 20:58:53

I having trouble with my threads. After i segue a couple of times between 2 screen when the thread is busy. The thread don't perform every line.., The breakpoint just disappear when it has to return to the main thread. Can somebody please help me ?

我的线有问题。当线程忙时,我在两个屏幕之间进行了几次segue。线程不能执行每一行。,当断点必须返回到主线程时就会消失。有人能帮我吗?

I release the thread when the view is unload.

在视图卸载时释放线程。

Thanks,

谢谢,

- (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 个解决方案

#1


11  

You are accessing the managedObjectContext from a different thread from where it was created. This is Core Data Threading Rule #1.

您正在从创建managedObjectContext的不同线程访问它。这是Core Data Threading Rule #1。

You are getting the MOC from the app delegate. If it's the normal Xcode-generated MOC, then it is created with thread-confinement concurrency. You can't even call performBlock with it. You can only access that MOC from the main thread. Period. Anything else is playing with fire, at best.

你从应用委托中获得MOC。如果它是常规的xcode生成的MOC,那么它是使用线程限制并发创建的。你甚至不能用它调用performBlock。您只能从主线程访问MOC。时期。其他的一切充其量也不过是玩火而已。

If you want to do all the work in a separate thread, you need a separate MOC as well. Like this (just typed - not compiled)...

如果您想在一个单独的线程中完成所有的工作,您还需要一个单独的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];
}];

Which would translate into something like this...

它可以转化成这样的东西……

- (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];
        });

    });

EDIT

编辑

The code generated by Xcode for creating the MOC uses init, which uses NSConfinementConcurrencyType. You can replace it with MainConcurrency, without any problems, but get several benefits.

Xcode生成的用于创建MOC的代码使用init,它使用NSConfinementConcurrencyType。您可以用mainconcurrent替换它,没有任何问题,但是有几个好处。

In your app delegate file, replace...

在应用委托文件中,替换…

    __managedObjectContext = [[NSManagedObjectContext alloc] init];

with this...

用这个……

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

Now, your main MOC can be "parented" and you can also call performBlock on it as well.

现在,您的主要MOC可以“代理”,您也可以在其上调用performBlock。

#2


0  

what about doing this...

这样做呢…

-(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];


}

Maybe that would work better?

也许那样会更好?

#3


0  

Change following code..

改变代码. .

   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];

                                  });

               });  

Hope, this will help you

希望这能对你有所帮助

#4


0  

Did you try building a method like :

你试过建立一种方法吗?

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

and call it with

并调用它

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

at the end of fetchFeedDataIntoDocument

在fetchFeedDataIntoDocument的末尾

Edit :

编辑:

Did you try to wrap it with NSOperationQueue in place of dispatch_queue ?

你有没有尝试用NSOperationQueue代替dispatch_queue来包装它?

Like :

如:

NSOperationQueue *operationQueue = [NSOperationQueue new];

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

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

#1


11  

You are accessing the managedObjectContext from a different thread from where it was created. This is Core Data Threading Rule #1.

您正在从创建managedObjectContext的不同线程访问它。这是Core Data Threading Rule #1。

You are getting the MOC from the app delegate. If it's the normal Xcode-generated MOC, then it is created with thread-confinement concurrency. You can't even call performBlock with it. You can only access that MOC from the main thread. Period. Anything else is playing with fire, at best.

你从应用委托中获得MOC。如果它是常规的xcode生成的MOC,那么它是使用线程限制并发创建的。你甚至不能用它调用performBlock。您只能从主线程访问MOC。时期。其他的一切充其量也不过是玩火而已。

If you want to do all the work in a separate thread, you need a separate MOC as well. Like this (just typed - not compiled)...

如果您想在一个单独的线程中完成所有的工作,您还需要一个单独的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];
}];

Which would translate into something like this...

它可以转化成这样的东西……

- (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];
        });

    });

EDIT

编辑

The code generated by Xcode for creating the MOC uses init, which uses NSConfinementConcurrencyType. You can replace it with MainConcurrency, without any problems, but get several benefits.

Xcode生成的用于创建MOC的代码使用init,它使用NSConfinementConcurrencyType。您可以用mainconcurrent替换它,没有任何问题,但是有几个好处。

In your app delegate file, replace...

在应用委托文件中,替换…

    __managedObjectContext = [[NSManagedObjectContext alloc] init];

with this...

用这个……

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

Now, your main MOC can be "parented" and you can also call performBlock on it as well.

现在,您的主要MOC可以“代理”,您也可以在其上调用performBlock。

#2


0  

what about doing this...

这样做呢…

-(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];


}

Maybe that would work better?

也许那样会更好?

#3


0  

Change following code..

改变代码. .

   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];

                                  });

               });  

Hope, this will help you

希望这能对你有所帮助

#4


0  

Did you try building a method like :

你试过建立一种方法吗?

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

and call it with

并调用它

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

at the end of fetchFeedDataIntoDocument

在fetchFeedDataIntoDocument的末尾

Edit :

编辑:

Did you try to wrap it with NSOperationQueue in place of dispatch_queue ?

你有没有尝试用NSOperationQueue代替dispatch_queue来包装它?

Like :

如:

NSOperationQueue *operationQueue = [NSOperationQueue new];

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

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