mor*_*tar 15 iphone core-data transient
我阅读有关瞬态属性的文档,但我无法真正理解它们的用途.如果我有这样的NSManagedObject的自定义子类,有人可以告诉我有和没有瞬态属性之间的区别吗?
@interface Board : NSManagedObject
{
NSMutableArray *_grid;
}
// Core Data to-many relationship
@property (nonatomic, retain) NSSet *pieces;
@property (nonatomic, readonly) NSArray *grid;
-(void)awake;
-(void)movePiece:(PieceState *)piece to_x:(int)x y:(int)y;
@end
@implementation Board
@dynamic pieces;
-(void)awakeFromInsert {
[super awakeFromInsert];
[self awake];
}
-(void)awakeFromFetch {
[super awakeFromFetch];
[self awake];
}
-(void)awake {
_grid = nil; // probably not necessary
}
-(NSArray *)grid {
if (!_grid) {
_grid = [[NSMutableArray alloc] initWithCapacity:10];
for (int i = 0; i < 10; i++) {
NSMutableArray *column = [[NSMutableArray alloc] initWithCapacity:10];
[_grid addObject:column];
for (int j = 0; j < 10; j++)
[column addObject:[NSNull null]];
[column release];
}
for (PieceState *piece in self.pieces)
if (piece.x >= 0 && piece.y >= 0)
[[_grid objectAtIndex:piece.x] replaceObjectAtIndex:piece.y withObject:piece];
}
return _grid;
}
-(void)movePiece:(PieceState *)piece to_x:(int)x y:(int)y {
if (x >= 0 && y >= 0) {
NSObject *capturedPieceObject = [[self.grid objectAtIndex:x] objectAtIndex:y];
if ([capturedPieceObject isKindOfClass:[PieceState class]]) {
PieceState *capturedPiece = (PieceState *)capturedPieceObject;
[self removePiecesObject:capturedPiece];
[[self managedObjectContext] deleteObject:capturedPiece];
capturedPiece = nil;
}
}
if (_grid) {
if (piece.x >= 0 && piece.y >= 0)
[[_grid objectAtIndex:piece.x] replaceObjectAtIndex:piece.y withObject:[NSNull null]];
if (x >= 0 && y >= 0)
[[_grid objectAtIndex:x] replaceObjectAtIndex:y withObject:piece];
}
[piece setX:x];
[piece setY:y];
}
- (void)didTurnIntoFault {
[_grid release];
_grid = nil;
[super didTurnIntoFault];
}
@end
Run Code Online (Sandbox Code Playgroud)
因此,片段和网格提供了两种访问相同数据的方法.pieces是实际的Core Data关系属性,是所有部分的密集列表.grid是一种查找由(x,y)坐标寻址的板上特定空间内容的方法.当一块更改位置时,网格会延迟构建并更新(只要它存在).
我不是将网格声明为瞬态属性,一切都运行良好.我只是想知道如果我没有声明一个瞬态属性,是否会出现一些可能导致错误的异常情况.
我想如果你正在做这样的派生属性,我需要读取瞬态属性来获得正确的撤销行为.我没有使用撤销,无论如何我都看不出它在这种情况下是如何工作的.如果撤消了一个片段移动,则撤消管理器可以将_grid的旧值分配给它(可能假设我没有将其设置为只读),但旧值与新值相同.它是指向同一NSMutableArray实例的指针,只有内容已更改.无论如何我不使用撤销.
如果我将网格声明为瞬态属性,那么我会获得任何好处吗?
补充问题.如果我有这样的代码怎么办:
Board *board = someOtherManagedObject.board;
NSObject *boardContents = [[board.grid objectAtIndex:5] objectAtIndex:5];
Run Code Online (Sandbox Code Playgroud)
访问someOtherManagedObject.board后,是否可能出现故障?我也很难理解错误.我想在那种情况下我的代码会崩溃.我注意到唤醒将_grid设置为nil.我认为序列将是这样的:
_grid = nil
[[_grid objectAtIndex:...
访问nil值,崩溃或至少没有操作另一方面,也许如果我声明网格是一个瞬态属性,那么故障会在我的网格getter被调用之前触发?
来自TechZen:
错误是占位符对象,它们定义具有关系的对象图,但不加载属性值.它们将作为NSManagedObject或私有_NSFault ...类的实例进行记录.
因为未建模的属性只是自定义NSManagedObject子类的属性而不是实体,所以故障对象对它们一无所知.故障对象从数据模型初始化,以便它们响应的所有密钥必须位于数据模型中.这意味着故障不能可靠地响应对未建模属性的请求.
等什么?我开始意识到我的对象在任何时候都可能是错误的,但是你告诉我他们甚至可能不是我班级的实例!或者,如果您使用自定义子类,它们是否保证是NSManagedObject(特别是我的子类)实例的那种错误?
如果它们不是自定义类的实例,那么会发生以下情况:
@interface Foo : NSManagedObject {
int data;
}
@property (nonatomic, retain) NSString *modeledProperty;
-(void)doSomething;
@end
@implementation Foo
@dynamic modeledProperty;
-(void)doSomething {
data++;
}
@end
Run Code Online (Sandbox Code Playgroud)
如果我在故障上调用doSomething会发生什么?
瞬态属性解决了这个问题.transient属性提供了上下文无需保存即可观察的密钥.如果您遇到故障,向其发送切线属性的键值消息将触发上下文"触发"故障并加载完整的托管对象.
好的,但是如果我有一个不是属性访问器的实例方法,比如上面的doSomething呢?在我打电话之前,如何确保我有一个真实的物体?或者我可以调用它,并且方法体中的第一件事确保我有一个真实对象(例如通过访问建模属性)?
在您的情况下,如果grid的值取决于Board类的任何建模属性的值,则希望对网格使用transient属性.这是保证在访问网格时始终填充网格的唯一方法.
我认为如果它依赖于建模属性的值,那么当它依赖于它们时它会触发故障,即线路for (PieceState *piece in self.pieces)
触发故障,因为它访问self.pieces,这是一个建模属性.但你告诉我哪个?
看来如果我理解你所说的并且确实如此,NSManagedObject的自定义子类非常有限.
如果是这种情况,那么您是不打算将应用程序逻辑放在自定义NSManagedObject子类中?应用程序逻辑是否应驻留在其他具有托管对象引用的类中,而托管对象只是您从中读取和写入的愚蠢对象(只是有点智能,具有维护数据一致性的一些功能)?将NSManagedObject子类化为非标准数据类型的一些"技巧"的唯一要点是什么?
Tec*_*Zen 43
瞬态属性的优势来自建模/观察属性与未建模/未观察属性之间的差异.
托管对象上下文使用键值观察(KVO)来监视建模属性.根据数据模型中提供的信息,它知道哪些属性必须具有值,默认值,最小值和最大值是什么,何时更改属性,最重要的是,托管对象是否具有属性的键名.所有这些都提供了托管对象的"托管"部分.
建模属性不需要自定义NSManagedObject子类,但可以使用初始化为实体的通用NSManagedObject实例.访问故障的建模属性(见下文)会导致故障完全加载.
托管对象上下文不会观察未建模的属性,而未建模的属性需要自定义NSManagedObject子类.unmodeled属性仅是类的属性,不会显示在实体中,并且它们永远不会保留在Core Data中.对未建模属性的更改不会被上下文忽视.
错误是占位符对象,它们定义具有关系的对象图,但不加载属性值.你可以把它们想象成"幽灵"物体.它们将作为NSManagedObject或私有_NSFault ...类的实例进行记录.如果它是NSManagedObject,则属性都是空的.当故障"触发"或"故障"时,占位符对象将替换为可以读取其属性的完全填充的NSManagedObject实例.
因为未建模的属性只是自定义NSManagedObject子类的属性而不是实体,所以故障对象对它们一无所知.故障对象从数据模型初始化,以便它们响应的所有密钥必须位于数据模型中.这意味着故障不能可靠地响应对未建模属性的请求.
瞬态属性解决了这个问题.transient属性提供了上下文无需保存即可观察的密钥.如果您遇到故障,向其发送切线属性的键值消息将触发上下文"触发"故障并加载完整的托管对象.
请务必注意,尽管数据模型具有临时属性的键名,但该属性仅在完全实例化和加载受管对象时才具有值.这意味着当您执行任何仅在持久性存储中运行的提取时,切线属性将没有值.
在您的情况下,grid
如果值grid
取决于Board
类的任何建模属性的值,则您希望使用瞬态属性.这是保证强制Core Data保证grid
在您访问时始终填充的唯一方法.
[编辑: 最后一个是高度理论化的.使用瞬态属性可确保Core Data跟踪属性,以便访问属性将导致触发故障并提供数据.但是,在实践中,访问任何建模属性将可靠地触发故障,并且始终可以使用未建模的方法(参见下文).
您还可以使用:
+[NSManagedObject contextShouldIgnoreUnmodeledPropertyChanges:]
Run Code Online (Sandbox Code Playgroud)
...强制上下文查看未建模的属性.但是,如果未建模的属性具有副作用,则会导致意外和非管理行为.
我认为尽可能使用瞬态属性以确保一切都被覆盖是一种好习惯.]
好的,但是如果我有一个不是属性访问器的实例方法,比如上面的doSomething呢?在我打电话之前,如何确保我有一个真实的物体?
我认为你已经在考虑这个问题了,我的繁琐解释并没有任何帮助.
Core Data为您管理所有这些问题.只要有核心数据我就一直在使用核心数据,而且我从未遇到过任何问题.如果您不得不经常停下来检查对象是否有故障,核心数据将没有多大用处.
例如,我设置了一个简单的模型,其类如下所示:
Α:
@class Beta;
@interface Alpha : NSManagedObject {
@private
}
@property (nonatomic, retain) NSNumber * num;
@property (nonatomic, retain) NSString * aString;
@property (nonatomic, retain) NSSet *betas;
-(NSString *) unmodeledMethod;
@end
@interface Alpha (CoreDataGeneratedAccessors)
- (void)addBetasObject:(Beta *)value;
- (void)removeBetasObject:(Beta *)value;
- (void)addBetas:(NSSet *)values;
- (void)removeBetas:(NSSet *)values;
@end
@implementation Alpha
@dynamic num;
@dynamic aString;
@dynamic betas;
-(NSString *) unmodeledMethod{
return @"Alpha class unmodeledMethod return value";
}
@end
Run Code Online (Sandbox Code Playgroud)
Beta版:
@class Alpha;
@interface Beta : NSManagedObject {
@private
}
@property (nonatomic, retain) NSNumber * num;
@property (nonatomic, retain) NSSet *alphas;
-(NSString *) unmodeledMethod;
-(NSString *) accessModeledProperty;
@end
@interface Beta (CoreDataGeneratedAccessors)
- (void)addAlphasObject:(Alpha *)value;
- (void)removeAlphasObject:(Alpha *)value;
- (void)addAlphas:(NSSet *)values;
- (void)removeAlphas:(NSSet *)values;
@end
@implementation Beta
@dynamic num;
@dynamic alphas;
-(NSString *) unmodeledMethod{
return [NSString stringWithFormat:@"%@ isFault=%@", self, [self isFault] ? @"YES":@"NO"];
}
-(NSString *) accessModeledProperty{
return [NSString stringWithFormat:@"\n isFault =%@ \n access numValue=%@ \n isFault=%@", [self isFault] ? @"YES":@"NO", self.num,[self isFault] ? @"YES":@"NO"];
}
@end
Run Code Online (Sandbox Code Playgroud)
然后我创建了一个Alpha
带有相关Beta
对象的对象的对象图.然后我重新启动了应用程序并运行了所有Alpha
对象的获取.然后我记录了以下内容:
id aa=[fetchedObjects objectAtIndex:0];
id bb=[[aa valueForKey:@"betas"] anyObject];
NSLog(@"aa isFault= %@",[aa isFault] ? @"YES":@"NO");
//=> aa isFault= NO
NSLog(@"\naa = %@",aa);
//=> aa = <Alpha: 0x63431b0> (entity: Alpha; id: 0x6342780 <x-coredata://752A19D9-2177-45A9-9722-61A40973B1BC/Alpha/p1> ; data: {
//=> aString = "name 2";
//=> betas = (
//=> "0x63454c0 <x-coredata://752A19D9-2177-45A9-9722-61A40973B1BC/Beta/p7>"
//=> );
//=> // ignore fetchedProperty = "<relationship fault: 0x6153300 'fetchedProperty'>";
//=> num = 0;
//=> })
NSLog(@"\nbb isFault= %@",[bb isFault] ? @"YES":@"NO");
//=> bb isFault= YES
NSLog(@"\nany beta = %@",[[bb class] description]);
//=> any beta = Beta
NSLog(@"\n-[Beta unmodeledMethod] =\n \n %@",[bb unmodeledMethod]);
//=> -[Beta unmodeledMethod] =
//=> <Beta: 0x639de70> (entity: Beta; id: 0x639dbf0 <x-coredata://752A19D9-2177-45A9-9722-61A40973B1BC/Beta/p7> ; ...
//=>...data: <fault>) isFault=YES
NSLog(@"\n-[Beta accessModeledProperty] = \n %@",[bb accessModeledProperty]);
-[Beta accessModeledProperty] =
//=> isFault =NO
//=> access numValue=2
//=> isFault=YES
NSLog(@"\nbb = %@",bb);
//=>bb = <Beta: 0x6029a80> (entity: Beta; id: 0x6029460 <x-coredata://752A19D9-2177-45A9-9722-61A40973B1BC/Beta/p7> ; data: {
//=> alphas = "<relationship fault: 0x60290f0 'alphas'>";
//=> num = 2;
//=>})
Run Code Online (Sandbox Code Playgroud)
注意:
aa
并bb
设置为,即使我做了一个通用的对象分配预期的类.上下文确保fetch返回正确的类.bb
是类,Beta
它也会报告为错误,这意味着该对象代表了Beta
类的一个实例,但是没有填充任何建模属性.bb
对象响应unmodeledMethod
选择器,即使在该方法中它仍然报告为故障.Beta.num
转换的建模属性bb
(编译器将其设置为触发)但是一旦访问完成,它就会恢复为故障.Alpha.betas
该Beta
对象具有的地址0x63454c0
,而bb
具有的地址0x639de70>
,而这是一个故障.在它从故障转换然后再转回之后,它是一个地址0x6029a80
.但是,所有三个对象的managedObjectID都是相同的.这里的道德是:
_NSFault…
类的实例.从编码员的角度来看,所有这些不同的对象都是可以互换的.总之不要担心未建模的属性和方法.他们应该透明地工作.最佳实践是使用瞬态属性,尤其是当这些属性具有建模属性的副作用时.您可以强制上下文跟踪未建模的属性,但这可能会导致不必要的复杂性.
如果您有疑问,只需自己对故障进行测试,以确保您的班级有效.
归档时间: |
|
查看次数: |
10625 次 |
最近记录: |