- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试实现这个核心数据栈:
PSC <--+-- MainMOC
|
+-- BackgroundPrivateMOC
有些事情我其实不明白。也许我们的 Persisten Store 中有一个对象,我们从主 MOC 中获取它以进行一些更改(用户手动更改它)。同时,我的 BG MOC 正在对同一个对象进行一些更改,并将更改保存到 PS。保存完成后,我们必须将 BG MOC 合并到 MAIN MOC(这是一种常见做法)。合并后我期望的是 MAIN MOC 包含来自 BG MOC 的更改(因为这些更改比 MAIN 晚了一点)。但这实际上并没有发生。合并完成后,我的 MAIN MOC 中只有一个脏的 refreshedObjects = 1
,如果我通过 MAIN MOC 再次获取该对象,我看不到通过 BG MOC 进行的任何更改。
refreshedObjects
的情况,以及如何将这些对象推送到 MAIN MOC 中以使其可供使用获取并使用?我相信我的示例代码可以帮助您更清楚地理解我的问题。您只需下载项目 ( https://www.dropbox.com/s/1qr50zto5j4hj40/ThreadedCoreData.zip?dl=0) 并运行我准备的 XCTest。
这是失败的测试代码:
@implementation ThrdCoreData_Tests
- (void)setUp
{
[super setUp];
/**
OUR SIMPLE STACK:
PSC <--+-- MainMOC
|
+-- BackgroundPrivateMOC
*/
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
// main context (Main queue)
_mainMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainMOC setPersistentStoreCoordinator:coordinator];
[_mainMOC setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
// background context (Private Queue)
_bgMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_bgMOC.persistentStoreCoordinator = self.persistentStoreCoordinator;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mergeBGChangesToMain:)
name:NSManagedObjectContextDidSaveNotification
object:_bgMOC];
u_int32_t value = arc4random_uniform(3000000000); // simply generate new random values for the test
_mainMOCVlaue = [NSString stringWithFormat:@"%u" , value];
_expectedBGValue = [NSString stringWithFormat:@"%u" , value/2];
Earthquake * mainEq = [Earthquake MR_findFirstInContext:self.mainMOC];
if (!mainEq){ // At the very first time the test is running, create one single test oject.
Earthquake * mainEq = [Earthquake MR_createEntityInContext:self.mainMOC];
mainEq.location = nil; // initial value will be nil
[self.mainMOC MR_saveOnlySelfAndWait];
}
}
- (void)testThatBGMOCSuccessfullyMergesWithMain
{
_expectation = [self expectationWithDescription:@"test finished"];
// lets change our single object in main MOC. I expect that the value will be later overwritten by `_expectedBGValue`
Earthquake * mainEq = [Earthquake MR_findFirstInContext:self.mainMOC];
NSLog(@"\nCurrently stored value:\n%@\nNew main value:\n%@", mainEq.location, _mainMOCVlaue);
mainEq.location = _mainMOCVlaue; // the test will succeed if this line commented
// now change that object in BG MOC by setting `_expectedBGValue`
[_bgMOC performBlockAndWait:^{
Earthquake * bgEq = [Earthquake MR_findFirstInContext:_bgMOC];
bgEq.location = _expectedBGValue;
NSLog(@"\nNew expected value set:\n%@", _expectedBGValue);
[_bgMOC MR_saveToPersistentStoreAndWait]; // this will trigger the `mergeBGChangesToMain` method
}];
[self waitForExpectationsWithTimeout:3 handler:nil];
}
- (void)mergeBGChangesToMain:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(), ^{
[self.mainMOC mergeChangesFromContextDidSaveNotification:notification];
// now after merge done, lets find our object with expected value `_expectedBGValue`:
Earthquake * expectedEQ = [Earthquake MR_findFirstByAttribute:@"location" withValue:_expectedBGValue inContext:self.mainMOC];
if (!expectedEQ){
Earthquake * eqFirst = [Earthquake MR_findFirstInContext:self.mainMOC];
NSLog(@"\nCurrent main MOC value is:\n%@\nexptected:\n%@", eqFirst.location, _expectedBGValue);
}
XCTAssert(expectedEQ != nil, @"Expected value not found");
[_expectation fulfill];
});
}
最佳答案
首先,在发布核心数据代码时,建议大家不要发布依赖第三方库的代码,除非第三方库与你的问题直接相关。我认为 MR 是一种神奇的记录,但我没有使用它,而且它似乎只是混淆了帖子的内容,因为谁知道它在幕后做什么(或不做什么)。
换句话说,尝试将示例缩减到尽可能少的代码……不要更多……并且只在绝对必要时才包含第三方库。
其次,在为核心数据使用编写单元测试时,我建议使用内存堆栈。您总是从空开始,并且可以根据需要对其进行初始化。更易于用于测试。
也就是说,您的问题是对 mergeChangesFromContextDidSaveNotification
做什么(和不做什么)的误解。
基本上,您在 Core Data 持久存储中有一个对象。您有两个不同的 MOC 通过同一个 PSC 连接到商店。
然后您的测试将对象加载到主 MOC 中,并在不保存到 PSC 的情况下更改值。然后第二个 MOC 加载同一个对象,并将其值更改为不同的值(即,存储,并且两个 MOC 都对同一对象的特定属性具有不同的值)。
现在,当我们保存 MOC 时,如果有冲突,冲突将按照 mergePolicy
的指示进行处理。但是,合并策略不适用于 mergeChangesFromContextDidSaveNotification
。
您可以将 mergeChangesFromContextDidSaveNotification
视为插入任何新对象、删除任何已删除的对象以及“刷新”任何更新的对象,同时保留任何本地更改。
在您的测试中,如果您添加另一个属性(例如“title”)并在 BG MOC 中更改“title”和“location”,但仅更改主 MOC 中的“location”,您将看到“title"按预期从 BG MOC 合并到主 MOC。
但是,正如您在问题中指出的那样,“位置”似乎没有合并。实际上,它确实被合并了,但是任何本地更改都会覆盖商店中的内容……这正是您想要发生的,因为用户可能进行了该更改,并且不希望它在背后被更改。
基本上,任何待处理的本地更改都将覆盖来自待合并 MOC 的更改。
如果你想要不同的东西,你必须在合并时实现那个行为,就像这样......
- (void)mergeBGChangesToMain:(NSNotification*)note {
NSMutableSet *updatedObjectIDs = [NSMutableSet set];
for (NSManagedObject *obj in [note.userInfo objectForKey:NSUpdatedObjectsKey]) {
[updatedObjectIDs addObject:[obj objectID]];
}
[_mainMOC performBlock:^{
for (NSManagedObject *obj in [_mainMOC updatedObjects]) {
if ([updatedObjectIDs containsObject:obj.objectID]) {
[_mainMOC refreshObject:obj mergeChanges:NO];
}
}
[_mainMOC mergeChangesFromContextDidSaveNotification:note];
}];
}
该代码首先收集在 merged-from-MOC 中更新的每个对象的 ObjectID
。
在进行合并之前,我们会查看合并到 MOC 中的每个更新对象。如果我们将一个对象合并到我们的 MOC 中,并且我们的 merge-to-MOC 也更改了该对象,那么我们希望允许 merged-from-MOC 中的值覆盖 merged-to-MOC 中的值。因此,我们从存储中刷新本地对象,基本上丢弃任何本地更改(有副作用,例如,导致对象成为错误,释放对任何关系的引用,并释放任何 transient 属性 - 请参阅 refreshObject:mergeChanges 的文档:).
考虑以下类别,它解决了您的情况,以及使用像 NSFetchedResultsController
这样的观察者时的常见问题。
@interface NSManagedObjectContext (WJHMerging)
- (void)mergeChangesIntoContext:(NSManagedObjectContext*)moc
withDidSaveNotification:(NSNotification*)notification
faultUpdatedObjects:(BOOL)faultUpdatedObjects
overrideLocalChanges:(BOOL)overrideLocalChanges
completion:(void(^)())completionBlock;
@end
@implementation NSManagedObjectContext (WJHMerging)
- (void)mergeChangesIntoContext:(NSManagedObjectContext *)moc
withDidSaveNotification:(NSNotification *)notification
faultUpdatedObjects:(BOOL)faultUpdatedObjects
overrideLocalChanges:(BOOL)overrideLocalChanges
completion:(void (^)())completionBlock {
NSAssert(self == notification.object, @"Not called with");
NSSet *updatedObjects = notification.userInfo[NSUpdatedObjectsKey];
NSMutableSet *updatedObjectIDs = nil;
if (overrideLocalChanges || faultUpdatedObjects) {
updatedObjectIDs = [NSMutableSet setWithCapacity:updatedObjects.count];
for (NSManagedObject *obj in updatedObjects) {
[updatedObjectIDs addObject:[obj objectID]];
}
}
[moc performBlock:^{
if (overrideLocalChanges) {
for (NSManagedObject *obj in [moc updatedObjects]) {
if ([updatedObjectIDs containsObject:obj.objectID]) {
[moc refreshObject:obj mergeChanges:NO];
}
}
}
if (faultUpdatedObjects) {
for (NSManagedObjectID *objectID in updatedObjectIDs) {
[[moc objectWithID:objectID] willAccessValueForKey:nil];
}
}
[moc mergeChangesFromContextDidSaveNotification:notification];
if (completionBlock) {
completionBlock();
}
}];
}
@end
关于ios - 多上下文核心数据 : merging to unsaved context,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28520133/
初学者 android 问题。好的,我已经成功写入文件。例如。 //获取文件名 String filename = getResources().getString(R.string.filename
我已经将相同的图像保存到/data/data/mypackage/img/中,现在我想显示这个全屏,我曾尝试使用 ACTION_VIEW 来显示 android 标准程序,但它不是从/data/dat
我正在使用Xcode 9,Swift 4。 我正在尝试使用以下代码从URL在ImageView中显示图像: func getImageFromUrl(sourceUrl: String) -> UII
我的 Ubuntu 安装 genymotion 有问题。主要是我无法调试我的数据库,因为通过 eclipse 中的 DBMS 和 shell 中的 adb 我无法查看/data/文件夹的内容。没有显示
我正在尝试用 PHP 发布一些 JSON 数据。但是出了点问题。 这是我的 html -- {% for x in sets %}
我观察到两种方法的结果不同。为什么是这样?我知道 lm 上发生了什么,但无法弄清楚 tslm 上发生了什么。 > library(forecast) > set.seed(2) > tts lm(t
我不确定为什么会这样!我有一个由 spring data elasticsearch 和 spring data jpa 使用的类,但是当我尝试运行我的应用程序时出现错误。 Error creatin
在 this vega 图表,如果我下载并转换 flare-dependencies.json使用以下 jq 到 csv命令, jq -r '(map(keys) | add | unique) as
我正在提交一个项目,我必须在其中创建一个带有表的 mysql 数据库。一切都在我这边进行,所以我只想检查如何将我所有的压缩文件发送给使用不同计算机的人。基本上,我如何为另一台计算机创建我的数据库文件,
我有一个应用程序可以将文本文件写入内部存储。我想仔细看看我的电脑。 我运行了 Toast.makeText 来显示路径,它说:/数据/数据/我的包 但是当我转到 Android Studio 的 An
我喜欢使用 Genymotion 模拟器以如此出色的速度加载 Android。它有非常好的速度,但仍然有一些不稳定的性能。 如何从 Eclipse 中的文件资源管理器访问 Genymotion 模拟器
我需要更改 Silverlight 中文本框的格式。数据通过 MVVM 绑定(bind)。 例如,有一个 int 属性,我将 1 添加到 setter 中的值并调用 OnPropertyChanged
我想向 Youtube Data API 提出请求,但我不需要访问任何用户信息。我只想浏览公共(public)视频并根据搜索词显示视频。 我可以在未经授权的情况下这样做吗? 最佳答案 YouTube
我已经设置了一个 Twilio 应用程序,我想向人们发送更新,但我不想回复单个文本。我只是想让他们在有问题时打电话。我一切正常,但我想在发送文本时显示传入文本,以确保我不会错过任何问题。我正在使用 p
我有一个带有表单的网站(目前它是纯 HTML,但我们正在切换到 JQuery)。流程是这样的: 接受用户的输入 --- 5 个整数 通过 REST 调用网络服务 在服务器端运行一些计算...并生成一个
假设我们有一个名为 configuration.js 的文件,当我们查看内部时,我们会看到: 'use strict'; var profile = { "project": "%Projec
这部分是对 Previous Question 的扩展我的: 我现在可以从我的 CI Controller 成功返回 JSON 数据,它返回: {"results":[{"id":"1","Sourc
有什么有效的方法可以删除 ios 中 CBL 的所有文档存储?我对此有疑问,或者,如果有人知道如何从本质上使该应用程序像刚刚安装一样,那也会非常有帮助。我们正在努力确保我们的注销实际上将应用程序设置为
我有一个 Rails 应用程序,它与其他 Rails 应用程序通信以进行数据插入。我使用 jQuery $.post 方法进行数据插入。对于插入,我的其他 Rails 应用程序显示 200 OK。但在
我正在为服务于发布请求的 API 调用运行单元测试。我正在传递请求正文,并且必须将响应作为帐户数据返回。但我只收到断言错误 注意:数据是从 Azure 中获取的 spec.js const accou
我是一名优秀的程序员,十分优秀!