ios - Qual é a diferença entre os atributos atômicos e não atômicos?




objective-c properties (18)

O que significa nonatomic e nonatomic nas declarações de propriedade?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

Qual é a diferença operacional entre esses três?


atômico (padrão)

Atomic é o padrão: se você não digitar nada, sua propriedade é atômica. Uma propriedade atômica é garantida que, se você tentar ler a partir dela, receberá um valor válido. Ele não faz nenhuma garantia sobre qual valor pode ser, mas você receberá de volta bons dados, não apenas memória inútil. O que isso permite que você faça se você tiver vários threads ou vários processos apontando para uma única variável, um thread pode ler e outro thread pode escrever. Se eles acertarem ao mesmo tempo, o thread do leitor terá a garantia de obter um dos dois valores: antes da alteração ou após a alteração. O que a atomic não lhe dá é qualquer garantia sobre qual desses valores você pode obter. Atomic é comumente confundido com thread-safe, e isso não está correto. Você precisa garantir sua segurança de thread de outras maneiras. No entanto, atomic irá garantir que, se você tentar ler, você recebe algum tipo de valor.

não atômico

Por outro lado, não-atômico, como você provavelmente pode adivinhar, apenas significa “não faça essa coisa atômica”. O que você perde é a garantia de que você sempre recebe algo de novo. Se você tentar ler no meio de uma gravação, poderá recuperar dados ilegíveis. Mas, por outro lado, você vai um pouco mais rápido. Como as propriedades atômicas precisam fazer alguma mágica para garantir que você recuperará um valor, elas são um pouco mais lentas. Se é uma propriedade que você está acessando muito, você pode querer descer para nonatomic para se certificar de que você não está incorrendo nessa penalidade de velocidade.

Veja mais aqui: https://realm.io/news/tmi-objective-c-property-attributes/


Atômica:

A Atomic garante que o acesso à propriedade será realizado de maneira atômica. Por exemplo, ele sempre retorna um objeto totalmente inicializado, qualquer get / set de uma propriedade em um thread deve ser concluído antes que outro possa acessá-lo.

Se você imaginar a seguinte função ocorrendo em dois threads ao mesmo tempo, você pode ver porque os resultados não seriam bons.

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

Prós: O retorno de objetos totalmente inicializados sempre faz dele a melhor escolha no caso de multi-threading.

Contras: acerto de desempenho, torna a execução um pouco mais lenta

Não Atômico:

Ao contrário do Atomic, ele não garante o retorno de objeto totalmente inicializado a cada vez.

Prós: Execução extremamente rápida.

Contras: Chances de valor de lixo em caso de multi-threading.


A melhor maneira de entender a diferença é usar o seguinte exemplo.

Suponha que haja uma propriedade de cadeia atômica chamada "nome" e, se você chamar [self setName:@"A"] do encadeamento A, chame [self setName:@"B"] do encadeamento B e chame [self name] thread C, então todas as operações em threads diferentes serão executadas serialmente, o que significa que se um thread estiver executando um setter ou getter, os outros threads aguardarão.

Isso torna a propriedade "nome" de leitura / gravação segura, mas se outro encadeamento, D, chamar [name release] simultaneamente, essa operação poderá produzir uma falha, porque não há nenhuma chamada setter / getter envolvida aqui. O que significa que um objeto é seguro para leitura / gravação (ATOMIC), mas não é seguro para thread, pois outros threads podem enviar simultaneamente qualquer tipo de mensagem para o objeto. O desenvolvedor deve garantir segurança de thread para esses objetos.

Se a propriedade "nome" foi nonatomic, então todos os threads no exemplo acima - A, B, C e D serão executados simultaneamente produzindo qualquer resultado imprevisível. No caso de atômica, qualquer um dos A, B ou C será executado primeiro, mas D ainda pode executar em paralelo.


A sintaxe e a semântica já estão bem definidas por outras excelentes respostas a essa questão. Como a execução e o desempenho não são bem detalhados, adicionarei minha resposta.

Qual é a diferença funcional entre esses 3?

Eu sempre considerei atômica como um padrão bastante curioso. No nível de abstração em que trabalhamos, usar propriedades atômicas para uma classe como um veículo para alcançar 100% de segurança de roscas é um caso crucial. Para programas verdadeiramente multithreaded, a intervenção do programador é quase certamente um requisito. Enquanto isso, as características de desempenho e execução ainda não foram detalhadas em profundidade. Tendo escrito alguns programas altamente multithreaded ao longo dos anos, eu estava declarando minhas propriedades como nonatomic o tempo todo porque atomic não era sensato para qualquer finalidade. Durante a discussão dos detalhes das propriedades atômicas e nonatomic esta questão , eu fiz alguns perfis encontrou alguns resultados curiosos.

