3

わかりました、これで少し迷っています。現在、タイプが NSPrivateQueueConcurrencyType に設定された 2 番目の ManagedObjectContext を使用してバックグラウンド コア データ操作を実行しようとしていますが、上記のエラーで惨めに失敗しています。

文字列の NSArray が渡される NSOperation のカスタム サブクラスと、メイン スレッドからの 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 できると期待するのは間違っていますか?

4

2 に答える 2

4

この場合、エラー メッセージ "error: NULL _cd_rawData but the object is not beingturned into a fault" は、コンテキスト外の管理対象オブジェクトにアクセスしていることを示しています。基本的に、フェッチは永続ストアからすべてのツイートをエラーとして返します。管理対象オブジェクトのプロパティにアクセスしようとすると、Core Data によってエラーが発生し、ストアから完全なオブジェクトがフェッチされます。

indexOfObjectWithOptions:passingTest:オプションを指定して NSArray メソッドを呼び出すことNSEnumerationConcurrentで、配列内の要素に対して非同期実行を実行することを意味します。キーワード concurrent は、複数のスレッドを使用して配列要素を操作できることを示します。

あなたのコンテキストでは、これは、このブロック内の管理対象オブジェクトにアクセスすると、オブジェクトを所有する管理対象オブジェクト コンテキストとは別のスレッドでアクセスする可能性があることを意味します。したがって、条件付きチェック - で tweetToCheck.text にアクセスすると、内部でif ([tweetToCheck.text rangeOfString:obj].location != NSNotFound)Core Data がその管理対象オブジェクトを永続ストアからフェッチし、それを管理対象オブジェクト コンテキスト スレッドの一部ではないスレッドに返します。

さらに、indexOfObjectWithOptions:passingTest:実際にはこの操作の結果に関心がないため、メソッドを使用する必要はありません。

特定のつぶやきの単語が冒涜的な単語に含まれているかどうかをテストするだけなので、NSSet を使用する方が便利なように思えます。NSSetのドキュメントを引用すると、「要素の順序が重要ではなく、オブジェクトがセットに含まれているかどうかをテストする際のパフォーマンスが考慮される場合、配列の代わりにセットを使用できます」。明らかに、これはあなたの基準を満たしているようです。

したがって、initは次のようになります。

 -(id)initWithStore:(NSPersistentStoreCoordinator*)store 
           badWords:(NSSet*)badWords
{
   self = [super init];
   if(self) {
     self.persistentStoreCoordinator = store;
     self.badWords = [words copy];
   }
   return self;
}

冒涜のタグが付けられていないツイートの更新のみに関心があるため、おそらく、冒涜のフラグが付けられていないツイートのみを取得する必要があります。

//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];

//Setup the Request
[request setEntity:[NSEntityDescription entityForName:@"Tweet" inManagedObjectContext:self.backgroundContext]];
[request setPredicate:[NSPredicate predicateWithFormat:@"profanity = NO"]];

冒涜的ではないツイートの配列ができたので、ツイートを反復処理して、各単語に冒涜的な単語が含まれているかどうかを確認できます。対処する必要がある唯一のことは、ツイートを単語に分割する方法です (カンマや感嘆符などを無視します)。次に、単語ごとに分音記号を取り除き、おそらく大文字と小文字を区別する必要があります。したがって、次のような人になってしまいます。

if([self.badWords containsObject:badWordString]) {
    currentTweet.profanity = [NSNumber numberWithBOOL:YES];
}

NSSet で述語を実行できるので、大文字と小文字を区別しないクエリを実際に実行できることを思い出してください。

NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"SELF = %@[cd]",wordToCheck];
BOOL foundABadWord = ([[[self.badWords filteredSetUsingPredicate:searchPredicate] allObjects] count] > 0);

考慮したいもう 1 つのことは、ツイート内の重複した単語を削除することです。同じチェックを複数回実行したくはありません。したがって、パフォーマンスをどのように見つけるかによって、ツイートの各単語を NSSet に配置し、ツイート内の一意の単語に対してクエリを実行するだけで済みます。

if([[self.badWords intersectsSet:tweetDividedIntoWordsSet]) {
    //we have a profane tweet here!
}

どの実装を選択するかはあなた次第ですが、アプリで英語のみを使用していると仮定すると、大文字と小文字を区別しない検索を実行したいと思うでしょう。

編集

最後に注意すべきことは、どれだけ努力しても、冒とく的な言葉や虐待的な言葉を検出する最良の手段は常に人であるということです。冒とく的な表現の検出に関するこの SO の投稿を読むことをお勧めします - How do you implement a good profanity filter?

于 2014-03-26T15:52:32.617 に答える