2015-04-21 36 views
0

我在我的NSManagedObject上使用了一些Transient属性来预先计算只需要在依赖属性更新时计算一次的事情,但是当我重置Transient属性时永远不会保存的值。setPrimitiveValue for Transient property not saved

例如:

-(NSString*)age { 
    // notify core data to access value 
    [self willAccessValueForKey:@"age"]; 
    // get primitive value 
    NSString* age = [self primitiveValueForKey:@"age"]; 
    // is age already exist? 
    if (age == nil) { 
     // get age 
     age = [GlobalHelper convertToAgeStringWithUTCDateOfBirth:(NSString*)self.dateOfBirth]; 
     // set primitive value 
     [self setPrimitiveValue:age forKey:@"age"]; 
    } 
    // notify core data done access value 
    [self didAccessValueForKey:@"age"]; 
    return age; 
} 

-(void)setDateOfBirth:(id)dateOfBirth { 
     // change value for key 
     [self willChangeValueForKey:@"dateOfBirth"]; 
     [self willChangeValueForKey:@"age"]; 
     // set primitive 
     [self setPrimitiveValue:dateOfBirth forKey:@"dateOfBirth"]; 
     [self setPrimitiveValue:nil forKey:@"age"]; 
     // did change value for key 
     [self didChangeValueForKey:@"age"]; 
     [self didChangeValueForKey:@"dateOfBirth"]; 
} 

所以你可以从上面的代码中看到,当我同步此对象,当“出生日期”字段更新它尝试设置“年龄”为“零”,当访问“年龄”字段,如果它是“无”,那么计算年龄并不再计算它。

然而,问题是当我在“setDateOfBirth”中将“age”字段设置为“nil”时,它从来没有保存过,当我再次访问年龄时,“age”属性仍然具有以前的值,但“dateOfBirth”已更新。

任何远程与此主题有关的帮助将是非常赞赏,我有这个大麻烦......

编辑:我上面有逻辑的作品完美的方案,其中从相同的两个setter和getter电话上下文(相同的线程上下文),问题只发生在setter从具有专用队列的AFNetworking API的完成块中调用并且合并后从主线程调用的getter时。

看来,当我从AFNetworking完成块的上下文中保存时,它只保存dateOfBirth,但不保存dateOfBirth,它在合并后设置为零,并且当来自主线程“age”的getter调用具有先前的值时它永远不会再计算。

+0

你刚才说年龄是一个短暂的财产,你为什么期望它被保存? – Rog

+0

瞬态属性不会保存到存储器中,但会保存到内存上下文中,并且可以在https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdNSAttributes中很好地解释瞬态属性的用途。 html – andykkt

+1

您需要在保存这些记录的位置发布代码的一部分。它可能是一个管理对象上下文跨越线程的问题,或者将更改与瞬态属性合并(详见本页底部的详细信息)https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles /cdUsingMOs.html) – Rog

回答

1

你不应该在阅读年龄时设置年龄。这似乎是非常奇怪的设计,也没有调用适当的键值编码通知。

请按照您的原始逻辑:如果出生日期发生变化,请更新年龄。您应该在setDateOfBirth中执行此操作,并在设置器中调用适当的键值编码通知age

-(void) setDateOfBirth:(id)newValue { 
    [self willChangeValueForKey:@"dateOfBirth"]; 
    [self setPrimitiveValue:newValue forKey:@"dateOfBirth"]; 
    [self didChangeValueForKey:@"dateOfBirth"]; 

    self.age = [GlobalHelper 
     convertToAgeStringWithUTCDateOfBirth:(NSString*)newValue]; 
} 

-(void)age { 
    [self willAccessValueForKey:@"age"]; 
    NSString* age = [self primitiveValueForKey:@"age"]; 
    [self didAccessValueForKey:@"age"]; 
    if (age == nil) { 
     age = [GlobalHelper 
     convertToAgeStringWithUTCDateOfBirth:(NSString*)self.dateOfBirth]; 
    } 
    self.age = age; 
    return age; 
} 

然而,所有你不得不问自己多么昂贵的convertAge电话真的是。除了最极端的情况外,其中一个简单的获取器实际上就是您所需要的。

此外,我认为你的dateOfBirth属性应该有一个标准的管理对象属性类型,而不是id。它是一个字符串吗?那么你应该声明它为NSString,而不是使用id和演员。

+0

首先,谢谢你的答案和原因dateOfBirth属性是id,因为它是Transformer类型,它是为了安全原因而编码的,但与此无关..我在dateOfBirth属性设置并计算它时重置了年龄的原因只要年龄为零,我就可以确保它只计算一次。并且它对于一个转换API调用来说并不昂贵,但想象一下,如果您需要在表视图中显示数百或数千个字段,并且每次滚动它时都会调用convert api。 – andykkt

+0

我得到的逻辑完美适用于场景从相同的上下文(相同的线程上下文)的setter和getter调用,问题只发生在AFNetworking api的完成块从private线程调用的setter和合并后从主线程调用的getter时。 – andykkt

+0

如果你只是使用一个getter,即使在一个有1000个条目的tableview中,它也只会被屏幕上的行调用,所以没问题。 - 至于线程问题,您应该将更改传递给主线程(例如,通过保存父上下文)并保存。 – Mundi