16

我阅读了有关瞬态属性的文档,但我无法真正理解它们的目的。如果我有一个像这样的 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。我认为顺序应该是这样的:

  1. 称为网格吸气剂
  2. _grid 已分配
  3. 访问的self.pieces
  4. 故障火灾
  5. 叫醒
  6. _grid = nil
  7. 返回网格吸气剂
  8. [[_grid objectAtIndex:...访问 nil 值、崩溃或至少无操作
  9. 网格 getter 返回 nil
  10. 当 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 会发生什么?

  1. 不响应选择器,崩溃
  2. 运行我的代码,但是我的实例变量不存在,谁知道当它执行 data++ 时会发生什么
  3. data 存在,只是 modeledProperty 不存在,因为它是一个错误

瞬态属性解决了这个问题。瞬态属性提供了上下文可以观察而无需保存的键。如果您有故障,向其发送瞬态属性的键值消息将触发上下文“触发”故障并加载完整的托管对象。

好的,但是如果我有一个不是属性访问器的实例方法,比如上面的 doSomething 呢?在调用它之前如何确保我有一个真实的对象?或者我可以调用它,并且在方法主体中首先确保我有一个真实的对象(例如通过访问建模属性)?

在您的情况下,如果 grid 的值取决于 Board 类的任何建模属性的值,则您希望对 grid 使用瞬态属性。这是保证在您访问网格时始终填充网格的唯一方法。

我认为如果它依赖于建模属性的值,那么它会在依赖于它们时触发故障,即该行for (PieceState *piece in self.pieces)触发故障是因为它访问了 self.pieces,这是一个建模属性。但你告诉我哪个?

  1. 我什至不能在故障上调用网格 getter 方法
  2. 我可以调用它,但我不能以我想要的方式使用 _grid

看来,如果我理解您的意思并且确实如此, NSManagedObject 的自定义子类非常有限。

  1. 它们不能有任何不是建模属性 getter 或 setter 的实例方法,因为不能保证对象在调用时处于可用状态。(例外:只是属性访问器的辅助方法的实例方法会很好。)
  2. 除了计算值的临时缓存之外,它们不能有任何用于任何有用目的的实例变量,因为这些实例变量可能随时被删除。我知道它们不会保留在磁盘上,但我认为只要我将对象保留在内存中,它们至少会保留。

如果是这种情况,那么您不打算将应用程序逻辑放在您的自定义 NSManagedObject 子类中吗?应用程序逻辑是否应该驻留在对托管对象具有引用的其他类中,并且托管对象只是您读取和写入的哑对象(只是有点聪明,具有保持数据一致性的一些功能)?将 NSManagedObject 子类化以使用非标准数据类型做一些“技巧”的唯一要点是什么?

4

1 回答 1

47
于 2011-09-22T13:29:08.903 回答