有人知道这里是怎么回事吗?Core Data / iCloud

5

这里发生的情况是应用程序刚刚接收到了 NSPersistentStoreDidImportUbiquitousContentChangesNotification 通知,我刚刚调用了

- (void)storesDidUpdate:(NSNotification*)note {
    FLOG(@"storesDidUpdate ");
    [_managedObjectContext mergeChangesFromContextDidSaveNotification:note];

    // Refresh user Interface
    [[NSNotificationCenter defaultCenter] postNotificationName:@"iProjectCoreDataUpdated"
                                                        object:self];

    [self updateDetails];
}

现在该应用程序正在尝试通过简单地执行以下操作的方式,使用新的文本副本更新UITextView:

self.detailText.attributedText  = [self.detailItem valueForKey:@"scope"];

detailItem是在从iCloud接收更新之前检索的NSManagedObject。

不确定为什么textContainer在抱怨或抱怨什么,也不确定为什么导入日志正在发生,因为我已经收到了完成通知!

奇怪的是,如果我只调用reloadData,其他UITableView会正确更新。

您有任何想法,是否我在这里做错了什么?请注意,如果我不尝试更新textView,则它可以正常工作,如果我关闭视图并返回到它,则会获得正确的数据。

此外,当我重新启动应用程序时,所有数据都在那里,没有损坏或任何问题,事实上,当涉及到Core Data存储时,该应用程序在大多数方面似乎异常稳健,尽管iCloud两侧的事情都会爆炸!

哦,而reloadFetchedResults有点误导,因为我似乎不需要那样做,所以方法名称是过去的遗留物,在此调用中,我只是刷新UI中的值。

2013年10月9日07:25:53.783 MyApp[4509:510b] OpeningViewController.reloadFetchedResults:已调用 2013年10月9日07:25:53.786 MyApp[4509:510b] InfoDetailViewController.reloadFetchedResults:在InfoDetailViewController中已调用 2013年10月9日07:25:53.788 MyApp[4509:510b] * 在/SourceCache/UIFoundation/UIFoundation-258/UIFoundation/TextSystem/NSLayoutManager_Private.m:1510中void _UIPerformResizeOfTextViewForTextContainer(NSLayoutManager *, UIView *, NSTextContainer *, NSUInteger)()中的断言失败 2013年10月9日07:25:53.793 MyApp[4509:510b] -_PFUbiquityRecordImportOperation main:CoreData:Ubiquity:错误导入事务日志:      transactionLogLocation::/var/mobile/Library/Mobile Documents/HHHHHHNNNN〜com〜mycompany〜MyApp/CoreData/New Document/duncangroenewald〜simAABC628E-9D5E-58F7-9B8D-0BC724C6D0C8/New Document/W8MckEJ0x2d〜HZZIeUH2be6hs41TEOONzKIrCuLcuP4=/6C953E7C-B2AF-47F7-B828-DD062B96415D.1.cdt      transactionNumber:5 ,异常:仅在主线程上运行!    用户信息:(null) 2013年10月9日07:25:53.803 MyApp[4509:510b] -_PFUbiquityRecordsImporter操作:failedWithError :: CoreData:Ubiquity:导入操作遇到错误:Error Domain = NSCocoaErrorDomain Code = 134060“无法完成操作。 (Cocoa错误134060。)”UserInfo = 0x16d882c0 {exception = Only run on the main thread!}   userInfo:{      exception =“仅在主线程上运行!”;   }。 尝试导入URL处的日志文件:      transactionLogLocation::/var/mobile/Library/Mobile Documents/HHHHHHNNNN〜com〜mycompany〜MyApp/CoreData/New Document/duncangroenewald〜simAABC628E-9D5E-58F7-9B8D-0BC724C6D0C8/New Document/W8MckEJ0x2d〜HZZIeUH2be6hs41TEOONzKIrCuLcuP4=/6C953E7C-B2AF-47F7-B828-DD062B96415D.1.cdt      transactionNumber:5 2013年10月9日07:25:53.809 MyApp[4509:510b] *由于未捕获的异常'NSInternalInconsistencyException',原因:'仅在主线程上运行!' ***第一个抛出调用堆栈: (0x2ff23f53 0x3a6996af 0x2ff23e2d 0x308cb1df 0x3796643d 0x37966185 0x3798f7bb 0x379926f7 0x37992759 0x379b378b 0x379b331f 0x379b2f0d 0x3273b05d 0x129717 0x2fee6121 0x2fe5a317 0x3083edcd 0x308436ab 0x118263 0x2fee6121 0x2fe5a317 0x2fd8c69f 0x2fd8cc93 0x2fd813dd 0x3085197b 0x308f5b35 0x3ab83297 0x3ab8309b 0x3ab83d15 0x3ab83f8d 0x3acbedbf 0x3acbec84) libc ++ abi.dylib:以NSException类型终止未捕获的异常

