核心数据对象变为空

时间:2014-04-29 06:33:00

标签: ios iphone objective-c core-data restkit-0.20

我正在创建一个应该支持离线模式的任务应用程序。我已经使用RestKit下载任务并将其映射到本地Core数据中。

这在在线模式下运作良好。但在线下有一个奇怪的问题。我使用NSPredicate从本地存储中获取数据。为此我使用魔法记录。

+ (void)getIdeasTasksWithPageNo:(int)pageNo completionHandler:(void (^)(NSArray *, NSError *))completionHandler {

    NSArray *tasks = [self MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"due_date = nil AND user_id = %@", [DBUsers currentUser].id]];
    completionHandler(tasks, nil);
}

我称之为:

[DBTasks getIdeasTasksWithPageNo:1 completionHandler:^(NSArray *tasks, NSError *error) {
            if (!error) {
                [self displayTasksWithResults:tasks forPageNo:1];                   

            } else {
                NSLog(@"Error is %@", error);
            }
        }];

这就是我在UITableView中显示它的方式

-(void)displayTasksWithResults:(NSArray *)tasks forPageNo:(int)pageNo {
    if (!self.tasksArray) {
        self.tasksArray = [[NSMutableArray alloc] init];

    } else {
        [self.tasksArray removeAllObjects];
    }
    [self.tasksArray addObjectsFromArray:tasks];
    [self.tableview reloadData];
}

这仅适用于第一次,所有任务都填入UITableView

问题是在填充UITableView后,self.tasksArray中的所有记录都变为Null。如果我滚动UITableView,表行开始为空。

但如果我在self.tasksArray方法中打印displayTasksWithResults,则会完美打印。

(
    "Title: Task 01",
    "Title: You've gone incognito. Pages you view in incognito tabs won't stick around in your browser's history, cookie store, or search history after you've closed all of your incognito tabs. Any files you download or bookmarks you create will be kept. ",
    "Title: Task 06",
    "Title: Task 04",
    "Title: Hi",
    "Title: Task 3",
    "Title: Task 4",
    "Title: Hi 4",
    "Title: hh",
    "Title: Task 02",
    "Title: Task 05\n",
    "Title: Task 4",
    "Title: Task 5",
    "Title: Task 2 updated",
    "Title: Here is a task. ",
    "Title: Task 03",
    "Title: Hi 3",
    "Title: Task 2",
    "Title: Hi 2",
    "Title: Testing task email with Idea Task",
    "Title: Task f6",
    "Title: 1.117",
    "Title: Task f5",
    "Title: Task f12",
    "Title: Task f4",
    "Title: Task f3",
    "Title: 111.0.113",
    "Title: 111.0.115",
    "Title: Pages you view in incognito tabs won't stick around in your browser's history, cookie store, or search history after you've closed all of your incognito tabs. Any files you download or bookmarks you create will be kept.",
    "Title: Task f7",
    "Title: 1.116",
    "Title: 1.118",
    "Title: Going incognito doesn't hide your browsing from your employer, your internet service provider, or the websites you visit. ",
    "Title: 111.0.111"
)

如果我稍后打印self.taskArray,可能会在didSelectRow的{​​{1}}代表中,则会打印如下:

UITableView

我认为这可能与( "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)", "Title: (null)" ) 有关,但不知道如何修复它。

请帮忙!

6 个答案:

答案 0 :(得分:16)

问题是(正如我在评论中写的那样)对象是在后台线程上获取的,但是在主(UI)线程上使用。托管对象只能在上下文中“生存” 它们是在中创建的。如果上下文被释放,对象仍然存在,但是 属性访问器方法只返回nil

可能的解决方案:

  • 获取主线程上的对象。
  • 使用

    NSManagedObject *copy = [[mainContext objectWithID:[object objectID]];
    

    将对象从背景上下文“复制”到主上下文。 (也许是MagicalRecord 有一个方便的方法。)

  • 不是提取托管对象,而是设置

    [fetchRequest setResultType:NSDictionaryResultType];
    [fetchRequest setPropertiesToFetch:@[@"title", ...]];
    

    使用您感兴趣的属性获取词典的数组。

答案 1 :(得分:3)

终于找到了Null对象的原因。我在后台打电话。因此,Magical Records在NSManagedObject+MagicalFinders.m

中为该线程创建了一个新的托管对象上下文
+ (NSManagedObjectContext *) MR_contextForCurrentThread;
{
    if ([NSThread isMainThread])
    {
        return [self MR_defaultContext];
    }
    else
    {
        NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
        NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
        if (threadContext == nil)
        {
            threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
            [threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
        }
        return threadContext;
    }
}

所以我需要将对象从后台线程上下文复制到主线程上下文。我终于找到了解决方案here并创建了我的方法。

+ (void)backgroundFetchWithPredicate:(NSPredicate *)predicate completion:(void(^)(NSArray *, NSError *))completion {
    NSManagedObjectContext *privateContext = [NSManagedObjectContext MR_context];
    [privateContext performBlock:^{
        NSArray *privateObjects = [self MR_findAllWithPredicate:predicate inContext:privateContext];
        NSArray *privateObjectIDs = [privateObjects valueForKey:@"objectID"];
        // Return to our main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSPredicate *mainPredicate = [NSPredicate predicateWithFormat:@"self IN %@", privateObjectIDs];
            NSArray *finalResults = [self MR_findAllWithPredicate:mainPredicate];
            completion(finalResults, nil);
        });
    }];
}

现在我只需要这样称呼它:

[self backgroundFetchWithPredicate:predicate completion:^(NSArray *results, NSError *error) {
            completionHandler(results, error);
        }];

答案 2 :(得分:0)

我想这是因为你直接将值赋给数组。尝试使用mutablecopy

分配值
NSArray *tasks = [[self MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"due_date = nil AND user_id = %@", [DBUsers currentUser].id]] mutableCopy];

答案 3 :(得分:0)

我在您的代码中发现了几个弱点,尝试解决它们:

  1. 您永远不会在pageNo

  2. 中使用+ (void)getIdeasTasksWithPageNo:(int)pageNo completionHandler:(void (^)(NSArray *, NSError *))completionHandler参数
  3. 您在上述方法中始终为error参数传递nil

  4. 从数组[self.tasksArray removeAllObjects];中删除对象的目的是什么? (首先尝试评论这一行可能就是这个原因)

答案 4 :(得分:0)

首先,检查您是否在多线程环境中使用CoreData,如果这样做,您可能需要添加一些调度队列代码以确保您的CoreData代码仅在一个线程上运行。 其次,调用结果数组的其他一些方法来查看NSManagedObject是否处于故障状态,调用它的某些方法将使其被提取。

答案 5 :(得分:0)

此外,对字段中具有意外Null值的NSManagedObject类的实现执行一次预防性检查。 在NSManagedObject类的.m文件中,确保为字段执行@dynamic,而不是由于某种原因,错误地执行@synthesize。

// NullFieldsCoreDataObject.h
@interface NullFieldsCoreDataObject : NSManagedObject
@property (nonatomic, retain) NSString * nullField;
@property (nonatomic, retain) NSString * anotherOneNullField;
@property (nonatomic, retain) NSNumber * andAnotherOneNullField;
@end

// NullFieldsCoreDataObject.m
#import "NullFieldsCoreDataObject.h"

@implementation NullFieldsCoreDataObject
// wrong:
//@synthesize nullField, anotherOneNullField, andAnotherOneNullField;
// correctly:
@dynamic nullField, anotherOneNullField, andAnotherOneNullField;
@end
相关问题