Execução

Está bem. A primeira coisa que gostaria de esclarecer é que a implementação de bloqueio é definida pela implementação e abstraída. Louis usa @synchronized(self) em seu exemplo - eu vi isso como uma fonte comum de confusão. A implementação não usa de fato @synchronized(self) ; Ele usa bloqueios de rotação no nível do objeto. A ilustração de Louis é boa para uma ilustração de alto nível usando construções com as quais todos estamos familiarizados, mas é importante saber que ela não usa @synchronized(self) .

Outra diferença é que as propriedades atômicas irão reter / liberar o ciclo de seus objetos dentro do getter.

atuação

Aqui está a parte interessante: o desempenho usando acessos de propriedades atômicas em casos não contestados (por exemplo, single-threaded) pode ser realmente muito rápido em alguns casos. Em casos menos que ideais, o uso de acessos atômicos pode custar mais de 20 vezes a sobrecarga de recursos nonatomic atômicos. Enquanto o caso Contested usando 7 threads foi 44 vezes mais lento para a estrutura de três bytes (2.2 GHz Core i7 Quad Core, x86_64). A estrutura de três bytes é um exemplo de uma propriedade muito lenta.

Nota interessante: Os acessadores definidos pelo usuário da estrutura de três bytes foram 52 vezes mais rápidos que os acessadores atômicos sintetizados; ou 84% da velocidade de acessores não atômicos sintetizados.

Objetos em casos contestados também podem exceder 50 vezes.

Devido ao número de otimizações e variações nas implementações, é muito difícil medir os impactos do mundo real nesses contextos. Muitas vezes você pode ouvir algo como "Confie, a menos que você faça um perfil e encontre um problema". Devido ao nível de abstração, é realmente muito difícil medir o impacto real. Recolher custos reais de perfis pode ser muito demorado e, devido a abstrações, bastante impreciso. Além disso, o ARC vs MRC pode fazer uma grande diferença.

Então, vamos voltar atrás, não nos concentrando na implementação de acessos de propriedade, incluiremos os suspeitos usuais como objc_msgSend e examinaremos alguns resultados de alto nível do mundo real para muitas chamadas para um getter NSString em casos incontestáveis (valores em segundos):

  • MRC | não atômico | getters implementados manualmente: 2
  • MRC | não atômico | getter sintetizado: 7
  • MRC | atômico | getter sintetizado: 47
  • ARC | não atômico | getter sintetizado: 38 (nota: ARC está adicionando ref count cycling aqui)
  • ARC | atômico | getter sintetizado: 47

Como você provavelmente adivinhou, a atividade de contagem de referência / ciclagem é um contribuinte significativo com atomics e sob ARC. Você também veria diferenças maiores em casos contestados.

Embora eu preste muita atenção ao desempenho, eu ainda digo Semantics First! . Enquanto isso, o desempenho é uma baixa prioridade para muitos projetos. No entanto, saber detalhes de execução e custos das tecnologias que você usa certamente não faz mal. Você deve usar a tecnologia certa para suas necessidades, objetivos e habilidades. Espero que isso economize algumas horas de comparações e ajude você a tomar uma decisão mais bem informada ao projetar seus programas.


Atomic significa que apenas um thread acessa a variável (tipo estático). Atomic é thread-safe, mas é lento.

Nonatomic significa que vários threads acessam a variável (tipo dinâmico). Nonatomic é thread-inseguro, mas é rápido.


Depois de ler tantos artigos, o publica e faz aplicações de demonstração para verificar atributos de propriedades de variáveis, decidi juntar todas as informações de atributos:

  1. atomic // padrão
  2. nonatomic
  3. strong = retain // padrão
  4. weak = unsafe_unretained
  5. retain
  6. assign // Default
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite // Default