编辑: 我在这里发布了一些iOS和OSX的Core Data/iCloud应用程序示例 - 它们包括用于加载/删除数据的后台线程以及iCloud同步。希望解决此处描述的问题。http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/


奇怪的是,在其他视图中,完全相同的代码似乎可以毫无问题地更新用户界面。 - Duncan Groenewald
很难确定,但错误信息反复显示“仅在主线程上运行!”这一事实可能是一个非常重要的线索,需要立即认真考虑。 - Tom Harrington
汤姆,请查看下面的附加信息... - Duncan Groenewald
4个回答

9

好的,这里是解决方法 - 我想我从未意识到接收通知会在另一个线程上运行。这是正常行为吗?你接收到的所有Core Data通知都在后台线程上调用吗?如果是,那么你是否总是需要确保任何操作都在正确的线程上运行?

- (void)storesDidUpdate:(NSNotification*)note {
    FLOG(@"storesDidUpdate ");
    [_managedObjectContext mergeChangesFromContextDidSaveNotification:note];

    [[NSOperationQueue mainQueue] addOperationWithBlock:^ {
        //Your code goes in here
        LOG(@" Main Thread Code");

        // Refresh user Interface
        [[NSNotificationCenter defaultCenter] postNotificationName:@"CoreDataUpdatedNotification"
                                                        object:self];
    }];

}

编辑:

我最终记录了如何相当可靠地处理Core Data和iCloud,包括响应用户更改其使用iCloud偏好设置而移动到和从iCloud的处理方式。当我尝试弄清如何让它工作时,我无法找到一个好的解释和可用的代码,所以我希望这对其他人有所帮助。附带是一个应用程序工作的视频,您可以看到它的行为,以帮助您确定是否达到了您想要实现的目标。

http://ossh.com.au/design-and-technology/software-development/


哦,我也应该在主线程上调用mergeChangesFromContext吗? - Duncan Groenewald
通知将在发布它们的线程上接收。对于大多数核心数据通知,这意味着您使用的任何线程都可以。但是,对于iCloud来说,情况并非总是如此。 - Tom Harrington
糟糕!谢谢Tom,现在一切似乎都很稳定。我仍在尝试破坏数据库,但尽管其他人说Core Data和iCloud不可靠,我还是没有成功。它已经可靠了吗,还是我还没有尽力? - Duncan Groenewald
在iOS 7中,它比以前更好,但我还没有测试足够的次数来确定它是否可靠。 - Tom Harrington
是的,NSNotifications 可以来自不同的线程,也可能来自你不拥有或不知道的线程。 - Cullen SUN
感谢您发布这篇文章。这并不是一个明显的线程问题,在我的情况下,我没有因为我正在进行的核心数据操作而遇到这个问题,而是因为我试图在主线程上执行需要完成的UI操作。但是我也遇到了相同的断言失败。 - broughten

3
我遇到了同样的问题。正如@John Rogers所解释的那样,这正是我的问题。
我创建了UITextView,并在后台调用了一个URL来获取一些数据,这些数据应该插入到我生成的UITextView中。但问题出在方法sendAsynchronousRequest上,在后台调用它会导致我的应用崩溃。
我通过在接收到数据时执行特定选择器来解决了这个问题。因此,也许您可以将您的方法调用插入到另一个方法中,并以这种方式调用它:
[self performSelectorOnMainThread:@selector(received:) 
                       withObject:data 
                    waitUntilDone:YES];

这样你就可以回到主线程了。


0

是的,我明白了,但我没有在任何线程上执行任何操作,所以肯定是其他原因。我正在更新UITextView,我猜可能是其中某些内容导致了失败。我不清楚这与ubiquityImporter有什么关系——也许能够解释下面的内容的人可以指点我正确的方向。我是否在错误的线程上运行了某些东西?

