gpt4 book ai didi

ios - 核心数据可转换属性(NSArray)为空

转载 作者:可可西里 更新时间:2023-11-01 06:17:47 25 4
gpt4 key购买 nike

将NSArray保存到可转换的Core Data属性时,该对象将无法在后续获取其实体时进行访问。但是,此后任何读取都可以使用它。这是怎么回事?

我可以在iOS应用程序中的一个位置设置和保存Core Data实体及其属性。然后,我去阅读最近保存的实体。除可转换NSArrays之外的所有属性均可用。由于某些原因,数组显示为空(当在日志中打印时,它看起来像这样:route = "(\n)"。如果应用程序关闭然后再次打开,则该属性不再为空。有什么想法吗?

我知道将NSArray保存到可转换属性不是最佳实践。你能解释为什么会这样吗?

更新1

NSArray充满了CLLocation对象。

控制台中未打印任何错误或警告。它们的任何编译器警告或错误也不是。

更新2

以下是我为此问题编写的XCTest。直到最后一个断言(如预期的那样),测试才会失败。

- (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中设置Core Data模型的方式。选择了路线属性。请注意,我已经尝试了带有或不带有 transient 属性的情况。我是否需要添加 Value Transformer Name,那是什么?



更新4

核心数据管理代码本身来自我的GitHub存储库 SSCoreDataManger(据我所知,这很好用)。

这是 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接口(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

在实现中,使用 @dynamic设置这些属性

最佳答案

“可转换”实体属性是通过NSValueTransformer实例传递的属性。用于特定属性的NSValueTransformer类的名称在托管对象模型中设置。当核心数据访问属性数据时,它将调用+[NSValueTransformer valueTransformerForName:]以获取值转换器的实例。使用该值转换器,将持久存储在实体中的NSData转换为通过托管对象实例的属性访问的对象值。

您可以在“核心数据编程指南”部分的Non-Standard Persistent Attributes中了解更多信息。

默认情况下,Core Data使用注册为名称NSKeyedUnarchiveFromDataTransformerName的值转换器,然后反向使用它来执行转换。如果在“核心数据模型编辑器”中未指定值转换器名称,则将发生这种情况,通常这是您想要的行为。如果要使用其他NSValueTransformer,则必须通过调用+[NSValueTransformer setValueTransformer:forName:]在应用程序中注册它的名称,并在模型编辑器(或代码)中设置字符串名称。请记住,您使用的值转换器必须同时支持正向和反向转换。

默认值转换器可以将任何支持键控归档的对象转换为NSData。就您而言,您有一个NSArray(实际上是NSMutableArray,这不好)。 NSArray支持NSCoding,但是由于它是一个集合,所以包含在其中的对象也必须支持它-否则无法归档。幸运的是,CLLocation确实支持NSSecureCoding,这是NSCoding的较新版本。

您可以使用Core Data的转换器轻松测试NSArrayCLLocation的转换。例如:

- (void)testCanTransformLocationsArray {
NSValueTransformer *transformer = nil;
NSData *transformedData = nil;

transformer = [NSValueTransformer valueTransformerForName:NSKeyedUnarchiveFromDataTransformerName];
transformedData = [transformer reverseTransformedValue:[self locations]];
XCTAssertNotNil(transformedData, @"Transformer was not able to produce binary data");
}

我鼓励您针对可转换属性编写此类测试。可以很容易地对应用程序进行与默认转换器不兼容的更改(例如,插入不支持键控归档的对象)。

使用这样的一组测试,我无法在将 NSArrayCLLocation归档时重现任何问题。

您的问题有一个非常重要的部分:

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?



这表明(至少在您的应用程序中,也许不是在您的测试中)数据正在转换并应用于商店中的实体。当应用程序设置 routes值时,该数组将保存到存储中-我们知道这一点,因为下次启动该应用程序时,数据会出现。

通常,这表明在上下文之间传递更改时应用程序存在问题。从您发布的代码来看,您似乎正在使用单个上下文,并且仅在主线程中使用-否则 SSCoreDataManager单例将无法正常工作,并且它正在使用过时的线程限制并发模型。

同时 SSCoreDataManager在使用 -performBlock:来访问单个 NSManagedObjectContext的地方。 performBlock:仅应与使用队列并发类型创建的上下文一起使用。此处使用的上下文是使用 -init创建的,它只包装了 -initWithConcurrencyType:并传递了 NSConfinementConcurrencyType值。因此,您肯定在单例中存在并发问题,这很可能导致您所看到的某些行为。您将属性值保留在实体上,但是稍后在包装该属性的属性在托管对象上下文中引发错误时,看不到该值反射(reflect)出来。

如果您能够使用Xcode 6.x和iOS 8进行开发,请通过传递启动参数来打开Core Data并发调试。
-com.apple.CoreData.ConcurrencyDebug 1
到您的应用程序。尽管仅在使用 performBlock:创建的上下文上调用 -init应该已经引发了异常,但这应该使您更容易看到这里的一些问题。如果您的应用程序正在执行吞噬可能掩盖此问题和更多问题的异常的操作。

从您的问题尚不清楚,仅当您尝试在调试器中访问 routes时,还是在使用它时也看到损坏的功能时,才看到此问题。在调试托管对象时,您必须非常清楚何时引发属性值错误。在这种情况下,可能仅在调试器中看到一个空数组,这是因为正在以一种不会引起故障的方式访问它,这将是正确的行为。从您对其他应用程序行为的描述来看,这似乎很可能是问题的局限性-毕竟,正确地保留了值。

不幸的是,《核心数据编程指南》 barely mentions what a fault is与唯一性并列。故障是核心数据的基本部分-这是使用数据的重点-几乎与唯一性无关。幸运的是,几年前 Incremental Store Programming Guide进行了更新,对核心数据的内部有了很多见解,包括故障。

您的测试和单例还有其他问题,很遗憾,这些问题不在此问题的范围内。

关于ios - 核心数据可转换属性(NSArray)为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28056708/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com