Core Data 自定义访问器甚至没有被调用

Posted

技术标签:

【中文标题】Core Data 自定义访问器甚至没有被调用【英文标题】:Core Data custom accessor doesn't even get called 【发布时间】:2012-02-18 06:22:43 【问题描述】:

我有一个我试图在运行时设置的 Core Data 属性,其值来自另一个属性。然而,奇怪的是,我构建的自定义访问器似乎从未被调用过。

属性 seasonNameVisible 仅从包含搜索字段中的术语的谓词中调用,如下所示:

// Add our search predicates
for (NSString *term in searchTerms) 
    NSPredicate *searchTermPredicate = [NSPredicate predicateWithFormat:@"(episodeName contains[cd] %@) OR (fromSeason.seasonNameVisible contains[cd] %@) OR (fromSeason.fromSeries.seriesName contains[cd] %@)", term, term, term];
    [subPredicates addObject:searchTermPredicate];

如果我将该属性更改为seasonName,则谓词的那部分返回结果就好了,所以我不会怀疑谓词或任何其他搜索代码。

我的计划是在运行时从seasonName 派生seasonNameVisible NSString。所以,我修改了Season NSManagedObject 子类来覆盖访问器和设置器,使用原始访问器。但据我所知,我的访问器永远不会被调用。

这是标题/接口:

//
//  Season.h
//

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Episode, Series;

@interface Season : NSManagedObject

@property (nonatomic, retain) NSNumber * seasonIndex;
@property (nonatomic, retain) NSString * seasonName;
@property (nonatomic, retain) NSString * seasonNameVisible;
@property (nonatomic, retain) NSSet *episodes;
@property (nonatomic, retain) Series *fromSeries;

@property (nonatomic, retain) NSString * primitiveSeasonName;
@property (nonatomic, retain) NSString * primitiveSeasonNameVisible;
@end

@interface Season (CoreDataGeneratedAccessors)

- (void)addEpisodesObject:(Episode *)value;
- (void)removeEpisodesObject:(Episode *)value;
- (void)addEpisodes:(NSSet *)values;
- (void)removeEpisodes:(NSSet *)values;

@end

// my additions
@interface Season (PrimitiveAccessors)
- (NSString *)primitiveSeasonName;
- (NSString *)primitiveSeasonNameVisible;
@end

...以及实现:

//
//  Season.m
//

#import "Season.h"
#import "Episode.h"
#import "Series.h"


@implementation Season

@dynamic seasonIndex;
@dynamic seasonName;
@dynamic seasonNameVisible;
@dynamic episodes;
@dynamic fromSeries;

// my additions
@dynamic primitiveSeasonName;
@dynamic primitiveSeasonNameVisible;

- (NSString *)seasonNameVisible

    NSString *visible;
    [self willAccessValueForKey:@"seasonNameVisible"];
    visible = [self primitiveValueForKey:@"seasonNameVisible"];
    [self didAccessValueForKey:@"seasonNameVisible"];

    if (visible != nil) 
        return visible;
     else 
        [self willAccessValueForKey:@"seasonName"];
        visible = [[self primitiveValueForKey:@"seasonName"] substringFromIndex:2];
        [self didAccessValueForKey:@"seasonName"];
        [self setSeasonNameVisible:visible];
        return visible;
    


- (void)setSeasonNameVisible:(NSString *)seasonNameVisible 

    [self willChangeValueForKey:@"seasonNameVisible"];
    [self setPrimitiveValue:seasonNameVisible forKey:@"seasonNameVisible"];
    [self didChangeValueForKey:@"seasonNameVisible"];


@end

我已经阅读了 Apple 的文档,并在 *** 上搜索了有关自定义访问器方法的帮助,并且我认为我的代码是正确的(这是我第一次尝试使用原始访问器,或者重写 NSManagedObject 方法,所以我有点超出了我通常的深度),但即使我在它上面放了一个断点,它似乎也永远不会被调用。

【问题讨论】:

