2010-08-13 33 views
13

Tôi đã đọc một số bài đăng ở đây về NSManagedObjectContext và các ứng dụng đa luồng. Tôi cũng đã đi qua ví dụ CoreDataBooks để hiểu cách các luồng riêng biệt yêu cầu NSManagedObjectContext của riêng họ và cách hoạt động lưu được hợp nhất với NSManagedObjectContext chính. Tôi tìm thấy ví dụ để được tốt, nhưng cũng quá cụ thể ứng dụng. Tôi đang cố gắng để khái quát hóa điều này, và tự hỏi liệu cách tiếp cận của tôi là âm thanh hay không.Cách tiếp cận chung cho NSManagedObjectContext trong ứng dụng đa luồng

Cách tiếp cận của tôi là có chức năng chung để tìm nạp NSManagedObjectContext cho chuỗi hiện tại. Hàm trả về NSManagedObjectContext cho luồng chính, nhưng sẽ tạo một mới (hoặc lấy nó từ một bộ nhớ đệm) nếu được gọi từ trong một luồng khác. Điều đó xảy ra như sau:

+(NSManagedObjectContext *)managedObjectContext { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *moc = delegate.managedObjectContext; 

    NSThread *thread = [NSThread currentThread]; 

    if ([thread isMainThread]) { 
     return moc; 
    } 

    // a key to cache the context for the given thread 
    NSString *threadKey = [NSString stringWithFormat:@"%p", thread]; 

    // delegate.managedObjectContexts is a mutable dictionary in the app delegate 
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts; 

    if ([managedObjectContexts objectForKey:threadKey] == nil) { 
     // create a context for this thread 
     NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease]; 
     [threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]]; 
     // cache the context for this thread 
     [managedObjectContexts setObject:threadContext forKey:threadKey]; 
    } 

    return [managedObjectContexts objectForKey:threadKey]; 
} 

Thao tác lưu đơn giản nếu được gọi từ chuỗi chính. Lưu các hoạt động được gọi từ các luồng khác yêu cầu hợp nhất trong chuỗi chính. Cho rằng tôi có một generic commit chức năng:

+(void)commit { 
    // get the moc for this thread 
    NSManagedObjectContext *moc = [self managedObjectContext]; 

    NSThread *thread = [NSThread currentThread]; 

    if ([thread isMainThread] == NO) { 
     // only observe notifications other than the main thread 
     [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(contextDidSave:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:moc]; 
    } 

    NSError *error; 
    if (![moc save:&error]) { 
     // fail 
    } 

    if ([thread isMainThread] == NO) { 
     [[NSNotificationCenter defaultCenter] removeObserver:self 
                name:NSManagedObjectContextDidSaveNotification 
                object:moc]; 
    } 
} 

Trong contextDidSave: chức năng chúng tôi thực hiện việc hợp nhất, nếu gọi bằng thông báo trong commit.

+(void)contextDidSave:(NSNotification*)saveNotification { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSManagedObjectContext *moc = delegate.managedObjectContext; 

    [moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
         withObject:saveNotification 
        waitUntilDone:YES]; 
} 

Cuối cùng, chúng tôi làm sạch-up bộ nhớ cache của NSManagedObjectContext với điều này:

+(void)initialize { 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(threadExit) 
               name:NSThreadWillExitNotification 
               object:nil]; 
} 

+(void)threadExit { 
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]]; 
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;  

    [managedObjectContexts removeObjectForKey:threadKey]; 
} 

này biên soạn và dường như làm việc, nhưng tôi biết luồng vấn đề có thể được khôn lanh do điều kiện chủng tộc. Có ai nhìn thấy một vấn đề với cách tiếp cận này?

Ngoài ra, tôi đang sử dụng điều này từ trong bối cảnh yêu cầu không đồng bộ (sử dụng ASIHTTPRequest), tìm nạp một số dữ liệu từ máy chủ và cập nhật và chèn cửa hàng trên iPhone. Dường như NSThreadWillExitNotification không kích hoạt sau khi yêu cầu hoàn thành, và cùng một luồng được sử dụng cho các yêu cầu tiếp theo. Điều này có nghĩa là cùng một NSManagedObjectContext được sử dụng cho các yêu cầu riêng biệt trên cùng một luồng. Đây co phải vân đê?

