好的,我对此有点迷失,我目前正在尝试使用第二个 ManagedObjectContext 运行后台核心数据操作,其类型设置为 NSPrivateQueueConcurrencyType 并因上述错误而惨遭失败。
我有一个 NSOperation 的自定义子类,它被传递一个字符串的 NSArray 和来自主线程的 PersistentStoreCoordinator,然后它创建自己的 ManagedObjectContext,运行查询并执行和操作。
这是该课程的代码:
//
// ProcessProfanity.m
// Hashtag Live Desktop
//
// Created by Gareth Jeanne on 24/03/2014.
// Copyright (c) 2014 Gareth Jeanne. All rights reserved.
//
#import "ProcessProfanity.h"
#import "Tweet.h"
static const int ImportBatchSize = 250;
@interface ProcessProfanity ()
@property (nonatomic, copy) NSArray* badWords;
@property (nonatomic, strong) NSManagedObjectContext* backgroundContext;
@property (nonatomic, strong) NSPersistentStoreCoordinator* persistentStoreCoordinator;
@end
@implementation ProcessProfanity
{
}
- (id)initWithStore:(NSPersistentStoreCoordinator*)store badWords:(NSArray*)words
{
self = [super init];
if(self) {
self.persistentStoreCoordinator = store;
self.badWords = words;
}
return self;
}
- (void)main
{
_backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_backgroundContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
_backgroundContext.undoManager = nil;
[_backgroundContext performBlockAndWait:^
{
[self import];
}];
}
- (void)import
{
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:@"Tweet" inManagedObjectContext:self.backgroundContext]];
NSError *error = nil;
//Create an array from the returned objects
NSArray* tweetsToProcess = [self.backgroundContext executeFetchRequest:request error:&error];
NSAssert2(tweetsToProcess != nil && error == nil, @"Error fetching events: %@\n%@", [error localizedDescription], [error userInfo]);
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSString *result = nil;
[self.badWords indexOfObjectWithOptions:NSEnumerationConcurrent
passingTest:^(NSString *obj, NSUInteger idx, BOOL *stop)
{
if (tweetToCheck){
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
{
result = obj;
*stop = YES;
//return YES;
}
}
return NO;
}];
if (!result){
//DDLogVerbose(@"The post does not contain any of the words from the naughty list");
if(tweetToCheck){
tweetToCheck.profanity = [NSNumber numberWithBool:false];
}
}
else{
if(tweetToCheck){
//DDLogVerbose(@"The string contains '%@' from the the naughty list", result);
tweetToCheck.profanity = [NSNumber numberWithBool:true];
}
}
}
[self.backgroundContext save:NULL];
}
@结尾
这就是我所说的:
-(void)checkForProfanity{
if(!self.operationQueue){
self.operationQueue = [[NSOperationQueue alloc] init];
}
NSArray* termsToPass = [self.filterTerms copy];
ProcessProfanity* operation = [[ProcessProfanity alloc] initWithStore:self.persistentStoreCoordinator badWords:termsToPass];
[self.operationQueue addOperation:operation];
}
编辑 1
我似乎遇到错误的特定行,或者至少 Xcode 中断的地方是:
if ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)
我设法缩小了一点,包含要搜索字符串的术语列表的 NSArray 可能非常大,可能超过 1,000 个 NSString。如果我用那个大小的数组进行测试,我就会遇到问题。但是,如果我将数组减少到大约 15 个 NSString,我不会收到错误,所以我认为这不一定是线程相关的问题,我想知道数组是否在主线程中被释放。我已修改代码以进行深层复制,然后按如下方式进行 __block 复制,但似乎没有帮助。
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
和
for (Tweet* tweetToCheck in tweetsToProcess){
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
__block NSString *result = nil;
[array indexOfObjectWithOptions:NSEnumerationConcurrent
事实上,在 Xcode 中断的地方,如果我 PO 数组,我得到一个找不到对象的消息,但如果我 PO 结果,我正确地得到一个返回的对象,它是 nil。
编辑 2
所以我做了以下改变,没有改变:
使 NSArray 强大而不是复制:
@property (nonatomic, strong) NSArray* badWords;
并在分配时将其复制:
self.badWords = [[NSArray alloc] initWithArray:words copyItems:YES];
并在处理对象的实际方法中使用 ___block 声明创建了 NSArray 的本地副本:
__block NSArray *array = [[NSArray alloc] initWithArray:self.badWords copyItems:YES];
这肯定意味着它会在 ProcessProfanity 对象的生命周期内一直存在?
我期望能够从块内的断点 PO 数组是错误的吗?