0

Ich habe Probleme mit der Architektur meines Codes. Ich habe view, die erstellen und hängen an model Klasse. Modellklasse ist verantwortlich für alle Berechnungen und Logik. Dies bedeutet, dass es viele Dinge zu berechnen hat und Objekte aus Core-Daten erhalten.NSManagedObject in verschiedenen Threads abrufen und verwenden

enter image description here

Bezüglich enter link description here jedes NSManagedObject, die in einem Thread erhalten wird, muß in demselben Thread verwendet werden.

Mein Problem ist, dass auf erstellen Objekt in verschiedenen Thread erhalten werden muss, weil es einige Zeit dauert, um Modell zu bauen, aber danach muss ich Objekt und sie Eigenschaften im Hauptthread von View erhalten (zum Beispiel in CellForIndex ...)

Ich weiß, ich bin nicht der einzige mit diesem Design. Wie andere dieses Problem lösen?


EDIT:

dies mit Code betonieren.

Lassen Sie sagen, wir UIView Objekt MyUIView und Modellobjekt Model

MyUIView

@interface MyUIView() 

@property(nonatomic) Model * model; 

@end 

@implementation MyUIView 

- (void) createModel 
{ 
    // privateManagerContext is context manager created with 
    // NSPrivateQueueConcurrencyType, connected to persistent store 
    // and sync with "main thread" manager context with 
    // NSManagedObjectContextDidSaveNotification and NSManagedObjectContextDidSaveNotification 

    [self.model createWithManagadContext:privateManagerContext]; 
} 

// ASSUME THAT THIS CODE IS CALLED AFTER 
- (void) getNumberOfSomeProperties 
{ 
    int number = [self.model getNumberOfProperties]; 
} 

- (void) getProperties 
{ 
    NSArray *array = [self.model properties] 
} 

// OR WE HAVE TO TRIGGERED SOME LONG CALCULATION 

- (void) triggerLongCalculation 
{ 
    [self.model longCalculation]; 
} 

- (void) afterNotifyModelIsCompletedCalculating 
{ 
    [self doSomeWork]; 
    [self getProperties]; 
    .... 
} 
@end 

Modell

@interface Model() 

@property(nonatomic) NSArray * cashedProperties; 

@end 

@implementation MyUIView 

- (void) createWithManagadContext:(NSManagedObjectContext *) privateManagerContext 
{ 
    dispatch_async(self.model_queue, ^(void){ 

     // Here we fetch objects and do some calculations 
     [self populateModel]; 

     /// Model is complete 
     [Notifications notifyModelIsCompletedCreating:self]; 
    }); 
} 


- (void) longCalculation 
{ 
    dispatch_async(self.model_queue, ^(void){ 
     // NO CORE DATA FETCH INVOLVED 
     [Notifications notifyModelIsCompletedCalculating:self]; 
    }); 
} 

- (int) getNumberOfProperties 
{ 
    return self.cashedProperties.count; 
} 

- (NSArray) properties 
{ 
    NSMutableArray * a = [[NSMutableArray alloc]init]; 
    for (Property * p in self.cashedProperties) { 
     [a addObject:p.name]; 
    } 

    return a; 
} 

@end 
01 haben

Wie würden Sie also in diesen hypothetischen Klassen mit NSManagedObject und NSManagedObjectContext umgehen?

EDIT 2:


I Muster sind mit dem ich erstelle zwei verwaltete Objektkontext in AppDelegate, einem privaten und eine Haupt-und schaffen sync zwischen ihnen.

- (NSManagedObjectContext *)managedObjectContext 
{ 
    if (__managedObjectContext != nil) { 
     return __managedObjectContext; 
    } 

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 
    if (coordinator != nil) { 
     __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 
     [__managedObjectContext setPersistentStoreCoordinator:coordinator]; 
    } 
    return __managedObjectContext; 
} 

- (NSManagedObjectContext *) privateQueueContext 
{ 
    if (_privateQueueContext != nil) { 
     return _privateQueueContext; 
    } 

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 
    if (coordinator != nil) { 
     _privateQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
     [_privateQueueContext setPersistentStoreCoordinator:coordinator]; 
    } 
    return _privateQueueContext; 
} 


- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     [[NSNotificationCenter defaultCenter] addObserver:self 
               selector:@selector(contextDidSavePrivateQueueContext:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:[self privateQueueContext]]; 
     [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(contextDidSaveMainQueueContext:) 
               name:NSManagedObjectContextDidSaveNotification 
                object:[self managedObjectContext]]; 
    } 
    return self; 
} 

#pragma mark - Notifications 

- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification 
{ 
    @synchronized(self) { 
     [self.managedObjectContext performBlock:^{ 

      NSArray* objects = [notification.userInfo valueForKey:NSUpdatedObjectsKey]; 
      for (NSManagedObject* obj in objects) { 
       NSManagedObject* mainThreadObject = [self.managedObjectContext objectWithID:obj.objectID]; 
       [mainThreadObject willAccessValueForKey:nil]; 
      } 

      [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
     }]; 
    } 
} 

- (void)contextDidSaveMainQueueContext:(NSNotification *)notification 
{ 
    @synchronized(self) { 
     [self.privateQueueContext performBlock:^{ 

      NSArray* objects = [notification.userInfo valueForKey:NSUpdatedObjectsKey]; 
      for (NSManagedObject* obj in objects) { 
       NSManagedObject* mainThreadObject = [self.privateQueueContext objectWithID:obj.objectID]; 
       [mainThreadObject willAccessValueForKey:nil]; 
      } 

      [self.privateQueueContext mergeChangesFromContextDidSaveNotification:notification]; 
     }]; 
    } 
} 
+0

Sie können das Objekt in den Hauptthread laden, oder Sie können eine nicht verwaltete Version des Objekts erstellen und diese nach Abschluss der Berechnungen an den Hauptthread übergeben. – Avi

Antwort

1

Sie könnten zwei verschiedene NSManagedObjectContext mit Eltern/Kind-Konfiguration verwenden. Das übergeordnete Element für die Benutzeroberfläche in der Hauptwarteschlange, das untergeordnete Element für schwere Arbeit in der privaten Warteschlange. Sobald der Kindkontext beendet ist, um seine schwere Arbeit auszuführen, werden seine Änderungen dann in den Hauptkontext übertragen. Sie können für den Fall, dass Sie eine Tabellenansicht in Ihrem View-Controller verwenden, einen NSFetchedResultsController verwenden, der im Hauptkontext beobachtet wird. Sobald der Hauptkontext die Änderungen von seinem untergeordneten Kontext empfängt und zusammenführt, aktualisiert der NSFetchedResultsController die Benutzeroberfläche entsprechend, solange seine Delegate-Methoden implementiert sind.

Wenn Sie nicht NSFRC verwenden Sie Ihre Haupt-Kontext für Benachrichtigungen registrieren konnte „name: NSManagedObjectContextObjectsDidChangeNotification“ oder „Name: NSManagedObjectContextObjectsDidSaveNotification“ und sehen, welche Objekte hinzugefügt wurden/gelöscht/aktualisiert und aktualisiert damit die Benutzeroberfläche.

Wenn Sie stattdessen thread confinement (von Apple in den letzten Jahren veraltet) von parent/child verwenden, möchten Sie vielleicht die objectID zwischen threads übergeben und das Objekt im Kontext abrufen, da NShandagedObjects nicht Thread-sicher sind .

Code:

Zuerst würde ich nicht eine Referenz Ihres Modells in der Ansicht haben. View sollte nur dumm sein und Outlets oder zu bearbeitende Methoden verfügbar machen. Ich würde einen ViewController haben, der mit dem Modell und der Ansicht kommuniziert.

Nehmen wir an, Sie haben eine util-Methode, um einen Worker-Kontext (als untergeordneter Kontext des übergeordneten Elements in der Hauptwarteschlange) jedes Mal zu erstellen, wenn Sie einen schweren Job ausführen müssen.

func newWorkerContext() -> NSManagedObjectContext { 
    let workerContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) 
    workerContext.parentContext = self.mainContext 
    return workerContext 
} 

In Ihrem Modell würden Sie

//Lets say you have a reference to your workerContext 
func longCalculation() { 
    workerContext.performBlock({() -> Void in 
      //Heavy process of information 
      //Once finished you save the worker context and changes are propagated to the parent context  
    }) 

} 

In Ihrem Viewcontroller haben würden Sie haben

class MyViewController: UIViewController { 

    func viewDidLoad() { 
     super.viewDidLoad() 

     NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(handleDataModelChange(_:)), name: NSManagedObjectContextObjectsDidChangeNotification, object: REFERENCE_TO_MAIN_CONTEXT) 
    } 

    func deinit { 
     NSNotificationCenter.defaultCenter().removeObserver(self, name: NSManagedObjectContextObjectsDidChangeNotification, object: REFERENCE_TO_MAIN_CONTEXT) 
    } 

func handleDataModelChange(notification: NSNotification) { 

    //Check changes are relevant for the UI you are updating and if so update. 
    if let changedObjects = changes[NSUpdatedObjectsKey] as? NSSet { 
    } 
    if let insertedObjects = changes[NSInsertedObjectsKey] as? NSSet { 
    } 
    if let deletedObjects = changes[NSDeletedObjectsKey] as? NSSet { 
    } 
} 
} 

dass Denken Sie daran, die Änderungen in der persistenten Speicher bestehen bleiben Sie auf der Haupt zu retten Kontext. Es ist ein einfaches Beispiel, aber ich hoffe, es gibt Ihnen die Idee, was jetzt zu tun ist.

+0

Danke für die Antwort. In der Theorie verstehe ich, was Sie meinen, aber ich aktualisiere meine Frage, wenn Sie in der Lage sein werden, konkreter zu antworten. –

+0

@MarkoZadravec hast du versucht, den Ansatz, den ich vorgeschlagen habe ?? – ubiAle

+0

Ich sehe deine Antwort, danke. Ich aktualisiere meine Frage. Ich verwende NSManagedObjectContextDidSaveNotification in einem Anwendungsdelegaten anstelle von NSManagedObjectContextObjectsDidChangeNotification in UIViewController. –

Verwandte Themen