No artigo Atributos de propriedade variável ou modificadores no iOS, você pode encontrar todos os atributos mencionados acima, e isso definitivamente ajudará você.

  1. atomic

    • atomic significa que apenas um thread acessa a variável (tipo estático).
    • atomic é segmento seguro.
    • Mas é lento no desempenho
    • atomic é o comportamento padrão
    • Atores atômicos em um ambiente não coletado pelo lixo (ou seja, ao usar reter / liberar / autorelease) usarão um bloqueio para garantir que outro encadeamento não interfira na configuração / obtenção correta do valor.
    • Não é realmente uma palavra-chave.

    Exemplo:

        @property (retain) NSString *name;
    
        @synthesize name;
  2. nonatomic

    • nonatomic significa acesso múltiplo thread a variável (tipo dinâmico).
    • nonatomic é thread-inseguro.
    • Mas é rápido no desempenho
    • nonatomic é comportamento padrão. Precisamos adicionar a palavra-chave nonatomic no atributo property.
    • Isso pode resultar em um comportamento inesperado, quando dois processos diferentes (threads) acessam a mesma variável ao mesmo tempo.

    Exemplo:

        @property (nonatomic, retain) NSString *name;
    
        @synthesize name;

Isso é explicado na documentation da Apple, mas abaixo estão alguns exemplos do que realmente está acontecendo. Note que não existe palavra-chave "atômica", se você não especificar "nonatomic" então a propriedade é atômica, mas especificar "atômica" explicitamente resultará em um erro.

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

Agora, a variante atômica é um pouco mais complicada:

//@property(retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

Basicamente, a versão atômica tem que ter um bloqueio para garantir a segurança do thread, e também está batendo a contagem de ref no objeto (e a contagem de liberação automática para equilibrá-lo) para que o objeto seja garantido para o chamador, caso contrário é uma condição de corrida em potencial se outro segmento estiver configurando o valor, fazendo com que a contagem de referências caia para 0.

Na verdade, há um grande número de variantes diferentes de como essas coisas funcionam, dependendo de se as propriedades são valores ou objetos escalares e como reter, copiar, somente leitura, não atômico etc. Em geral, os sintetizadores de propriedade apenas sabem como fazer a "coisa certa" para todas as combinações.


O padrão é atomic , isso significa que você custa desempenho sempre que você usa a propriedade, mas é thread-safe. O que o Objective-C faz é setar um lock, então apenas o thread atual pode acessar a variável, desde que o setter / getter seja executado.

Exemplo com MRC de uma propriedade com um ivar _internal:

[_internal lock]; //lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

Então estes dois últimos são os mesmos:

@property(atomic, retain) UITextField *userName;

@property(retain) UITextField *userName; // defaults to atomic

Por outro lado, o nonatomic não adiciona nada ao seu código. Por isso, é apenas thread seguro se você codificar o mecanismo de segurança mesmo.

@property(nonatomic, retain) UITextField *userName;

As palavras-chave não precisam ser escritas como primeiro atributo de propriedade.

Não se esqueça, isso não significa que a propriedade como um todo seja isenta de segmentos. Somente a chamada de método do setter / getter é. Mas se você usar um setter e depois um getter ao mesmo tempo com 2 threads diferentes, ele pode ser quebrado também!


Os dois últimos são idênticos; "atomic" é o comportamento padrão ( note que não é realmente uma palavra-chave; ela é especificada apenas pela ausência de nonatomic - atomic foi adicionado como uma palavra-chave em versões recentes do llvm / clang).

Supondo que você esteja @sintetizando as implementações do método, mudanças atômicas vs. não-atômicas alteram o código gerado. Se você está escrevendo seus próprios setter / getters, atômico / não atômico / reter / atribuir / copiar são meramente consultivos. (Nota: @synthesize é agora o comportamento padrão em versões recentes do LLVM. Também não há necessidade de declarar variáveis ​​de instância; elas também serão sintetizadas automaticamente e terão um _substituído ao nome para evitar o acesso direto acidental).

Com "atomic", o setter / getter sintetizado irá garantir que um valor inteiro seja sempre retornado do getter ou definido pelo setter, independentemente da atividade do setter em qualquer outro thread. Ou seja, se o thread A estiver no meio do getter enquanto o thread B chama o setter, um valor viável real - um objeto autoreleased, muito provavelmente - será retornado ao chamador em A.

Em nonatomic , nenhuma dessas garantias são feitas. Assim, nonatomic é consideravelmente mais rápido que "atômico".

O que "atômico" não faz é garantir a segurança dos fios. Se o thread A está chamando o getter simultaneamente com o thread B e C chamando o setter com valores diferentes, o thread A pode obter qualquer um dos três valores retornados - aquele antes de qualquer setter ser chamado ou qualquer um dos valores passados ​​para os setters em B e C. Da mesma forma, o objeto pode acabar com o valor de B ou C, não há maneira de dizer.

Garantir a integridade dos dados - um dos principais desafios da programação multi-threaded - é alcançado por outros meios.

