Quello che sto cercando di fare:risparmio NSManagedObjectContext senza colpire il thread principale
- eseguire sincronizzazione in background con un'API web senza congelare l'interfaccia utente. Sto usando MagicalRecord ma non è proprio specifico.
- assicurarsi che sto utilizzando contesti & come correttamente
Che la mia domanda è in realtà: è la mia comprensione corretta? Più un paio di domande alla fine.
Così, i contesti messi a disposizione dalla MagicalRecord sono:
- MR_rootSavingContext di PrivateQueueConcurrencyType che viene utilizzato a persistere i dati al negozio, che è un processo lento
- MR_defaultContext di MainQueueConcurrencyType
- e per lo sfondo si vorrebbe lavorare con un contesto generi Ted da MR_context(), che è un figlio di MR_defaultContext ed è di PrivateQueueConcurrencyType
Ora, per salvare in modo asincrono, abbiamo due opzioni:
- MR_saveToPersistentStoreWithCompletion() che salverà fino a MR_rootSavingContext e scrivere sul disco
- MR_saveOnl ySelfWithCompletion() che salverà solo fino al contesto genitore (io? MR_defaultContext per un contesto creato con MR_context)
Da lì, ho pensato che avrei potuto fare quanto segue (chiamiamolo Tentativo # 1) senza congelare l'interfaccia utente:
let context = NSManagedObjectContext.MR_context()
for i in 1...1_000 {
let user = User.MR_createInContext(context) as User
context.MR_saveOnlySelfWithCompletion(nil)
}
// I would normally call MR_saveOnlySelfWithCompletion here, but calling it inside the loop makes any UI block easier to spot
Ma, la mia ipotesi era sbagliato. I looked into MR_saveOnlySelfWithCompletion e ho visto che si basa su
[self performBlock:saveBlock];
esegue in modo asincrono un dato blocco sulla coda del ricevitore.
Quindi ero un po 'perplesso, dal momento che mi aspetterei di non bloccare l'interfaccia utente a causa di ciò.
Poi ho provato (chiamiamolo Tentativo # 2)
let context = NSManagedObjectContext.MR_context()
for i in 1...1_000 {
let user = User.MR_createInContext(context) as User
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {() -> Void in
context.MR_saveOnlySelfWithCompletion(nil)
}
}
E questo fa il lavoro, ma non mi sembra giusto.
Poi ho trovato qualcosa nel release notes of iOS 5.0
Quando l'invio di messaggi a un contesto creato con un'associazione di coda , è necessario utilizzare il performBlock: o performBlockAndWait: metodo se il codice non è già in esecuzione su quel coda (per il tipo di coda principale ) o nell'ambito di un'attivazione PerformBlock ... (per il tipo di coda privata). All'interno dei blocchi passati ai metodi , è possibile utilizzare liberamente i metodi di NSManagedObjectContext.
Quindi, sto supponendo che:
- Tentativo # 1 si blocca l'interfaccia utente in quanto realtà sto chiamando dalla coda principale e non nell'ambito di un performBlock
- Tentativo # 2 opere, ma sto creando ancora un altro thread mentre il contesto ha già un proprio thread in background
Così of course che cosa devo fare è l'uso saveWithBlock:
01.235.164,106174 millionsMagicalRecord.saveWithBlock { (localContext) -> Void in
for i in 1...1_000 {
User.MR_createInContext(context)
}
}
Esegue l'operazione on a direct child of MR_rootSavingContext che è di PrivateQueueConcurrencyType. Grazie a rootContextChanged, qualsiasi modifica che risale a MR_rootSavingContext sarà disponibile per MR_defaultContext.
così sembra che:
- MR_defaultContext è il contesto perfetto quando si tratta di visualizzazione dei dati
- modifiche sono preferibilmente fatte in un MR_context (figlio di MR_defaultContext)
- attività in esecuzione lunghi come ad esempio un la sincronizzazione del server viene preferibilmente eseguita utilizzando saveWithBlock
Ciò che ancora non si ottiene è come lavorare con MR_save [...] WithCompletion(). Lo userei su MR_context ma visto che ha bloccato il thread principale nei miei casi di test, non vedo quando diventa rilevante (o cosa mi è mancato ...).
Grazie per il vostro tempo :)
Grazie, risposta interessante. "Non è necessario salvare per ogni creazione di oggetti." - Effettivamente, l'ho fatto per assicurarmi che qualsiasi blocco sul thread principale fosse facile da individuare, avrei dovuto dirlo. MR_saveOnlySelfWithCompletion chiama internBlock internamente, ecco perché non capisco perché l'interfaccia utente sia bloccata. – Nycen
performBlock NON garantisce che verrà eseguito su un thread in background ma sul thread il contesto è vincolato. Se questo è il contesto principale, bloccherà normalmente. Fondamentalmente, pensa che mentre sul thread principale, chiamare un metodo di contesto all'esterno o all'interno di performBlock è lo stesso! Quindi assicurati che il tuo contesto non sia il principale. (Ho modificato la mia risposta) –
Mentre la tua risposta è fantastica, non sarei d'accordo con "Non c'è bisogno di risparmiare per ogni creazione di oggetti". Nella maggior parte dei casi questo può essere vero, ma se quel particolare contesto che stai modificando deve spingere le modifiche frequentemente in altri contesti, potrebbe esserci una necessità. Così come altri, che non puoi prevedere :) – Tim