iOS Entrevista Prep 1 — Gerenciamento de memória

Apr 28 2023
O gerenciamento de memória é uma das perguntas mais frequentes durante a entrevista do iOS, é uma grande bandeira vermelha se você não conseguir demonstrar uma boa compreensão dos conceitos básicos, como ciclo de referência, referência forte/fraca. Para se preparar para todos os tipos de questões relacionadas ao gerenciamento de memória, é fundamental entender o mecanismo fundamental por trás da cortina do ARC.

O gerenciamento de memória é uma das perguntas mais frequentes durante a entrevista do iOS, é uma grande bandeira vermelha se você não conseguir demonstrar uma boa compreensão dos conceitos básicos, como ciclo de referência, referência forte/fraca. Para se preparar para todos os tipos de questões relacionadas ao gerenciamento de memória, é fundamental entender o mecanismo fundamental por trás da cortina do ARC.

Questões de entrevista

  • Explique como o ARC funciona no Obj-C/Swift
  • Quais são as situações comuns que podem levar ao vazamento de memória?
  • Quais são alguns cenários comuns em que o ciclo de referência pode acontecer?
  • Quando você deve usar a propriedade copy? Como isso ajuda a evitar bugs?
  • Você pode explicar a diferença entre referência sem dono (inseguro) e fraca?

O gerenciamento de memória no aplicativo iOS é baseado em um modelo de contagem de referência. Quando você inicializa um novo objeto, a memória é alocada no heap e a contagem de referência é definida como 1. A contagem de referência aumenta à medida que mais objetos definem uma referência forte a ela.

Pelo contrário, se o objeto proprietário renunciar à referência forte, a contagem de referência diminuirá em 1. Assim que a contagem de referência se tornar zero, a memória será destruída como resultado.

Ao compilar o código com o recurso ARC, o compilador pega as referências que você cria e insere chamadas automaticamente para o mecanismo de gerenciamento de memória subjacente .

Forte, fraco e sem dono

Com a introdução da Contagem Automática de Referências (ARC), só precisamos especificar o tipo de propriedade ao referenciar um objeto

  • Referências fortes — garante que o objeto referenciado permaneça na memória enquanto a referência for válida.
  • Referências fracas — que não têm efeito no tempo de vida de um objeto referenciado.
  • Referências sem dono — um sem dono não mantém um controle forte como uma referência fraca
  • Quando o objeto referenciado é desalocado, a referência fraca será definida como nula, enquanto a referência não proprietária se tornará um ponteiro pendente, enviar uma mensagem para ele resultará em uma falha
  • Sem proprietário é usado quando a outra instância tem o mesmo tempo de vida ou mais
  • Espera-se que a referência não proprietária sempre tenha um valor, portanto, o ARC nunca o define como nulo
  • use strong para reter objetos — embora a palavra-chave reter seja sinônimo, é melhor usar strong em seu lugar
  • use fraco se você quiser apenas um ponteiro para o objeto sem retê-lo - útil para evitar ciclos de retenção (ou seja, delegados) - ele irá automaticamente anular o ponteiro quando o objeto for liberado
  • use assign para primatives - exatamente como fraco, exceto que não anula o objeto quando liberado (definido por padrão)

Se você tiver um bom entendimento de contagem de referência por trás do ARC, será muito fácil entender a ideia do ciclo de referência. Se dois objetos estiverem conectados por um círculo de referência forte, eles se manterão vivos mesmo que não haja outra referência forte.

Ciclos de Referência em Fechamentos/Bloqueio

Uma das situações comuns em que um ciclo de referência forte pode ser introduzido é ao usar fechamento/bloqueio. Um ciclo de referência forte pode ocorrer se você atribuir um fechamento/bloco a uma propriedade de uma instância de classe e o corpo desse fechamento/bloco capturar a instância (auto). Observe que, se você simplesmente criar um novo bloco sem atribuí-lo a uma propriedade, isso não causará nenhum ciclo de referência.

- (void)configureBlock {
  // capture the weak reference to avoid the reference cycle
    XYZBlockKeeper * __weak weakSelf = self;

    self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;
    if (strongSelf != nil) return;

        [strongSelf doSomething];   
    }
}

lazy var someClosure = { [weak self] in
    // closure body goes here
  guard let strongSelf = self else { return }
}

A captura forte selfem um asyncfechamento GCD não causará um ciclo de referência, mas estenderá o tempo de vida de self.Por exemplo, se você fizer uma solicitação de rede de um controlador de exibição que foi descartado nesse meio tempo, o fechamento ainda será chamado. Se você capturar o controlador de exibição fracamente, ele será nil.No entanto, se você capturá-lo fortemente, o controlador de exibição permanecerá ativo até que o fechamento termine seu trabalho

Copiar propriedades em Objective-C

Na prática, usamos a propriedade copy para classes que possuem uma versão mutável, como NSString, NSArray. Para que nossa propriedade mantenha sua própria cópia, portanto não será impactada se a variável mutável original for atualizada.

@interface XYZBadgeView : NSView

@property NSString *firstName;
@property NSString *lastName;

@end

// If you need to set a copy property's instance variable directly
- (id)initWithSomeOriginalString:(NSString *)aString {
    self = [super init];
    if (self) {
        _instanceVariableForCopyProperty = [aString copy];
    }
    return self;
}

- (void)example {
  NSMutableString *nameString = [NSMutableString stringWithString:@"John"];
  self.badgeView.firstName = nameString;
  
// If we don't create a copy then firstName will also be affected
  // Because it points to the same object as nameString
  [nameString appendString:@"ny"];
}