Somando a isso:

atomicity de uma única propriedade também não garante a segurança do encadeamento quando várias propriedades dependentes estão em jogo.

Considerar:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

Nesse caso, o thread A pode renomear o objeto chamando setFirstName: e, em seguida, chamando setLastName: Enquanto isso, o encadeamento B pode chamar fullName entre as duas chamadas do encadeamento A e receberá o novo primeiro nome junto com o antigo sobrenome.

Para resolver isso, você precisa de um modelo transacional . Ou seja, algum outro tipo de sincronização e / ou exclusão que permita excluir o acesso ao fullName enquanto as propriedades dependentes estiverem sendo atualizadas.


Resposta mais fácil primeiro: não há diferença entre seus dois segundos exemplos. Por padrão, os acessadores de propriedade são atômicos.

Atores atômicos em um ambiente não coletado pelo lixo (ou seja, ao usar reter / liberar / autorelease) usarão um bloqueio para garantir que outro encadeamento não interfira na configuração / obtenção correta do valor.

Consulte a seção " Performance and Threading " da documentação do Objective-C 2.0 da Apple para obter mais informações e outras considerações ao criar aplicativos multi-threaded.



Propriedades atômicas : - Quando uma variável atribuída com propriedade atômica significa que ela tem apenas um acesso de encadeamento e será thread-safe e será boa na perspectiva de desempenho, terá um comportamento padrão.

Propriedades não atômicas : - Quando uma variável atribuída com propriedade atômica significa que ela tem acesso multi thread e não será thread-safe e será lenta na perspectiva de desempenho, terá comportamento padrão e quando dois threads diferentes quiserem acessar a variável ao mesmo tempo isso dará resultados inesperados.


Atomic: Garanta a segurança do thread travando o thread usando o NSLOCK.

Não atômico: não garante a segurança do encadeamento, pois não há mecanismo de bloqueio de encadeamento.


Para simplificar toda a confusão vamos entender mutex lock.Mutex bloqueio de acordo com o nome bloqueia a mutabilidade do objeto.Então, se o objeto é acessado por uma classe nenhuma outra classe pode acessar o mesmo objeto.No iOS @sychronise também fornecer o mutex lock.Now ele serve no modo FIFO e garante que o fluxo não é afetado por duas classes compartilhando a mesma instância.No entanto, se a tarefa estiver no thread principal, evite acessar o objeto usando propriedades atômicas, pois ela pode conter sua interface e degradar o desempenho


Antes de começar: Você deve saber que todos os objetos na memória precisam ser desalocados da memória para que uma nova gravação ocorra. Você não pode simplesmente escrever em cima de algo como faz no papel. Você deve primeiro apagar (dealloc) e então você pode escrever sobre ele. Se no momento em que o apagamento é feito (ou meio feito) e nada ainda foi escrito (ou metade escreveu) e você tentar lê-lo pode ser muito problemático! Atômica e não atômica ajudam a tratar esse problema de diferentes maneiras.

Primeiro leia this questão e leia a resposta de Bbum . Além disso, leia meu resumo.

atomic vai sempre garantir

  • Se duas pessoas diferentes quiserem ler e escrever ao mesmo tempo, seu papel não vai apenas queimar! -> Seu aplicativo nunca irá travar, mesmo em uma condição de corrida.
  • Se uma pessoa está tentando escrever e só escreveu 4 das 8 letras para escrita, então não pode ler no meio, a leitura só pode ser feita quando todas as 8 letras estiverem escritas -> Nenhuma leitura (get) acontecerá em 'um encadeamento que ainda está escrevendo', ou seja, se houver 8 bytes para bytes a serem gravados e apenas 4 bytes forem gravados — até esse momento, você não poderá ler a partir dele. Mas desde que eu disse que não vai falhar, então ele iria ler o valor de um objeto autoreleased .
  • Se antes de escrita que você tenha apagado o que foi escrito anteriormente em papel e então alguém quer ler você pode ainda ler. Como? Você lerá algo semelhante ao Mac OS Lixeira (como lixeira não é 100% apagada ... está em um limbo) ---> Se ThreadA é para ler enquanto ThreadB já desalocou para escrever, você poderia ou obter valor do valor final totalmente escrito por ThreadB ou obter algo do pool de autorelease.

