核心数据,无法检索/设置到我的属性。最常见的错误?

Posted

技术标签:

【中文标题】核心数据,无法检索/设置到我的属性。最常见的错误?【英文标题】:Core Data, can't retrieve/set to my properties. Most common mistake? 【发布时间】:2012-08-07 16:28:42 【问题描述】:

主题:核心数据,无法从 VC 中检索/设置我的属性。最常见的错误?

通过这个网站进行了搜索,但 无法完全得到我需要的答案(虽然有很多很好的提示),所以我想我发布这个问题,希望它能解决我遇到的问题几个星期了!是的,很沮丧,你大概知道这种感觉!所以任何帮助都会很棒 - 谢谢! :-)

概览:

ios 5.1 项目。让核心数据正常工作(在 main.h/NSLog 中测试),但我 无法从其他视图控制器检索和设置实体属性(数据)。 Xcode 从其他视图控制器中识别出在 AppDelegate 中找到的我的单例“AppContent”,但不能识别实体名称及其属性。

最常见的错误是什么?

我感觉我只是错过了在正确的位置导入一些文件等......

更多细节;

仅供参考:我正在尝试使用 Matt Campell 推荐的方法,该方法在 AppDelegate 中创建一个单例,可以在整个应用程序中使用,以便与任何视图控制器中的 managedObjectContext 一起使用,并将数据检索并保存到 Core Data 及其实体及其各自的属性。这是通过将以下两个文件导入应用程序来完成的;

appContent.h

#import <Foundation/Foundation.h>
#import "Contest.h"  // Root Entity in CD model
#import "Player.h"

@interface AppContent : NSObject

+(AppContent *)sharedContent;

@property(strong, readonly) id rootObject;

-(void)save;
-(void)rollback;

@end

appContent.m

#import "AppContent.h"
#import <CoreData/CoreData.h>

@interface AppContent()

-(NSURL *)dataStoreURL;
@property (nonatomic, strong, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;

@end

@implementation AppContent
NSManagedObjectModel *_managedObjectModel;
NSPersistentStoreCoordinator *_persistentStoreCoordinator;
NSManagedObjectContext *_managedObjectContext;
id _rootObject;

static AppContent *singletonInstance = nil;

+ (AppContent *)sharedContent
    @synchronized(self)
        if (singletonInstance == nil)
            singletonInstance = [[self alloc] init];

        return(singletonInstance);
    



- (NSURL *)dataStoreURL 

    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

    return [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"DataStore.sql"]];


- (NSManagedObjectModel *)managedObjectModel 
    if (_managedObjectModel) 
        return _managedObjectModel;
    
    _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];    
    return _managedObjectModel;


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
    if (_persistentStoreCoordinator) 
        return _persistentStoreCoordinator;
    

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                               configuration:nil
                                                         URL:[self dataStoreURL]
                                                     options:nil
                                                       error:&error]) 
        NSLog(@"Unresolved Core Data error with persistentStoreCoordinator: %@, %@", error, [error userInfo]);
        

    return _persistentStoreCoordinator;


- (NSManagedObjectContext *)managedObjectContext 
    if (_managedObjectContext) 
        return _managedObjectContext;
    

    if ([self persistentStoreCoordinator]) 
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
    

    return _managedObjectContext;


-(id)rootObject
    if(_rootObject)
        return _rootObject;

// #warning Replace [CHANGE] with your root object entity name
    NSString *entityName = @"Contest";
    NSManagedObjectContext *context = [self managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
                                          inManagedObjectContext:context];
    request.entity = entity;
    NSArray *listOfObjects = [context executeFetchRequest:request
                                                error:nil];
    if([listOfObjects count] == 1)
        _rootObject = [listOfObjects lastObject];
        return _rootObject;
    
    _rootObject = [NSEntityDescription insertNewObjectForEntityForName:entityName
                                            inManagedObjectContext:context];

    // Adding some testdata (only first time...)
    // Contest
    Contest *c = _rootObject;
    c.name = @"Big Game 1";

    // Players
    Player *p1 = (Player *) [NSEntityDescription insertNewObjectForEntityForName:@"Player"
                                                          inManagedObjectContext:context];
    p1.name = @"Player One";
    [c addPlayersObject:p1];

    Player *p2 = (Player *) [NSEntityDescription insertNewObjectForEntityForName:@"Player"
                                                              inManagedObjectContext:context];
    p2.name = @"Player Two";
    [c addPlayersObject:p2];

    [self save];

    return _rootObject;