+0

chris, tôi đang gặp phải sự cố đa luồng tương tự khi sử dụng một NSManagedObjectContext được tạo trong chuỗi chính cho tất cả các hoạt động trong Hàng đợi NSoperation. Các probkem đến khi mỗi thread cố gắng để lưu bối cảnh, các ứng dụng treo ngẫu nhiên ném một ngoại lệ từ dữ liệu cốt lõi. tôi đã suy nghĩ để khóa bối cảnh này trong khi sử dụng trong tất cả các hoạt động, để mỗi người trong số họ sẽ có quyền truy cập độc quyền của bối cảnh. Tôi đã đọc ur trên solution.sounds gud, couls u xin vui lòng dán cho tôi mã mới mà u đã sử dụng để sáp nhập bối cảnh và cũng xin vui lòng bình luận về cách sử dụng một khóa cho playin –

Trả lời

8

Một năm sau khi đăng câu hỏi này tôi cuối cùng đã xây dựng một khuôn khổ để khái quát hóa và đơn giản hóa việc làm việc của tôi với Dữ liệu cốt lõi. Nó vượt ra ngoài câu hỏi ban đầu và thêm một số tính năng để làm cho tương tác của Dữ liệu cốt lõi dễ dàng hơn nhiều.Chi tiết tại đây: https://github.com/chriscdn/RHManagedObject

0

Tôi đã tìm thấy giải pháp sau khi cuối cùng cũng hiểu được vấn đề tốt hơn. Giải pháp của tôi không trực tiếp giải quyết các câu hỏi trên, nhưng không giải quyết vấn đề tại sao tôi phải đối phó với các chủ đề ở nơi đầu tiên.

Ứng dụng của tôi sử dụng thư viện ASIHTTPRequest cho các yêu cầu không đồng bộ. Tôi lấy một số dữ liệu từ máy chủ và sử dụng chức năng delegate requestFinished để thêm/sửa đổi/xóa các đối tượng dữ liệu cốt lõi của tôi. Hàm requestFinished đang chạy trong một chuỗi khác và tôi cho rằng đây là tác dụng phụ tự nhiên của các yêu cầu không đồng bộ.

Sau khi đào sâu hơn tôi thấy rằng ASIHTTPRequest cố tình chạy theo yêu cầu trong một thread riêng biệt, nhưng có thể được ghi đè trong lớp con của tôi về ASIHTTPRequest:

+(NSThread *)threadForRequest:(ASIHTTPRequest *)request { 
    return [NSThread mainThread]; 
} 

thay đổi nhỏ này đặt requestFinished trong thread chính, trong đó đã loại bỏ sự cần thiết của tôi để quan tâm đến các chủ đề trong ứng dụng của tôi.

+0

Tôi không hoàn toàn chắc chắn tôi đã hiểu. ASIHTTPRequest không sử dụng một luồng riêng biệt (thực sự là một NSOperationQueue) cho các yêu cầu không đồng bộ, nhưng bằng nhau nó luôn chạy requestFinished trên mainthread. (Trong mã mới nhất, chức năng này nằm trong phương thức callSelectorOnMainThread.) Điều đó nói rằng tôi không thể thấy bất kỳ bất lợi nào đối với giải pháp của bạn. – JosephH

+0

Tôi thấy rằng requestFinished đã không chạy trên thread chính cho đến khi tôi thêm ba dòng trên. Có thể điều này đã thay đổi với ASIHTTPRequest không? Tôi đang sử dụng v1.7. – chris

+0

Bạn chính xác. Các đại biểu ASIHTTPRequest được chạy trên thread chính, nhưng tôi đã thực hiện một phân lớp của ASIHTTPRequest và đặt mã của tôi trong phương thức 'requestFinished:'. Điều này không nhất thiết phải được gọi trên chủ đề chính. cảm ơn – chris

Các vấn đề liên quan