First throw call stack:
(
    0   CoreFoundation                      0x027c55e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x019238b6 objc_exception_throw + 44
    2   CoreFoundation                      0x027c5448 +[NSException raise:format:arguments:] + 136
    3   Foundation                          0x01062960 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 101
    4   UIFoundation                        0x01b33131 -[NSLayoutManager(NSPrivate) _resizeTextViewForTextContainer:] + 419
    5   UIFoundation                        0x01b32e16 -[NSLayoutManager(NSPrivate) _recalculateUsageForTextContainerAtIndex:] + 2083
    6   UIFoundation                        0x01b67675 _enableTextViewResizing + 234
    7   UIFoundation                        0x01b6b275 -[NSLayoutManager textStorage:edited:range:changeInLength:invalidatedRange:] + 688
    8   UIFoundation                        0x01b6b2fa -[NSLayoutManager processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:] + 82
    9   UIFoundation                        0x01b93d35 -[NSTextStorage _notifyEdited:range:changeInLength:invalidatedRange:] + 153
    10  UIFoundation                        0x01b93870 -[NSTextStorage processEditing] + 462
    11  UIFoundation                        0x01b93419 -[NSTextStorage endEditing] + 80
    12  UIFoundation                        0x01b934a3 -[NSTextStorage coordinateEditing:] + 66
    13  UIKit                               0x007f4f48 -[UITextView setAttributedText:] + 254
    14  MyApp                        0x00027d7a -[WBSDetailViewController displayItem] + 3146
    15  MyApp                        0x00028c6c -[WBSDetailViewController refreshUI:] + 156
    16  Foundation                          0x01088e39 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40
    17  CoreFoundation                      0x02821524 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
    18  CoreFoundation                      0x0277907b _CFXNotificationPost + 2859
    19  Foundation                          0x00fc2b91 -[NSNotificationCenter postNotificationName:object:userInfo:] + 98
    20  Foundation                          0x00fd206a -[NSNotificationCenter postNotificationName:object:] + 55
    21  MyApp                        0x00031095 -[BaseAppDelegate storesDidUpdate:] + 293
    22  Foundation                          0x01088e39 __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke + 40
    23  CoreFoundation                      0x02821524 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
    24  CoreFoundation                      0x0277907b _CFXNotificationPost + 2859
    25  Foundation                          0x00fddd7b -[NSNotificationCenter postNotification:] + 121
    26  CoreData                            0x0173c5f4 -[_PFUbiquityRecordsImporter postImportNotificationForStoreName:andLocalPeerID:withUserInfo:] + 1892
    27  CoreData                            0x0173cbc2 -[_PFUbiquityRecordsImporter operationDidFinish:] + 706
    28  CoreData                            0x0172e8ce -[_PFUbiquityRecordImportOperation main] + 15102
    29  Foundation                          0x0108aa69 -[__NSOperationInternal _start:] + 671
    30  Foundation                          0x01007798 -[NSOperation start] + 83
    31  Foundation                          0x0108cd34 __NSOQSchedule_f + 62
    32  libdispatch.dylib                   0x05b794b0 _dispatch_client_callout + 14
    33  libdispatch.dylib                   0x05b67088 _dispatch_queue_drain + 450
    34  libdispatch.dylib                   0x05b66e85 _dispatch_queue_invoke + 126
    35  libdispatch.dylib                   0x05b67e25 _dispatch_root_queue_drain + 83
    36  libdispatch.dylib                   0x05b6813d _dispatch_worker_thread2 + 39
    37  libsystem_pthread.dylib             0x05f05dab _pthread_wqthread + 336
    38  libsystem_pthread.dylib             0x05f09cce start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type NSException

当你遇到崩溃时,请注意在Xcode的左侧窗格中尝试调用方法的线程。如果它不是com.apple.main-thread,则必须使用GCD在主队列上调用您的代码。 - John Rogers

0

阅读所有答案+评论(以及您的修复)-我认为应该注意只能从主线程更新任何UI控件。

我假设[self updateDetails];调用了self.detailText.attributedText = [self.detailItem valueForKey:@"scope"];代码?...这是实际更新UI的代码吗?

如果是这样,那么您可以使用-performSelectorOnMainThread:执行对-updateDetails的调用,或者在-updateDetails中检查您是否正在主线程上执行([NSThread isMainThread])并在那里包装UI更新代码,以便调用者不关心他们从哪个线程调用-updateDetails。


正如您在我的回答中所看到的,现在我总是从主线程发布我的通知,这样任何UI更新代码都会在主线程上调用。我不再使用我发布的初始代码。现在,每个UIViewController都会监听我的通知(该通知在主线程上发布),然后调用一个方法-(void)displayDetails{},其中包含UI更新代码,例如上面提取的代码。 - Duncan Groenewald
是的,您应该能够包装更新代码,以便它始终在主线程上执行。但是,我不这样做,因为不仅 UI 更新必须在主线程上,还有任何来自主 managedObjectContext 的对象操作也必须在主线程上进行。 - Duncan Groenewald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接