5

これはStackOverflowに関する私の最初の質問です。エチケットを破っている場合は、すみません。私はObjective-C/アプリの作成にもかなり慣れていません。

私はCS193Pスタンフォードコース、特にCoreDataの講義/デモをフォローしています。Paul HegartyのPhotomaniaアプリでは、彼はテーブルビューから始めて、UIフローを中断することなく、バックグラウンドでデータを入力します。ローカルエリアのビジネスを一覧表示するアプリケーションを作成しています(JSONデータを返すAPIから)。

Paulの写真/写真家のクラスに従ってカテゴリを作成しました。クラス自体の作成は問題ではなく、クラスが作成される場所です。

A simplified data structure:
- Section
    - Sub-section
        - business
        - business
        - business
    - business
    - business
    - business

私のアプリケーションは、いくつかのボタンを備えたUIViewControllerで始まり、それぞれが対応するセクションのテーブルビューを開きます(これらはすべて正常に機能します。質問が意味をなすように十分な情報を提供しようとしています)。この質問に基づいたUIManagedDocumentのURLを作成/開くために、ヘルパーメソッドを呼び出します。これは、アプリケーションが実行されるとすぐに呼び出され、すぐに読み込まれます。

PaulのfetchFlickrDataIntoDocumentに非常によく似たメソッドがあります。

-(void)refreshBusinessesInDocument:(UIManagedDocument *)document
{
dispatch_queue_t refreshBusinessQ = dispatch_queue_create("Refresh Business Listing", NULL);
dispatch_async(refreshBusinessQ, ^{
    // Get latest business listing
    myFunctions *myFunctions = [[myFunctions alloc] init];
    NSArray *businesses = [myFunctions arrayOfBusinesses];

    // Run IN document's thread
    [document.managedObjectContext performBlock:^{

        // Loop through new businesses and insert
        for (NSDictionary *businessData in businesses) {
            [Business businessWithJSONInfo:businessData inManageObjectContext:document.managedObjectContext];
        }

        // Explicitly save the document.
        [document saveToURL:document.fileURL 
           forSaveOperation:UIDocumentSaveForOverwriting
          completionHandler:^(BOOL success){
              if (!success) {
                  NSLog(@"Document save failed");
              }
          }];
        NSLog(@"Inserted Businesses");
    }];
});
dispatch_release(refreshBusinessQ);
}

[myFunctions arrayOfBusinesses]は、JSONデータを解析し、個々のビジネスを含むNSArrayを返します。

ビジネス作成コードの開始時と終了時にNSLogを使用してコードを実行しました。各ビジネスにはセクションが割り当てられており、作成には0.006秒かかりますが、これらは数百あります。インサートは約2秒かかります。

ヘルパーメソッドはここにあります:

// The following typedef has been defined in the .h file
// typedef void (^completion_block_t)(UIManagedDocument *document);

@implementation ManagedDocumentHelper

+(void)openDocument:(NSString *)documentName UsingBlock:(completion_block_t)completionBlock
{
    // Get URL for document -> "<Documents directory>/<documentName>"
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    url = [url URLByAppendingPathComponent:documentName];

    // Attempt retrieval of existing document
    UIManagedDocument *doc = [managedDocumentDictionary objectForKey:documentName];

    // If no UIManagedDocument, create
    if (!doc) 
    {
        // Create with document at URL
        doc = [[UIManagedDocument alloc] initWithFileURL:url];

        // Save in managedDocumentDictionary
        [managedDocumentDictionary setObject:doc forKey:documentName];
    }

    // If the document exists on disk
    if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) 
    {
        [doc openWithCompletionHandler:^(BOOL success)
         {
             // Run completion block
             completionBlock(doc);
         } ];
    }
    else
    {
        // Save temporary document to documents directory
        [doc saveToURL:url 
      forSaveOperation:UIDocumentSaveForCreating 
     completionHandler:^(BOOL success)
         {
             // Run compeltion block
             completionBlock(doc);
         }];
    }
}

そして、viewDidLoadで呼び出されます:

