我阅读了有关瞬态属性的文档,但我无法真正理解它们的目的。如果我有一个像这样的 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
因此,pieces 和 grid 提供了两种访问相同数据的方法。pieces 是实际的 Core Data 关系属性,是所有片段的密集列表。网格是一种在 (x, y) 坐标寻址的板上查找特定空间内容的方法。当一块改变位置时,网格会延迟构建并更新(只要它存在)。
我没有将网格声明为瞬态属性,并且一切正常。我只是想知道如果我不声明瞬态属性,是否会出现一些不寻常的情况会导致错误。
如果您正在执行这样的派生属性,我认为我需要读取瞬态属性才能获得正确的撤消行为。我没有使用撤消,无论如何我看不出它在这种情况下如何工作。如果一块移动被撤消,撤消管理器可以将 _grid 的旧值分配回它(可能假设我没有将其设为只读),但旧值与新值相同。它是指向同一个 NSMutableArray 实例的指针,只是内容发生了变化。无论如何,我不使用撤消。
那么,如果我将网格声明为瞬态属性,我会得到什么好处吗?
附加问题。如果我有这样的代码怎么办:
Board *board = someOtherManagedObject.board;
NSObject *boardContents = [[board.grid objectAtIndex:5] objectAtIndex:5];
访问 someOtherManagedObject.board 后,板子是否可能出现故障?我也很难理解错误。我认为在那种情况下我的代码会崩溃。我注意到 awake 将 _grid 设置为 nil。我认为顺序应该是这样的:
- 称为网格吸气剂
- _grid 已分配
- 访问的self.pieces
- 故障火灾
- 叫醒
_grid = nil
- 返回网格吸气剂
[[_grid objectAtIndex:...
访问 nil 值、崩溃或至少无操作- 网格 getter 返回 nil
- 当 boardContents 应该包含一个值时崩溃或不正确的行为
另一方面,也许如果我声明网格是一个瞬态属性,那么在调用我的网格吸气剂之前故障会触发?
来自 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
如果我在故障上调用 doSomething 会发生什么?
- 不响应选择器,崩溃
- 运行我的代码,但是我的实例变量不存在,谁知道当它执行 data++ 时会发生什么
- data 存在,只是 modeledProperty 不存在,因为它是一个错误
瞬态属性解决了这个问题。瞬态属性提供了上下文可以观察而无需保存的键。如果您有故障,向其发送瞬态属性的键值消息将触发上下文“触发”故障并加载完整的托管对象。
好的,但是如果我有一个不是属性访问器的实例方法,比如上面的 doSomething 呢?在调用它之前如何确保我有一个真实的对象?或者我可以调用它,并且在方法主体中首先确保我有一个真实的对象(例如通过访问建模属性)?
在您的情况下,如果 grid 的值取决于 Board 类的任何建模属性的值,则您希望对 grid 使用瞬态属性。这是保证在您访问网格时始终填充网格的唯一方法。
我认为如果它依赖于建模属性的值,那么它会在依赖于它们时触发故障,即该行for (PieceState *piece in self.pieces)
触发故障是因为它访问了 self.pieces,这是一个建模属性。但你告诉我哪个?
- 我什至不能在故障上调用网格 getter 方法
- 我可以调用它,但我不能以我想要的方式使用 _grid
看来,如果我理解您的意思并且确实如此, NSManagedObject 的自定义子类非常有限。
- 它们不能有任何不是建模属性 getter 或 setter 的实例方法,因为不能保证对象在调用时处于可用状态。(例外:只是属性访问器的辅助方法的实例方法会很好。)
- 除了计算值的临时缓存之外,它们不能有任何用于任何有用目的的实例变量,因为这些实例变量可能随时被删除。我知道它们不会保留在磁盘上,但我认为只要我将对象保留在内存中,它们至少会保留。
如果是这种情况,那么您不打算将应用程序逻辑放在您的自定义 NSManagedObject 子类中吗?应用程序逻辑是否应该驻留在对托管对象具有引用的其他类中,并且托管对象只是您读取和写入的哑对象(只是有点聪明,具有保持数据一致性的一些功能)?将 NSManagedObject 子类化以使用非标准数据类型做一些“技巧”的唯一要点是什么?