-(void)save
    NSError *error = nil;
    NSManagedObjectContext *context = [self managedObjectContext];
    if([context hasChanges])
        [context save:&error];
    if(error)
        NSLog(@"Warning: Error saving to data store.  %@", error);


-(void)rollback
    NSManagedObjectContext *context = [self managedObjectContext];
    if([context hasChanges])
        [context rollback];


@end

这里是模型文件(由 CD 模型编辑器创建);

竞赛.h

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

@class Player;

@interface Contest : NSManagedObject

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSSet *players;
@end

@interface Contest (CoreDataGeneratedAccessors)

- (void)addPlayersObject:(Player *)value;
- (void)removePlayersObject:(Player *)value;
- (void)addPlayers:(NSSet *)values;
- (void)removePlayers:(NSSet *)values;

@end

竞赛.m

#import "Contest.h"
#import "Player.h"

@implementation Contest

@dynamic name;
@dynamic players;

@end

Player.h

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

@class Contest;

@interface Player : NSManagedObject

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) Contest *contests;

@end

Player.m

#import "Player.h"
#import "Contest.h"


@implementation Player

@dynamic name;
@dynamic contests;

@end

这是一个视图控制器

试图在此处获取核心数据实体及其属性,但 Xcode 看到了 appContent 但无法识别它,并且在构建它时出现错误“找不到属性播放器”...

contestPlayerVC.h

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "AppContent.h"


@interface contestPlayerVC : UIViewController

@property (strong, nonatomic) IBOutlet UITextField *playerNameField;
@property (strong, nonatomic) IBOutlet UITextField *playerMailField;
@property (strong, nonatomic) IBOutlet UIButton *playerSaveButton;

@property (weak, nonatomic) AppContent *content;

// Test
// @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;


- (IBAction)playerChooseImage:(UIButton *)sender;

- (IBAction)dismissModal:(UIButton *)sender;

- (IBAction)hideKeyboard:(id)sender;

- (IBAction)playerSave:(UIButton *)sender;

@end

contestPlayerVC.m 不显示孔文件,因为问题出在方法 viewDidLoad...

#import "contestPlayerVC.h"

@interface contestPlayerVC ()

@end

@implementation contestPlayerVC
@synthesize playerNameField, playerMailField, playerSaveButton, content;

- (void)viewDidLoad 

    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // Call 1 - seems to work ok
    self.content = [AppContent sharedContent];

////// Call 2 - gives error, can't find property "player" - WHY?
    // Trying to set textfield to CD Entity "Player" and it's property "name"
    self.playerNameField.text = self.content.player.name;

    NSLog(@"Player is: %@", self.content.player.name);

    // Call 3 - works ok
    NSLog(@"Content is: %@", self.content.description);


...

有什么提示或建议吗?谢谢! :-)

顺便说一句:很抱歉,这篇文章很长,我不确定如何描述我的问题足以让任何人理解它。如果您阅读了所有内容 - 我印象深刻,如果您能帮我解决这个问题,我会非常高兴。可能我完全错过了一些基本的东西。谢谢;-)

【问题讨论】:

【参考方案1】:

@user1578933 好的,让我们倒退一下,想想每个部分负责什么:

比赛是根(第一个)对象,负责与比赛相关的一切,包括球员的姓名和名单 玩家对玩家的一切负责,包括玩家的姓名 AppContent 负责管理对象图(检索对象图以便您可以使用它并记住用户添加和删除的内容) contestPlayerVC 负责展示内容(来自 AppContent)

