问题描述
将NSArray保存到可转换的Core Data属性时,该对象将无法在后续获取其实体时进行访问。但是,此后的任何提取都可用。发生了什么事?
When saving an NSArray to a transformable Core Data attribute, the object will not be available for access on the subsequent fetch of its entity. However, it is available on any fetch after that. What's going on?
我可以在iOS应用中的一个位置设置并保存Core Data实体及其属性。然后,我去阅读最近保存的实体。除可转换NSArrays之外的所有属性均可用。由于某些原因,数组显示为空(当在日志中打印时,它看起来像这样: route =(\n)
。如果应用程序关闭然后打开
I can set and save the Core Data entity and its attributes from one place in my iOS app. Then I go to read the most recently saved entity. All of the attributes except the transformable NSArrays are available. For some reason the arrays show up as empty (when printed in the log it looks like this: route = "(\n)"
. If the app closes and then opens again, the attribute is no longer empty. Any ideas?
我知道将NSArray保存为可转换属性并不是最佳实践。您能解释为什么会发生这种情况吗?
I understand that saving an NSArray to a transformable attribute is not the best practice. Could you explain why this happens?
更新1
NSArray中充满了CLLocation对象。
The NSArray is filled with CLLocation objects.
控制台中没有打印错误或警告,也没有任何编译器警告或错误。
There are no errors or warnings printed in the console. Nor are their any compiler warnings or errors.
更新2
下面是我为此问题编写的XCTest,直到最后一个断言(如预期的那样),测试才会失败。
Below is an XCTest I wrote for this issue. The test does not fail until the very last assertion (as expected).
- (void)testRouteNotNil {
// This is an example of a performance test case.
NSMutableArray *route;
for (int i = 0; i < 500; i++) {
CLLocation *location = [[CLLocation alloc] initWithLatitude:18 longitude:18];
[route addObject:location];
}
NSArray *immutableRoute = route;
// Save the workout entity
// Just use placeholder values for the XCTest
// The method below works fine, as the saved object exists when it is fetched and no error is returned.
NSError *error = [self saveNewRunWithDate:@"DATE01" time:@"TIME" totalSeconds:100 distance:[NSNumber numberWithInt:100] distanceString:@"DISTANCE" calories:@"CALORIES" averageSpeed:[NSNumber numberWithInt:100] speedUnit:@"MPH" image:[UIImage imageNamed:@"Image"] splits:route andRoute:immutableRoute];
XCTAssertNil(error);
// Fetch the most recently saved workout entity
RunDataModel *workout = [[[SSCoreDataManager sharedManager] fetchEntityWithName:@"Run" withSortAttribute:@"dateObject" ascending:NO] objectAtIndex:0];
XCTAssertNotNil(workout);
// Verify that the fetched workout is the one we just saved above
XCTAssertEqual(workout.date, @"DATE01");
// Check that the any non-NSArray object stored in the entity is not nil
XCTAssertNotNil(workout.distance);
// Check that the route object is not nil
XCTAssertNotNil(workout.route);
}
更新3
如下所示,这就是在Xcode中设置核心数据模型的方式。选择了路线属性。请注意,我已经尝试了带有或不带有瞬时属性的情况。我是否需要添加值变压器名称
,那是什么?
As you can see below, this is how the Core Data model is setup in Xcode. The route attribute is selected. Note that I have tried it both with and without the transient property. Do I need to add a Value Transformer Name
, what is that?
更新4
核心数据管理代码本身来自我的GitHub存储库(据我所知,效果很好)。
The Core Data management code itself comes from my GitHub repo, SSCoreDataManger (which works well to my knowledge).
这是 saveNewRunWithDate
方法:
- (NSError *)saveNewRunWithDate:(NSString *)date time:(NSString *)time totalSeconds:(NSInteger)totalSeconds distance:(NSNumber *)distance distanceString:(NSString *)distanceLabel calories:(NSString *)calories averageSpeed:(NSNumber *)speed speedUnit:(NSString *)speedUnit image:(UIImage *)image splits:(NSArray *)splits andRoute:(NSArray *)route {
RunDataModel *newRun = [[SSCoreDataManager sharedManager] insertObjectForEntityWithName:@"Run"];
newRun.date = date;
newRun.dateObject = [NSDate date];
newRun.time = time;
newRun.totalSeconds = totalSeconds;
newRun.distanceLabel = distanceLabel;
newRun.distance = distance;
newRun.calories = calories;
newRun.averageSpeed = speed;
newRun.speedUnit = speedUnit;
newRun.image = image;
newRun.splits = splits; // This is also an issue
newRun.route = route; // This is an issue
return [[SSCoreDataManager sharedManager] saveObjectContext];
}
下面是 RunDataModel
NSManagedObject接口:
And below is the RunDataModel
NSManagedObject Interface:
/// CoreData model for run storage with CoreData
@interface RunDataModel : NSManagedObject
@property (nonatomic, assign) NSInteger totalSeconds;
// ...
// Omitted most attribute properties because they are irrelevant to the question
// ...
@property (nonatomic, strong) UIImage *image;
/// An array of CLLocation data points in order from start to end
@property (nonatomic, strong) NSArray *route;
/// An array of split markers from the run
@property (nonatomic, strong) NSArray *splits;
@end
在实现中,这些属性是使用<$ c设置的$ c> @dynamic
In the implementation these properties are setup using @dynamic
推荐答案
@quellish的提供有关核心数据故障以及其中一些细微差别的信息。在进行了一些挖掘之后,并借助该答案,我找到了一个解决方案。
@quellish's answer provides information about Core Data faults and some of the nuances and trickeries which lie therein. After doing some digging, and with the help of that answer, I found a solution.
在获取所需的(问题)实体之前,刷新
: NSManagedObjectContext
中的NSManagedObject
Before fetching the desired (problem) entity, refresh the NSManagedObject
in the NSManagedObjectContext
:
[self.managedObjectContext refreshObject:object mergeChanges:NO];
这将更新托管对象的持久性属性以使用持久性存储中的最新值。还将对象变成故障。
This updates the persistent properties of a managed object to use the latest values from the persistent store. It also turns the object into a fault.
这篇关于核心数据可转换属性(NSArray)为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!