Writing Core Data code with performance in mind helps to prepare your app for the future. Seu banco de dados pode ser pequeno no início, mas pode facilmente crescer, resultando em consultas lentas e experiência diminuída para o usuário.
desde que comecei a escrever o aplicativo Collect by WeTransfer em 2017, tenho escrito um monte de código relacionado com dados de base, tocando-o quase todos os dias. Com milhões de usuários adicionando lotes de conteúdo, a realização de código de dados de base relacionados tornou-se uma habilidade importante em nossa equipe.
ao longo dos anos, desenvolvemos muitos insights, que estou feliz em compartilhar com você através de 5 dicas que você deve saber.
- 1: Faça uso de um contexto de objeto gerenciado de fundo
- Important: Don’t pass NSManagedObject instances between quues
- 2: salvar Apenas um objeto gerenciado o contexto, se necessário
- considere cuidadosamente quando salvar as suas alterações
- 3: Apenas obtenha o que necessita
- 4: Faça uso de limites de busca
- 5: Delete muitos objetos de uma vez usando um NSBatchDeleteRequest
- 6: saber como depurar o código de dados de base
- Conclusion
1: Faça uso de um contexto de objeto gerenciado de fundo
uma coisa que não fizemos desde o início é fazer uso de um contexto de objeto gerenciado de fundo. Nós só usamos o contexto da view para executar quaisquer tarefas relacionadas com os dados de base: inserção de novo conteúdo, exclusão de conteúdo, obtenção de conteúdo, etc.No início, a nossa aplicação era relativamente pequena. Fazer uso apenas do contexto de visualização não foi realmente um problema e não resultou em quaisquer penalidades de desempenho visíveis relacionadas com os dados de base. Obviamente, uma vez que nosso app começou a crescer, percebemos que o contexto da vista estava associado com a fila principal. Consultas lentas bloquearam a nossa IU e a nossa aplicação ficou menos a responder.
em geral, a melhor prática é realizar o processamento de dados em uma fila de fundo, uma vez que pode ser intensivo de CPU. Exemplos como a importação de JSON para os dados do núcleo poderiam bloquear o contexto da vista e resultar em falta de resposta na interface do Usuário.
a solução é fazer uso de um contexto de objeto de gestão de fundo. As últimas APIs facilitam a criação de um novo contexto a partir do seu contentor persistente:
let backgroundContext = persistentContainer.newBackgroundContext()
I recommend this method over the NSManagedObjectContext(concurrenyType:)
initializer as it will automatically be associated with the NSPersistentStoreCoordinator
and it will be set to consuse NSManagedObjectContextDidSave
broadcasts too. Isto mantém o seu contexto de fundo em sincronia com o contexto de vista.
pode gravar este contexto numa subclasse personalizada de contentores persistentes. Desta forma, você pode reutilizar seu contexto de fundo e você só tem que gerenciar dois contextos. Isto mantém a sua estrutura de Dados Central simples de entender e evita ter múltiplos contextos fora de sincronia.
se você só tiver que usar o contexto de fundo em alguns lugares, você também pode decidir usar o método performBackgroundTask(_:)
que cria um contexto de fundo no lugar:
persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}
no entanto, este método cria um novo NSManagedObjectContext
cada vez que é invocado. Você pode querer considerar usar o contexto de fundo compartilhado se você está enviando mais frequentemente para um contexto de fundo.
Important: Don’t pass NSManagedObject instances between quues
Writing multi-threaded Core Data code is a lot more complex than using a single view context. A razão para isso é que você não pode simplesmente passar um NSManagedObject
instanciado de um contexto de vista para um contexto de fundo. Fazer isso resultaria em um crash e corrupção de dados potencial.
Quando é necessário mover um objeto gerenciado a partir de uma fila para outra, você pode fazer uso do NSManagedObjectID
que é thread-safe:
let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}
2: salvar Apenas um objeto gerenciado o contexto, se necessário
Economia de um objeto gerenciado contexto compromete-se todas as alterações atuais para o contexto principal da loja. Como você pode imaginar, esta não é uma operação barata e só deve ser usada se necessário para garantir o desempenho em dados de base.Em primeiro lugar, é importante verificar se há alguma coisa para salvar. Se não houver alterações para commit, também não há razão para realizar uma gravação. Ao criar um método saveIfNeeded
, você permite-se facilmente incorporar uma verificação para isto:
considere cuidadosamente quando salvar as suas alterações
para além de usar saveIfNeeded
em vez de save()
também precisa de considerar se uma gravação faz sentido. Embora um contexto possa ter alterações, nem sempre é necessário enviar estas alterações directamente.
por exemplo, se você é importante vários itens na sua base de dados, você só pode querer gravar depois de ter importado todos os itens no seu contexto de fundo. Uma gravação é muitas vezes seguida por atualizações UI e várias gravuras depois uma da outra pode facilmente resultar em cargas desnecessárias. Além disso, levar em conta que as mudanças gravadas em um contexto de fundo são fundidas no contexto de visualização, bloqueando a fila principal em breve também. Portanto, esteja consciente!
3: Apenas obtenha o que necessita
a obtenção de dados é uma tarefa dispendiosa e precisa de ser o mais eficiente possível para preparar o seu aplicativo para grandes conjuntos de dados. O seguinte código é um erro muitas vezes cometido:
este código irá carregar todos os objetos inseridos na memória enquanto ele está sendo filtrado diretamente depois de apenas permanecer com conteúdo com um nome.
é muito mais eficiente usar predicados para apenas buscar os objetos que são necessários. O filtro acima pode ser escrito como se segue com um NSPredicate
:
isto tem duas vantagens:
- Apenas os objetos necessários são carregados na memória
- Você não precisa para iterar sobre todos os objetos
Predicados são muito flexíveis, e deve permitir-lhe buscar o conjunto de dados desejado na maioria dos casos, mantendo o desempenho no Core Data.
4: Faça uso de limites de busca
seguindo o exemplo anterior é importante definir limites de busca quando você só vai mostrar uma parte do conjunto de dados. Por exemplo, diga que você só precisa dos 3 primeiros nomes de todos os itens de conteúdo. Neste caso, seria desnecessário carregar todos os itens de conteúdo com um nome na memória. Poderíamos evitar isso, definindo um limite de busca:
este código só irá devolver os primeiros 3 itens de conteúdo com um nome.
5: Delete muitos objetos de uma vez usando um NSBatchDeleteRequest
em vez de iterar sobre um conjunto de dados apagando cada objeto um por um é muitas vezes mais performante usar um NSBatchDeleteRequest
que funciona mais rápido como ele opera no nível SQL na loja persistente em si.
pode aprender mais sobre os pedidos de remoção de lotes no meu post do blog, usando o NSBatchDeleteRequest para apagar lotes em dados centrais.
6: saber como depurar o código de dados de base
como em todo o código que escreve, é importante saber como optimizá-lo e depurá-lo, uma vez que não esteja a funcionar como esperado. Há muitas maneiras de depuração que são melhor explicadas em meu blog dedicado post: Depuração de dados Core no Xcode usando argumentos de lançamento.
Conclusion
Writing performant Core Data code from the beginning helps you to prepare your app for future large datasets. Embora o seu aplicativo pode estar se apresentando no início, ele pode facilmente abrandar uma vez que o seu banco de dados e modelo cresce. Ao fazer uso de um contexto de fundo, pedidos de busca inteligente, e pedidos de eliminação de lote você está fazendo o seu código de dados de base já mais Executante.
se você gosta de melhorar o seu conhecimento rápido, ainda mais, confira a página da categoria Swift. Sinta-se livre para entrar em contato comigo ou tweet para mim no Twitter se você tiver alguma dica adicional ou feedback.Obrigado!