在您的应用中,一切都以竞赛开始。在您的 AppContent 实现中,您有生成该属性 rootObject 的代码,这是第一次创建 Contest 对象的地方。

注意您已将其定义为 id,因为您正在为我们的程序直接复制此代码,但您也可以将 rootObject 定义为像这样输入竞赛

-(Contest *) rootObject;

这是您进入对象图的入口点。这个想法是您将访问 AppContent(使用单例模式),然后检索 Contest 对象。 Contest 对象将具有 NSSet 对象集合,其中包含对集合中每个玩家的引用。

这是一个如何在视图控制器中使用它的示例:

- (void)viewDidLoad 
    [super viewDidLoad];

    self.content = [AppContent sharedContent];

    //get reference to the root object contest
    Contest *theContest = (Contest *)self.content.rootObject;

    //get an array based reference to the players in theContest
    NSArray *players = [theContest.players allObjects];

    //Example of getting a reference to an individual player:
    Player *p1 = [players objectAtIndex:0];


本质上,这就是将内容添加到视图控制器的方式;对于表格视图控制器,您将使用索引路径行属性来找出播放器进入哪个表格视图单元格(请参阅 cellForRowAtIndexPath)。

@user1578933 - 在阅读了这篇文章以及您在Mobile App Mastery Institute 上留下的那些之后,您似乎对对象图以及所有这些对象关系的总体组织方式缺乏一些了解。我最近添加了一些关于如何使用和思考Objective-C object graph here 的附加内容。另外,我会再次仔细查看有关 Objective-C、Table Views 和 Core Data 的部分。

【讨论】:

非常感谢,这正是我想要的!现在我可以继续使用 Core Data 和您的 AppContent 概念,因为我现在对它有了更好的理解。是的,我将返回您的站点,在您更新的材料中阅读对象图。再次感谢:-)【参考方案2】:

您的错误是无法在 self.content 上找到 player 属性,这是因为您的 AppContent 类未声明该名称的任何属性。让我们一次一行地浏览您的代码:

self.content = [AppContent sharedContent];

现在self.content 是一个AppContent 类型的对象,因此将来对self.content.something 的任何调用都将查找在该类上声明的可见方法或属性(通常在“AppContent.h”中)。

self.playerNameField.text = self.content.player.name;

我们正在这里寻找 AppContent 对象中名为 player 的属性,该对象的标题是:

#import <Foundation/Foundation.h>
#import "Contest.h"  // Root Entity in CD model
#import "Player.h"

@interface AppContent : NSObject

+(AppContent *)sharedContent;

@property(strong, readonly) id rootObject;

-(void)save;
-(void)rollback;

@end

其中没有一个名为player@property - 您有一个rootObject 属性,并且您导入了“Player.h”标头,但您从未声明您尝试访问的属性。您需要添加如下一行:

@property(strong) Player * player;

并在您的“AppContent.m”文件中@synthesize它,或者提供适当的访问器方法来支持该属性。

【讨论】:

rootObject 也应该输入为 Contest 而不是 id。 @Tim & jrturton,感谢您的快速输入/帮助!明天将进行测试(现在在瑞典晚上 8.20)。 快速跟进:通过将播放器属性添加到其中,我可以访问实体播放器的“名称”属性吗? @user1578933:你告诉我 - Player 对象是否公开了一个名为 name@property? (您会在“Player.h”中查找。) @Tim,yes 确实如此,而且它是一个 NSString,所以它应该可以工作。我会在我开始测试它的一个小时内找到答案。需要在我的代码中看到它的工作,然后我才能检查你的答案是否正确。再次感谢您提供非常好的和解释性的答案!

以上是关于核心数据,无法检索/设置到我的属性。最常见的错误?的主要内容,如果未能解决你的问题,请参考以下文章

用核心数据属性填充数组

操作无法完成。 (可可错误 1560。)

从核心数据中检索 UUID 并设置 UUID 变量不起作用

无法从 Access DB 查询中检索数据到我的 VB 程序

如何将通知保存到我的 android 应用程序以及如何检索数据

为啥我的视图控制器没有检索到我的 CoreData 对象