if (!self.lgtbDatabase) {
    [ManagedDocumentHelper openDocument:@"DefaultLGTBDatabase" UsingBlock:^(UIManagedDocument *document){
        [self useDocument:document];
    }];
}

useDocumentは、self.documentを提供されたドキュメントに設定するだけです。

このコードを変更して、データが別のスレッドに挿入され、ユーザーがボタンをクリックしてセクションを表示できるようにします。データのインポートによってUIがハングすることはありません。

私がこの問題に数日間取り組んできて、ここにある他の同様の質問があっても、それを解決することができなかったので、どんな助けでもありがたいです。他に必要な情報がありましたら、お知らせください。

ありがとうございました

編集:

これまでのところ、この質問は1票の反対票を獲得しています。この質問を改善する方法がある場合、または誰かが私が見つけられなかった質問を知っている場合、方法または場所についてコメントしていただけますか?あなたが反対票を投じる別の理由がある場合は、私は否定性を理解することができず、より良い貢献をする方法を学びたいので、私に知らせてください。

4

2 に答える 2

11

これにはいくつかの方法があります。

あなたが使用しているので、あなたは新しいものを初期化するために利用して、あなたのものをするために使うことUIManagedDocumentができます。例えば:NSPrivateQueueConcurrencyTypeNSManagedObjectContextperformBlock

// create a context with a private queue so access happens on a separate thread.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// insert this context into the current context hierarchy
context.parentContext = parentContext;
// execute the block on the queue of the context
context.performBlock:^{

      // do your stuff (e.g. a long import operation)

      // save the context here
      // with parent/child contexts saving a context push the changes out of the current context
      NSError* error = nil;
      [context save:&error];
 }];

コンテキストから保存すると、プライベートコンテキストのデータが現在のコンテキストにプッシュされます。保存はメモリにのみ表示されるため、メインコンテキスト(にリンクされているコンテキスト)にアクセスしてUIDocumentそこで保存する必要があります(does-a-core-data-parent-managedobjectcontext-need-to-shareを参照してください)。 -a-concurrency-type-wi)。

もう1つの方法(私のお気に入りの方法)は、NSOperationサブクラスを作成してそこで何かを行うことです。たとえばNSOperation、次のようなサブクラスを宣言します。

//.h
@interface MyOperation : NSOperation

- (id)initWithDocument:(UIManagedDocument*)document;

@end

//.m
@interface MyOperation()

@property (nonatomic, weak) UIManagedDocument *document;

@end

- (id)initWithDocument:(UIManagedDocument*)doc;
{
  if (!(self = [super init])) return nil;

  [self setDocument:doc];

  return self;
}

- (void)main
{ 
  NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
  [moc setParentContext:[[self document] managedObjectContext]];

  // do the long stuff here...

  NSError *error = nil;
  [moc save:&error];

  NSManagedObjectContext *mainMOC = [[self document] managedObjectContext];
  [mainMOC performBlock:^{
    NSError *error = nil;
    [mainMOC save:&error];
  }];

  // maybe you want to notify the main thread you have finished to import data, if you post a notification remember to deal with it in the main thread...
}

これで、メインスレッドで、次のようにその操作をキューに提供できます。

MyOperation *op = [[MyOperation alloc] initWithDocument:[self document]];
[[self someQueue] addOperation:op];

mainPSのメソッドで非同期操作を開始することはできませんNSOperation。終了するmainと、その操作にリンクされているデリゲートは呼び出されません。真実を言うことはできますが、これには実行ループまたは同時動作に対処することが含まれます。

お役に立てば幸いです。

于 2012-06-12T10:53:35.080 に答える
0

最初はコメントを残すつもりでしたが、特権がないと思います。変更数のオファーを超えて、UIDocumentを指摘したかっただけです - (void)autosaveWithCompletionHandler:(void (^)(BOOL success))completionHandler

「便利な瞬間」を待つため、変更カウントの更新で経験した遅延はないはずです。

于 2012-07-27T02:09:04.870 に答える