Aula 188: Multithreading com Core Data
Core Data é uma poderosa estrutura fornecida pela Apple para gerenciar os objetos da camada de modelo em um aplicativo. É essencial entender como trabalhar com Core Data em um ambiente multithread para garantir que seu aplicativo permaneça responsivo e eficiente. Nesta aula, abordaremos os conceitos básicos de uso do Core Data com multithreading em Swift.
Por que Multithreading com Core Data?
Ao trabalhar com grandes conjuntos de dados ou realizar operações que exigem muitos recursos, é crucial descarregar essas tarefas do thread principal. Isso ajuda a manter a interface do usuário responsiva e melhora a experiência geral do usuário. No entanto, o Core Data não é seguro para threads, o que significa que você precisa lidar com a concorrência com cuidado.
Tipos de Concorrência do Core Data
O Core Data fornece vários tipos de concorrência:
-
Tipo de Concorrência de Fila Principal: Este é o tipo de concorrência padrão usado para tarefas relacionadas à interface do usuário. Ele opera na thread principal e é ideal para buscar ou salvar dados que atualizam a interface diretamente.
-
Tipo de Concorrência de Fila Privada: Este tipo permite criar uma fila separada para executar tarefas de longa duração. É projetado para operações em segundo plano e é mais seguro de usar ao lidar com conjuntos de dados maiores.
Criando uma Pilha de Core Data
Antes de mergulharmos no multithreading, vamos criar uma pilha básica de Core Data. Para este exemplo, definiremos um modelo simples de Core Data com uma entidade chamada Usuario
.
import UIKit
import CoreData
class PilhaCoreData {
static let compartilhada = PilhaCoreData()
lazy var containerPersistente: NSPersistentContainer = {
let container = NSPersistentContainer(name: "NomeDoSeuModelo")
container.loadPersistentStores(completionHandler: { (descricaoLoja, erro) in
if let erro = erro as NSError? {
fatalError("Erro não resolvido \(erro), \(erro.userInfo)")
}
})
return container
}()
var contexto: NSManagedObjectContext {
return containerPersistente.viewContext
}
}
Buscando Dados em uma Thread de Fundo
Vamos ver como buscar dados usando uma thread de fundo. Para isso, usaremos um contextoGerenciadoPrivado
criado a partir do nosso container persistente.
func buscarUsuarios(completions: @escaping ([Usuario]?) -> Void) {
let contextoDeFundo = PilhaCoreData.compartilhada.containerPersistente.newBackgroundContext()
contextoDeFundo.perform {
let requisicaoFetch: NSFetchRequest<Usuario> = Usuario.fetchRequest()
do {
let usuarios = try contextoDeFundo.fetch(requisicaoFetch)
completions(usuarios)
} catch {
print("Falha ao buscar usuários: \(error)")
completions(nil)
}
}
}
Neste código, criamos um novo contexto de fundo e realizamos a requisição de busca nesse contexto. Isso evita que a nossa thread principal da interface de usuário seja bloqueada.
Salvando Dados em uma Thread de Fundo
Agora vamos ver como salvar dados em um contexto de fundo. Este é um processo semelhante ao de busca de dados.
func salvarUsuario(nome: String) {
let contextoDeFundo = PilhaCoreData.compartilhada.containerPersistente.newBackgroundContext()
contextoDeFundo.perform {
let usuario = Usuario(context: contextoDeFundo)
usuario.nome = nome
do {
try contextoDeFundo.save()
} catch {
print("Falha ao salvar usuário: \(error)")
}
}
}
Aqui, criamos um novo objeto de usuário em segundo plano e o salvamos usando o contexto de fundo. Novamente, isso garante que a thread principal permaneça inalterada durante a operação de salvamento.
Mesclando Alterações no Contexto Principal
Ao usar um contexto de fundo, é essencial mesclar as alterações de volta ao contexto principal para manter a interface do usuário atualizada.
func buscarEDisplayUsuarios() {
buscarUsuarios { [weak self] usuarios in
DispatchQueue.main.async {
if let usuarios = usuarios {
self?.exibirUsuarios(usuarios)
}
}
}
}
Neste exemplo, buscamos os usuários em segundo plano e, em seguida, atualizamos a interface do usuário na thread principal. Isso garante que nosso aplicativo se comporte corretamente e ofereça uma experiência suave ao usuário.
Melhores Práticas para Multithreading com Core Data
-
Use Contextos de Fundo: Sempre utilize um contexto de fila privada para operações em segundo plano. Isso mantém a thread principal livre e responsiva.
-
Evite Acesso Direto Entre Threads: Nunca acesse objetos gerenciados ou contextos entre diferentes threads. Sempre realize operações dentro da fila do contexto.
-
Mescle Alterações: Utilize
NSNotificationCenter
para ouvir notificaçõesNSManagedObjectContextDidSave
e mesclar alterações quando necessário. -
Use o Método
perform
: Sempre utilizeperform
noNSManagedObjectContext
para garantir que suas operações sejam executadas na thread correta.
Conclusão
Nesta aula, discutimos a importância do multithreading com Core Data e como você pode gerenciar seus dados de uma forma que mantenha seu aplicativo responsivo. Ao usar contextos de fila privada e realizar operações de busca/salvamento em threads de fundo, você pode garantir uma experiência de usuário tranquila enquanto gerencia grandes conjuntos de dados. Lembre-se de mesclar as alterações adequadamente para manter sua interface atualizada. Boa codificação!