现在我再看一遍,我怀疑如果我用 KVC 调用所有东西,我不需要声明原始属性,但这仍然不能改变它没有得到的事实调用。 谓词用于获取请求,用于搜索视图,并且它是持久的,因为正如您在下面的回答中所述,您无法使用它进行获取。问题更多的是seasonNameVisible 正在返回nil 并且没有调用我的自定义访问器来填充它。 我猜jrturton的答案现在已经被删除了。 我很确定 fetch 请求不会通过您的对象来获取值,如果他们这样做了,那将是原始值(因此不会触发 KVO 通知)。如果您访问单个对象的 seasonNameVisible 属性(例如,将其显示在表格视图单元格中),您会得到正确的结果吗? 这是一个 SQLLite 存储吗?我相信 fetches 直接去那里,而不是通过访问器。也许改为覆盖 seasonName 的设置器以设置 seasonNameVisible? 【参考方案1】:

您要求基于延迟加载的属性获取核心数据。我认为这行不通。您需要在设置季节名称时设置您的 seasonNameVisible,或者,这可能更有意义,将您的季节名称的存储区分开为前缀(您要删除以提供可见名称)和真实的名字。

在 cmets 中的其他信息之后,我会建议以下内容:

将您的季节分成两个属性,一个季节编号 (int) 和季节名称 按季号对获取请求进行排序 section name key path 是对象的一个​​新的只读属性,它返回一个由数字和名称组成的字符串

您在评论中看到的错误是更改与 FRC 一起使用的提取请求时的正常现象。从模拟器中删除应用程序并重新构建它会消失,或者在开发时使用零缓存。 FRC 永久存储其缓存(例如,在运行之间),因此任何更改都会扰乱它。

Section name key path可以是你喜欢的任何key path或者属性名,只要排序一致即可。来自 NSFetchedResultsController 的文档:

sectionNameKeyPath

返回部分名称的结果对象的键路径。传递 nil 表示控制器应该生成单个部分。 部分名称用于预先计算部分信息。 如果此键路径与 fetchRequest 中第一个排序描述符指定的不同,则它们必须生成相同的相对排序。例如, fetchRequest 中的第一个排序描述符可能会指定持久属性的键; sectionNameKeyPath 可能会为从持久属性派生的临时属性指定一个键

因此,您将拥有一个具有两个持久属性 seasonNumberseasonNameSeason 对象。您将按季号对获取请求进行排序。您的部分名称键路径(对于可能在具有season 关系的剧集上的提取)将是@"season.seasonSectionName",实现如下 - 您的托管对象模型没有更改,只是更改您的 Season 对象:

季节.h:

@property(nonatomic,readonly) NSString *seasonSectionName;

季节.m:

-(NSString*)seasonSectionName

    return [NSString stringWithFormat:@"%d - %@",self.seasonNumber,self.seasonName];

你真正在做的只是用另一个属性来装饰季节编号。

【讨论】:

我从未真正设置过seasonName,因为它是从 SQLite 数据库中预加载的数据。我在其中添加了前缀,因此我可以将其用作表格中的部分标题。我之前把它们分开了,但是如果我没有字符串上的前缀,它在获取时不会正确排序,我试图避免为表中每个可能的部分标题编写自定义案例。我意识到这是一种笨拙的技巧,但我想不出更理智的方法。 ...也许从 seasonName 访问器调用 setSeasonNameVisible 会更有意义?我试试看。 您仍然必须访问每个季节才能获得存储在数据库中的正确值。请注意,您可以对节名称键路径使用瞬态值(例如,仅返回字符串的方法),只要结果以相同的顺序出现即可。季节名称中的数据是什么样的?您是如何得出前缀的? 它们的排序顺序不同,这就是为什么所有这些繁琐的事情都放在首位的原因。季节名称是一个实际名称,它不会按照季节应排序的顺序按字母顺序排序。 嘿。我在第一条评论中尝试了我的建议,为了我的麻烦,我得到了:'NSInternalInconsistencyException', reason: 'CoreData: FATAL ERROR: The persistent cache of section information does not match the current configuration. You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:' 我已根据您的 cmets 在我的答案中添加了更多信息,希望对您有所帮助。我们似乎保持非常不同的时间,所以聊天可能非常异步!

以上是关于Core Data 自定义访问器甚至没有被调用的主要内容,如果未能解决你的问题,请参考以下文章

从模型的访问器抛出自定义Laravel异常时未调用方法render()

有没有办法为 Core Data 原始访问器自动生成 @property 和 @dynamic 标签?

使用 Core Data 时如何重新排列部分

插入时不调用 Core Data 的自定义验证方法

Hadoop Partitioner 中的自定义计数器

执行删除规则时未调用自定义对多关系访问器方法