As contagens de retenção são a maneira pela qual a memória é gerenciada no Objective-C. Quando você cria um objeto, ele tem uma contagem de retenções de 1. Quando você envia um objeto a reter uma mensagem, sua contagem de retenções é incrementada em 1. Quando você envia uma mensagem de lançamento a um objeto, sua contagem de retenções é diminuída em 1. Quando enviar um objeto uma mensagem de liberação automática , sua contagem de retenção é diminuída em 1 em algum momento no futuro. Se a contagem de retenções de um objeto for reduzida para 0, ela será desalocada.

  • Atomic não garante a segurança do thread, embora seja útil para alcançar a segurança do thread. A Segurança de Encadeamentos é relativa a como você escreve seu código / a qual fila de encadeamentos você está lendo / escrevendo. Ele garante apenas multithreading não-crashable.

Espere o que?! O multithreading e a segurança de thread são diferentes?

Sim.Multithreading significa: vários threads podem ler uma parte compartilhada de dados ao mesmo tempo e nós não travaremos, mas isso não garante que você não esteja lendo de um valor não liberado. Com segurança de thread, é garantido que o que você lê não é liberado automaticamente. A razão pela qual nós não fazemos tudo atômico por padrão é, porque existe um custo de desempenho e para a maioria das coisas realmente não precisamos de segurança de thread. Algumas partes do nosso código precisam dele e, para essas poucas partes, precisamos escrever nosso código de maneira segura, usando travas, mutex ou sincronização.

nonatomic

  • Como não existe o Mac OS Trash Bin, ninguém se importa se você sempre obtém um valor (<- Isso poderia levar a uma falha), nem ninguém se importa se alguém tenta ler na metade de sua escrita (embora meio que escrever na memória é muito diferente da metade escrita no papel, na memória ela pode dar a você um valor estúpido louco de antes, enquanto no papel você só vê metade do que foi escrito) -> Não garante que você não bata, porque não usa mecanismo de autorelease.
  • Não garante valores escritos completos para serem lidos!
  • É mais rápido que o atômico

No geral, eles são diferentes em dois aspectos:

  • Bater ou não por ter ou não ter piscina autorelease.

  • Permitir que seja lido no meio de uma 'gravação ainda não finalizada ou valor vazio' ou que não permita e permita somente a leitura quando o valor estiver totalmente escrito.


Atomic significa que apenas um thread pode acessar a variável por vez (tipo estático). Atomic é thread-safe, mas é lento.

Nonatomic significa que vários encadeamentos podem acessar a variável ao mesmo tempo (tipo dinâmico). Nonatomic é thread-inseguro, mas é rápido.


Se você estiver usando sua propriedade no código multi-threaded, então você poderá ver a diferença entre atributos atômicos e não atômicos. Nonatomic é mais rápido que atômico e atômico é thread-safe, não nonatomic.

Vijayendra Tripathi já deu um exemplo para um ambiente multi-threaded.


  • -Atomic significa que apenas um thread acessa a variável (tipo estático).
  • -Atomic é segmento seguro.
  • -mas é lento no desempenho

Como declarar:

Como atomic é padrão, então

@property (retain) NSString *name;

E no arquivo de implementação

self.name = @"sourov";

Suponha que uma tarefa relacionada a três propriedades seja

 @property (retain) NSString *name;
 @property (retain) NSString *A;
 @property (retain) NSString *B;
 self.name = @"sourov";

Todas as propriedades funcionam paralelamente (como de forma assíncrona).

Se você chamar "nome" do tópico A ,

E

Ao mesmo tempo, se você ligar

[self setName:@"Datta"]

do fio B ,

Agora, se a propriedade * name for nonatomic, então

  • Ele retornará o valor "Datta" para A
  • Ele retornará o valor "Datta" para B

É por isso que non atomic é chamado thread inseguro Mas, mas é rápido no desempenho por causa da execução paralela

Agora, se a propriedade * name for atômica

  • Isso irá garantir o valor "Sourov" para A
  • Então ele retornará o valor "Datta" para B

É por isso que atomic é chamado thread Safe e é por isso que é chamado de leitura-gravação seguro

Essa operação de situação será executada em série. E lento no desempenho

- Nonatomic significa múltiplo acesso thread a variável (tipo dinâmico).

- Nonatomic é thread inseguro.

- mas é rápido no desempenho

-Nonatomic NÃO é um comportamento padrão, precisamos adicionar uma palavra-chave nonatomic no atributo de propriedade.

For In Swift Confirmando que as propriedades Swift são nonatomic no sentido ObjC. Uma razão é para você pensar se a atomicidade por propriedade é suficiente para suas necessidades.

Referência: https://forums.developer.apple.com/thread/25642

Para mais informações, visite o site http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html







nonatomic