Threading - Swift SDK
Nesta página
- Três regras a seguir
- Executar uma escrita em background
- Aguarde a conclusão das escritas assíncronas
- Confirme ou cancele uma escrita assíncrona
- Comunicação entre threads
- Crie uma fila em série para usar o Realm em uma thread em background
- Passe instâncias entre threads
- Use o mesmo Realm em todos os threads
- Atualização de Realms
- Objetos congelados
- Modelo de threading do Realm em detalhes
- Comparação e contraste com Git
- Estrutura interna
- Resumo
- Tipos Enviáveis, Não Enviáveis e Confinados a Threads
Para tornar seus aplicativos iOS e tvOS rápidos e responsivos, você deve equilibrar o tempo de computação necessário para organizar os visuais e lidar com interações do usuário com o tempo necessário para processar seus dados e executar sua lógica de negócios. Tipicamente, desenvolvedores de aplicativos distribuem esse trabalho em vários threads: o principal ou thread de UI para todo o trabalho relacionado à interface do usuário, e um ou mais threads de background para calcular volumes de trabalho mais pesados antes de enviá-los ao thread de UI para apresentação. Ao transferir trabalhos pesados para threads de background, o thread de UI pode permanecer altamente responsivo, independentemente do tamanho do volume de trabalho. Mas pode ser notoriamente difícil escrever um código multithread seguro, performático e sustentável que evite problemas como deadlocks e condições de corrida. O Realm visa simplificar isso para você.
Dica
Veja também:
A partir de 10.26.0, O Realm fornece métodos de gravação assíncrona para realizar gravações em background. Consulte: Executar uma gravação em background. Com a gravação assíncrona, você não precisa passar uma referência segura para threads ou objetos congelados entre os threads.
Esta página descreve como gerenciar manualmente os arquivos e objetos de Realm em todos os segmentos. O Realm também oferece suporte ao uso de um ator Swift para gerenciar o acesso ao Realm usando os recursos de simultaneidade do Swift . Para uma visão geral do suporte de atores do Realm, consulte Use Realm with Actors - Swift SDK.
Três regras a seguir
Antes de explorar as ferramentas do Realm para aplicações de várias threads, entenda e siga estas três regras:
- Não trave a leitura:
- A arquitetura de controle de concorrência multiversão (MVCC) da Realm elimina a necessidade de bloqueio para operações de leitura. Os valores que você lê nunca serão corrompidos ou em um estado parcialmente modificado. Você pode ler livremente o mesmo arquivo Realm em qualquer thread sem a necessidade de bloqueios ou mutexes. O bloqueio desnecessário seria um gargalo de desempenho, pois cada thread poderia precisar esperar sua vez antes de ler.
- Evite escritas síncronas na thread da UI se escrever em uma thread em background:
- Você pode escrever em um arquivo Realm de qualquer thread, mas só pode haver um escritor por vez. Consequentemente, transações de escrita síncronas bloqueiam umas às outras. Uma escrita síncrona na thread da UI pode fazer com que seu aplicativo pareça não responsivo enquanto espera que uma escrita em uma thread de segundo plano seja concluída. O Device Sync escreve em uma thread de background, então você deve evitar escritas síncronas na thread da UI com realms sincronizados.
- Não passe objetos ativos, coleções ou realms para outras threads:
- Objetos ativos, collections e instâncias de Realm são confinados à thread: ou seja, são válidos somente na thread em que foram criados. De forma prática, isso significa que não é possível passar instâncias ativas para outras threads. No entanto, o Realm oferece vários mecanismos para compartilhar objetos entre threads.
Executar uma escrita em background
Novidades na versão 10,26,0.
Você pode adicionar, modificar ou excluir objetos em segundo plano usando writeAsync.
Com writeAsync
, você não precisa passar uma referência thread-safe ou objetos congelados entre threads. Em vez disso, chame realm.writeAsync
. Você pode oferecer um bloco de conclusão para execução do método na thread de origem após a conclusão ou falha da escrita.
Itens a considerar nas escritas em background:
As escritas assíncronas bloqueiam o fechamento ou a invalidação do realm
Você pode confirmar ou cancelar transações explicitamente
let realm = try! Realm() // Query for a specific person object on the main thread let people = realm.objects(Person.self) let thisPerson = people.where { $0.name == "Dachary" }.first // Perform an async write to add dogs to that person's dog list. // No need to pass a thread-safe reference or frozen object. realm.writeAsync { thisPerson?.dogs.append(objectsIn: [ Dog(value: ["name": "Ben", "age": 13]), Dog(value: ["name": "Lita", "age": 9]), Dog(value: ["name": "Maui", "age": 1]) ]) } onComplete: { _ in // Confirm the three dogs were successfully added to the person's dogs list XCTAssertEqual(thisPerson!.dogs.count, 3) // Query for one of the dogs we added and see that it is present let dogs = realm.objects(Dog.self) let benDogs = dogs.where { $0.name == "Ben" } XCTAssertEqual(benDogs.count, 1) }
Aguarde a conclusão das escritas assíncronas
O SDK fornece um Bool
para sinalizar se o Realm está executando uma gravação assíncrona no momento. A variável isPerformingAsynchronousWriteOperations se torna true
após uma chamada para um dos seguintes:
writeAsync
beginAsyncWrite
commitAsyncWrite
Permanece verdadeiro até que todas as operações de gravação assíncronas programadas sejam concluídas. Embora isso seja verdade, isso bloqueia o fechamento ou invalidação do domínio.
Confirme ou cancele uma escrita assíncrona
Para concluir uma escrita assíncrona, você ou o SDK devem chamar:
Quando você usa o método writeAsync
, o SDK cuida da confirmação ou do cancelamento da transação. Isso fornece a conveniência da gravação assíncrona sem a necessidade de manter manualmente o estado vinculado ao escopo do objeto. No entanto, enquanto estiver no bloco writeAsync
, você pode chamar explicitamente commitAsyncWrite
ou cancelAsyncWrite
. Se você retornar sem chamar um desses métodos, writeAsync
:
Realiza a gravação após executar as instruções no bloco de gravação.
Retorna um erro
Em ambos os casos, isto conclui a operação writeAsync
.
Para ter mais controle sobre quando confirmar ou cancelar a transação de gravação assíncrona, use o método beginAsyncWrite
. Ao usar este método, você deve confirmar explicitamente as transações. Retornar sem confirmar uma gravação assíncrona cancela a transação. beginAsyncWrite
retorna um ID que você pode passar para cancelAsyncWrite
.
commitAsyncWrite
confirma de forma assíncrona uma transação de gravação. Esta é a etapa que persiste os dados no domínio. commitAsyncWrite
pode levar um bloco onComplete
. Este bloco é executado no thread de origem quando o commit é concluído ou falha com um erro.
A chamada de commitAsyncWrite
retorna imediatamente. Isso permite que o chamador continue enquanto o SDK executa a E/S em um thread em segundo plano. Esse método retorna um ID que você pode passar para cancelAsyncWrite
. Isso cancela a invocação pendente do bloco de conclusão. Não cancela a confirmação em si.
Você pode agrupar chamadas sequenciais para commitAsyncWrite
. O agrupamento dessas chamadas em lote melhora o desempenho de gravação; especialmente quando as transações em lote são pequenas. Para permitir o agrupamento de transações, configure o parâmetro isGroupingAllowed
como true
.
Você pode ligar para cancelAsyncWrite
em beginAsyncWrite
ou commitAsyncWrite
. Quando você chama beginAsyncWrite
, isso cancela toda a transação de escrita. Quando você o chama commitAsyncWrite
, isso cancela apenas um bloco de onComplete
que você pode ter passado para commitAsyncWrite
. Não cancela a confirmação em si. Você precisa do ID do beginAsyncWrite
ou do commitAsyncWrite
que deseja cancelar.
Comunicação entre threads
Para acessar o mesmo arquivo Realm de diferentes threads, você deve instanciar uma instância de realm em cada thread que precisa de acesso. Desde que você especifique a mesma configuração, todas as instâncias de realm mapearão para o mesmo arquivo no disco.
Uma das principais regras para trabalhar com o Realm em um ambiente de várias threads é que os objetos são confinados pela thread: não é possível acessar as instâncias de um domínio, coleção ou objeto originado em outras threads. A arquitetura de Controle de concorrência multiversão (MVCC) do Realm significa que pode haver muitas versões ativas de um objeto a qualquer momento. O confinamento de threads garante que todas as instâncias nesse thread tenham a mesma versão interna.
Quando há necessidade de comunicação entre threads, você tem várias opções, dependendo do seu caso de uso:
Para modificar um objeto em dois threads, consulte o objeto em ambos os threads.
Para reagir às alterações feitas em qualquer thread, use as notificaçõesdo Realm's.
Para ver as mudanças que aconteceram em outro thread na instância de domínio do thread atual, atualize sua instância de domínio.
Para enviar uma visualização rápida e somente leitura do objeto para outros threads, "freeze" o objeto.
Para manter e compartilhar muitas visualizações somente para leitura do objeto em sua aplicação, copie o objeto de realm.
Para compartilhar uma instância de um domínio ou objeto específico com outro thread ou entre os limites do ator, compartilhe uma referência thread-safe para a instância do domínio ou objeto. Para obter mais informações, consulte Passar uma ThreadSafeReference.
Crie uma fila em série para usar o Realm em uma thread em background
Ao usar o Realm em uma conversa de fundo, crie uma fila de série. O Realm não é compatível com o uso de realms em filas simultâneas, como a fila global()
.
// Initialize a serial queue, and // perform realm operations on it let serialQueue = DispatchQueue(label: "serial-queue") serialQueue.async { let realm = try! Realm(configuration: .defaultConfiguration, queue: serialQueue) // Do something with Realm on the non-main thread }
Passe instâncias entre threads
As instâncias de Realm
, Results
, List
e Objects
gerenciadas são confinadas por thread. Isso significa que você só pode usá-las no tópico onde as criou. No entanto, o Realm fornece um mecanismo chamado referências thread-safe que permite copiar uma instância criada em um thread para outro thread.
Conformidade com Sendable
Novidade na versão 10,20,0: @ThreadSafe wrapper e ThreadSafeReference estão em conformidade com Sendable
Se estiver a utilizar 5.6 o Swift ou superior, tanto o wrapper de propriedade @ThreadSafe como o ThreadSafeReference estão em conformidade com o Sendable.
Use o encapsulador @ThreadSafe
Novidades na versão 10.17.0.
Você pode passar instâncias confinadas por thread para outra thread da seguinte maneira:
Use o wrapper de propriedade
@ThreadSafe
para declarar uma variável que faça referência ao objeto original. Por definição, variáveis encapsuladas@ThreadSafe
são sempre opcionais.Passe a variável encapsulada
@ThreadSafe
para a outra thread.Use a variável
@ThreadSafe
-wrapped como faria com qualquer outra opção. Se o objeto referenciado for removido do realm, a variável de referência se tornará nil.
let realm = try! Realm() let person = Person(name: "Jane") try! realm.write { realm.add(person) } // Create thread-safe reference to person var personRef = person // @ThreadSafe vars are always optional. If the referenced object is deleted, // the @ThreadSafe var will be nullified. print("Person's name: \(personRef?.name ?? "unknown")") // Pass the reference to a background thread DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { let realm = try! Realm() try! realm.write { // Resolve within the transaction to ensure you get the // latest changes from other threads. If the person // object was deleted, personRef will be nil. guard let person = personRef else { return // person was deleted } person.name = "Jane Doe" } }
Outra maneira de trabalhar com um objeto em outro thread é fazer uma query por ele novamente nesse thread. Mas se o objeto não tiver uma chave primária, não é trivial consultá-lo. Você pode usar o wrapper @ThreadSafe
em qualquer objeto, independentemente de ele ter ou não uma chave primária.
Exemplo
O exemplo seguinte mostra como utilizar o @ThreadSafe
em um parâmetro de função. Isso é útil para funções que podem ser executadas de forma assíncrona ou em outra thread.
Dica
Se a sua aplicação acessar Realm em um contexto do async/await
, marque o código com @MainActor
para evitar falhas relacionadas a threading.
func someLongCallToGetNewName() async -> String { return "Janet" } func loadNameInBackground( person: Person?) async { let newName = await someLongCallToGetNewName() let realm = try! await Realm() try! realm.write { person?.name = newName } } func createAndUpdatePerson() async { let realm = try! await Realm() let person = Person(name: "Jane") try! realm.write { realm.add(person) } await loadNameInBackground(person: person) } await createAndUpdatePerson()
Use ThreadSafeReference (Swift antigo/Objective-C)
Antes do Realm Swift SDK versão 10.17.0 ou no Objective-C, é possível passar instâncias confinadas por threads para outro thread da seguinte maneira:
Inicializa um ThreadSafeReference com o objeto confinado à thread.
Passe a referência para a outro thread ou fila.
Resolva a referência no domínio do outro thread chamando Realm.resolve(_:). Use o objeto retornado normalmente.
Importante
Você deve resolver um ThreadSafeReference
exatamente uma vez. Caso contrário, o domínio de origem permanece fixado até que a referência seja desalocada. Por essa razão, ThreadSafeReference
deve ser de curta duração.
let person = Person(name: "Jane") let realm = try! Realm() try! realm.write { realm.add(person) } // Create thread-safe reference to person let personRef = ThreadSafeReference(to: person) // Pass the reference to a background thread DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { let realm = try! Realm() try! realm.write { // Resolve within the transaction to ensure you get the latest changes from other threads guard let person = realm.resolve(personRef) else { return // person was deleted } person.name = "Jane Doe" } }
Outra maneira de trabalhar com um objeto em outro thread é fazer uma query por ele novamente nesse thread. Mas se o objeto não tiver uma chave primária, não é trivial consultá-lo. Você pode usar ThreadSafeReference
em qualquer objeto, independentemente de ele ter ou não uma chave primária. Você também pode usá-lo com listas e resultados.
A desvantagem é que ThreadSafeReference
requer algum código padrão. Lembre-se de encapsular tudo em um DispatchQueue
com um autoreleaseFrequency
adequadamente definido, para que os objetos não permaneçam no thread de background. Então, pode ser útil fazer uma extensão de conveniência para lidar com o código padrão da seguinte maneira:
extension Realm { func writeAsync<T: ThreadConfined>(_ passedObject: T, errorHandler: @escaping ((_ error: Swift.Error) -> Void) = { _ in return }, block: @escaping ((Realm, T?) -> Void)) { let objectReference = ThreadSafeReference(to: passedObject) let configuration = self.configuration DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { do { let realm = try Realm(configuration: configuration) try realm.write { // Resolve within the transaction to ensure you get the latest changes from other threads let object = realm.resolve(objectReference) block(realm, object) } } catch { errorHandler(error) } } } }
Esta extensão adiciona um método writeAsync()
à classe Realm. Esse método passa uma instância para um thread em background para você.
Exemplo
Suponha que você tenha feito um aplicativo de e-mail e queira excluir todos os e-mails lidos em background. Agora você pode fazer isso com duas linhas de código. Observe que o fechamento é executado no thread de background e recebe sua própria versão do domínio e do objeto passado:
let realm = try! Realm() let readEmails = realm.objects(Email.self).where { $0.read == true } realm.writeAsync(readEmails) { (realm, readEmails) in guard let readEmails = readEmails else { // Already deleted return } realm.delete(readEmails) }
Use o mesmo Realm em todos os threads
Não é possível compartilhar instâncias de realm entre threads.
Para usar o mesmo arquivo domínio em todos os threads, abra uma instância de domínio diferente em cada thread. Contanto que você use a mesma configuração, todas as instâncias do domínio serão mapeadas para o mesmo arquivo no disco.
Atualização de Realms
Quando você abre um realm, ele reflete a mais recente confirmação de gravação bem-sucedida e permanece nessa versão até ser atualizado. Isso significa que o realm não verá as mudanças que aconteceram em outro thread até a próxima atualização. Um realm no thread da UI -- mais precisamente, em qualquer thread de loop de eventos -- atualiza-se automaticamente no início do loop desse thread. No entanto, você deve atualizar manualmente as instâncias do realm que não existem em threads de loop ou que têm a atualização automática desativada.
if (![realm autorefresh]) { [realm refresh] }
if (!realm.autorefresh) { // Manually refresh realm.refresh() }
Objetos congelados
Objetos vivos, confinados ao thread, funcionam bem na maioria dos casos. No entanto, alguns aplicativos -- aqueles baseados em arquiteturas reativas, baseadas em fluxo de eventos, por exemplo -- precisam enviar cópias imutáveis para vários threads para processamento antes de, finalmente, terminarem no thread da UI. Fazer uma cópia profunda toda vez seria caro, e o Realm não permite que instâncias vivas sejam compartilhadas entre threads. Nesse caso, você pode congelar e descongelar objetos, collections e realms.
O congelamento cria uma visão imutável de um objeto, collection ou domínio específico. O objeto, collection ou realm congelado ainda existe no disco e não precisa ser copiado profundamente quando passado para outros segmentos. Você pode compartilhar livremente o objeto congelado entre threads sem se preocupar com problemas de thread. Quando você congela um realm, seus objetos filhos também ficam congelados.
Dica
Use ThreadSafeReference com atores de Swift
Atualmente, o Realm não permite o uso de thaw()
com Swift Actors. Para trabalhar com dados do Realm nos limites dos atores, utilize o ThreadSafeReference
em vez de objetos congelados. Para mais informações, consulte Pass a ThreadSafeReference.
Objetos congelados não estão ativos e não são atualizados automaticamente. Eles são efetivamente snapshots do estado do objeto no momento do congelamento. Descongelar um objeto retorna uma versão ativa do objeto congelado.
// Get an immutable copy of the realm that can be passed across threads RLMRealm *frozenRealm = [realm freeze]; RLMResults *dogs = [Dog allObjectsInRealm:realm]; // You can freeze collections RLMResults *frozenDogs = [dogs freeze]; // You can still read from frozen realms RLMResults *frozenDogs2 = [Dog allObjectsInRealm:frozenRealm]; Dog *dog = [dogs firstObject]; // You can freeze objects Dog *frozenDog = [dog freeze]; // To modify frozen objects, you can thaw them // You can thaw collections RLMResults *thawedDogs = [dogs thaw]; // You can thaw objects Dog *thawedDog = [dog thaw]; // You can thaw frozen realms RLMRealm *thawedRealm = [realm thaw];
let realm = try! Realm() // Get an immutable copy of the realm that can be passed across threads let frozenRealm = realm.freeze() assert(frozenRealm.isFrozen) let people = realm.objects(Person.self) // You can freeze collections let frozenPeople = people.freeze() assert(frozenPeople.isFrozen) // You can still read from frozen realms let frozenPeople2 = frozenRealm.objects(Person.self) assert(frozenPeople2.isFrozen) let person = people.first! assert(!person.realm!.isFrozen) // You can freeze objects let frozenPerson = person.freeze() assert(frozenPerson.isFrozen) // Frozen objects have a reference to a frozen realm assert(frozenPerson.realm!.isFrozen)
Quando estiver trabalhando com objetos congelados, uma tentativa de realizar qualquer uma das seguintes ações gera uma exceção:
Abrir uma transação de gravação em um realm congelado.
Modificar um objeto congelado.
Adicionar um ouvinte de alteração a um realm, collection ou objeto congelado.
Você pode utilizar isFrozen
para verificar se o objeto está congelado. Isso é sempre seguro para threads.
if ([realm isFrozen]) { // ... }
if (realm.isFrozen) { // ... }
Objetos congelados continuam válidos desde que o domínio ativo que os gerou permaneça aberto. Portanto evite fechar o domínio ativo até que todas as threads tenham terminado com os objetos congelados. Você pode fechar um domínio congelado antes que o domínio ativo seja fechado.
Importante
Sobre o armazenamento em cache de objetos congelados
Armazenar muitos objetos congelados pode ter um impacto negativo no tamanho do arquivo do domínio. "Muitos" depende do dispositivo-alvo específico e do tamanho dos seus objetos domínio. Se você precisar armazenar em cache um grande número de versões, considere copiar o que você precisa fora do domínio.
Modificar um objeto congelado
Para modificar um objeto congelado, você deve descongelá-lo. Alternativamente, você pode consultar por ele em um reino não congelado e, então, modificá-lo. Chamar thaw
em um objeto ativo, collection ou realm retorna a si mesmo.
Descongelar um objeto ou collection também descongela o realm a que ele faz referência.
// Read from a frozen realm let frozenPeople = frozenRealm.objects(Person.self) // The collection that we pull from the frozen realm is also frozen assert(frozenPeople.isFrozen) // Get an individual person from the collection let frozenPerson = frozenPeople.first! // To modify the person, you must first thaw it // You can also thaw collections and realms let thawedPerson = frozenPerson.thaw() // Check to make sure this person is valid. An object is // invalidated when it is deleted from its managing realm, // or when its managing realm has invalidate() called on it. assert(thawedPerson?.isInvalidated == false) // Thawing the person also thaws the frozen realm it references assert(thawedPerson!.realm!.isFrozen == false) // Let's make the code easier to follow by naming the thawed realm let thawedRealm = thawedPerson!.realm! // Now, you can modify the todo try! thawedRealm.write { thawedPerson!.name = "John Michael Kane" }
Anexar a uma coleção congelada
Quando você adiciona a uma collection congelada, você deve descongelar tanto a collection quanto o objeto que deseja adicionar. Neste exemplo, consultamos dois objetos em um Realm congelado:
Um objeto Person que tem uma propriedade List de objetos Dog
Um objeto Dog
Devemos descongelar ambos os objetos antes de podermos adicionar o Dog à collection Lista de Dogs no Person. Se descongelarmos apenas o objeto Person, mas não o Dog, o Realm lançará um erro.
A mesma regra se aplica ao passar objetos congelados entre threads. Um caso comum pode ser chamar uma função em um thread em background para fazer algum trabalho em vez de bloquear a interface do usuário.
// Get a copy of frozen objects. // Here, we're getting them from a frozen realm, // but you might also be passing them across threads. let frozenTimmy = frozenRealm.objects(Person.self).where { $0.name == "Timmy" }.first! let frozenLassie = frozenRealm.objects(Dog.self).where { $0.name == "Lassie" }.first! // Confirm the objects are frozen. assert(frozenTimmy.isFrozen == true) assert(frozenLassie.isFrozen == true) // Thaw the frozen objects. You must thaw both the object // you want to append and the collection you want to append it to. let thawedTimmy = frozenTimmy.thaw() let thawedLassie = frozenLassie.thaw() let realm = try! Realm() try! realm.write { thawedTimmy?.dogs.append(thawedLassie!) } XCTAssertEqual(thawedTimmy?.dogs.first?.name, "Lassie")
Modelo de threading do Realm em detalhes
O Realm fornece acesso seguro, rápido, sem bloqueios e simultâneo em todos os threads com seu MVCC (Multiversion Concurrency Control) arquitetura.
Comparação e contraste com Git
Se você está familiarizado com um sistema de controle de versão distribuído como o Git, talvez já tenha uma compreensão intuitiva do MVCC. Dois elementos fundamentais do Git são:
Confirmações, que são escritas atômicas.
Ramificações, que são versões diferentes do histórico de confirmações.
Da mesma forma, o Realm tem gravações confirmadas atomicamente na forma de transações. O Realm também tem muitas versões diferentes da história a qualquer momento, como ramificações.
Ao contrário do Git, que suporta ativamente a distribuição e a divergência por meio de forking, um realm só tem uma versão mais recente verdadeira em um determinado momento e sempre escreve para o head dessa versão mais recente. O Realm não pode escrever em uma versão anterior. Isso significa que seus dados convergem para uma versão mais recente da verdade.
Estrutura interna
Um domínio é implementado usando uma estrutura de dados B+tree.. O nó de alto nível representa uma versão do domínio; nós secundários são objetos nessa versão do domínio. O domínio tem um ponteiro para sua versão mais recente, assim como o Git tem um ponteiro para sua confirmação de CABEÇALHO.
O Realm usa uma técnica de cópia em gravação para garantir isolamento e durabilidade. Quando você faz alterações, o Realm copia a parte relevante da árvore para gravar. Em seguida, Realm confirma as alterações em duas fases:
O Realm escreve as alterações no disco e verifica se foi feito.
Em seguida, o Realm coloca o ponteiro da versão mais recente apontando para a versão recém-escrita.
Esse processo de confirmação em duas etapas garante que, mesmo que a gravação falhe parcialmente, a versão original não será corrompida de forma alguma, pois as alterações foram feitas em uma cópia da parte relevante da árvore. Da mesma forma, o ponteiro raiz do domínio apontará para a versão original até que a nova versão seja válida.
Exemplo
O diagrama a seguir ilustra o processo de confirmação:
O realm é estruturado como uma árvore. O realm tem um ponteiro para a sua última versão, V1.
Ao escrever, o Realm cria uma nova versão V2 com base na V1. Realm faz cópias de objetos para modificação (A 1 , C 1), enquanto links para objetos não modificados continuam a apontar para as versões originais (B, D).
Após validar o commit, o Realm atualiza o ponteiro para a nova versão mais recente, V2. O Realm então descarta nós antigos que não estão mais conectados à árvore.
Realm usa técnicas de cópia zero, como mapeamento de memória, para lidar com dados. Quando você lê um valor do realm, você está virtualmente olhando para o valor no disco real, não uma cópia dele. Esta é a base para objetos vivos. É também por isso que um ponteiro de head de realm pode ser definido para apontar para a nova versão depois que a gravação no disco tiver sido validada.
Resumo
O Realm habilita a programação simples e segura de várias threads seguindo três regras:
não travar para ler
evite gravações no thread da IU se você gravar em threads em segundo plano ou usar o Device Sync
não passar objetos ativos para outras threads.
Há uma maneira adequada de compartilhar objetos entre threads para cada caso de uso.
Para ver as alterações feitas em outras threads na sua instância de realm,atualize manualmente as instâncias de realm que não existem nas threads de "loop" ou cuja atualização automática esteja desativada.
Para aplicativos baseados em arquiteturas reativas e em fluxo de eventos, você pode congelar objetos, collections e realms para passar cópias superficiais de forma eficiente para diferentes threads para processamento.
A arquitetura de controle de concorrência multiversão (MVCC) do Realm é semelhante à do Git. Diferentemente do Git, o Realm possui apenas uma única versão mais recente para cada realm.
O Realm faz confirmações em duas etapas para garantir o isolamento e a durabilidade.
Tipos Enviáveis, Não Enviáveis e Confinados a Threads
A API pública do SDK do Realm Swift contém tipos que se enquadram em três categorias amplas:
Enviável
Não pode ser enviado e não está confinado a threads
Confinado por thread
Você pode compartilhar tipos que não são Sendable e que não são confinados por thread entre threads, mas deve sincronizá-los.
Os tipos confinados por thread, a menos que congelados, estão confinados a um contexto de isolamento. Você não pode passá-los entre esses contextos, mesmo com sincronização.
Enviável | Non-Sendable | Confinada à thread |
---|---|---|
AnyBSON | RLMAppConfiguration | AnyRealmCollection |
AsyncOpen | RLMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RLMFindOptions | Lista |
RLMAPIKeyAuth | RLMNetworkTransport | Map |
RLMApp | RLMRequest | MutableSet |
RLMAsyncOpenTask | RLMResponse | Projeção |
RLMChangeStream | RLMSyncConfiguration | RLMArray |
RLMCompensatingWriteInfo | RLMSyncTimeoutOptions | RLMChangeStream |
RLMCredentials | RLMDictionary | |
RLMDecimal128 | RLMDictionaryChange | |
RLMEmailPasswordAuth | RLMEmbeddedObject | |
RLMMaxKey | RLMLinkingObjects | |
RLM Minkey | Objeto RLM | |
Cliente RLMMongoClient | RLMPropertyChange | |
RLMMongoCollection | RLMRealm | |
RLMMongoDatabase | RLMResults | |
RLMObjectId | RLMSection | |
RLMObjectSchema | RLMMongoClient | |
RLMProgressNotification | RLMSectionedResultsChangeset | |
RLMProgressNotificationToken | RLMSet | |
RLMProperty | RLMSyncSubscription | |
RLMPropertyDescriptor | RLMSyncSubscriptionSet | |
RLMProviderClient | RealmOptional | |
RLMPushClient | RealmProperty | |
RLMSchema | ||
RLMSortDescriptor | ||
RLMSyncErrorActionToken | ||
RLMSyncManager | ||
RLMSyncSession | ||
RLMThreadSafeReference | ||
RLMUpdateResult | ||
RLMUser | ||
RLMUserAPIKey | ||
RLMUserIdentity | ||
RLMUserProfile | ||
ThreadSafe |