Pergunta

Eu queria ter algo esclarecido.

Digamos que eu tenha o seguinte código:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

Isso criará 5 milhões de strings autorizadas nessa chamada de função. Eu esperava que isso mantenha esses objetos até o término do aplicativo, pois o único @AutoreLeasePool que vejo é o que está envolvendo a instanciação do aplicativo em Main.M. Não é esse o caso, no entanto. No final desta chamada de função, parece que todos eles recebem seu lançamento chamados e são removidos da memória.

Esse documento:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsautoreleasepool_class/reference/reference.html

Afirma que "o kit de aplicativos cria um pool de autorlease no segmento principal no início de todos os ciclos do loop do evento e o drena no final, liberando assim quaisquer objetos autorizados gerados ao processar um evento".

Isso faz sentido para mim, mas isso está no Uikit, e não no kit de aplicação. Minha pergunta é: uikit/cacau toque faz a mesma coisa neste caso ou há outra explicação para meus objetos serem lançados?

Obrigado!

Foi útil?

Solução 2

Sim, Uikit faz a mesma coisa. O pool de rosca principal criada pelo sistema é drenada no final de cada ciclo de loop de execução. Provavelmente é melhor não confiar nessa vida exata em seu próprio código. Se você criar um novo thread manualmente (usando por exemplo, nsthread), será responsável por criar o pool de autorlease nesse tópico.

EDIT: A resposta de Rob fornece boas informações adicionais sobre o comportamento sob arco. Em geral, é justo dizer que os objetos têm menos probabilidade de acabar no pool de autorlease devido a algumas otimizações que o arco é capaz de fazer.

Outras dicas

Andrew respondeu à sua principal pergunta que, sim, seu pool de autorlease será drenado em todos os ciclos do loop de corrida principal. Portanto, quaisquer objetos autorlease criados em viewDidLoad Pode ser drenado imediatamente quando você renderá de volta ao loop de corrida principal. Eles certamente não serão mantidos "até o término do aplicativo".

Mas devemos ter cuidado: você está claramente assumindo que esses objetos estão sendo adicionados a um pool de autorlease. Algumas advertências para esta suposição:

  1. No passado (e ainda necessário para a interoperabilidade ARC-MRC), ao devolver objetos de métodos cujos nomes não começaram alloc, new, copy, ou mutableCopy, esses objetos queriam os objetos, desalocados apenas quando o pool de autorlease foi drenado (ou seja, quando você se rendeu ao loop de corrida).

  2. Mas o ARC ficou mais inteligente em minimizar a necessidade de pools de autorlease (ver http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease, que discute callerAcceptsFastAutorelease, agora chamado callerAcceptsOptimizedReturn invocado por prepareOptimizedReturn), então você geralmente não verá isso autorelease comportamento. Portanto, se a biblioteca e o chamador estiverem usando o ARC, os objetos não poderão ser colocados no pool de autorlease, mas o ARC os liberará inteligentemente imediatamente, se não forem necessários.

    Com projetos de arco contemporâneo, os pools de autorlease geralmente não são necessários. Mas certos casos especiais, ainda é possível se beneficiar do uso de pools automáticos. Vou delinear um desses casos abaixo.

Considere o seguinte código:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

O código a seguir adicionará 500.000 objetos ao pool de autorlease, que só serão drenados quando eu ceder ao loop de corrida:

no pool

Nesse caso, você pode usar um pool de autorlease para minimizar a marca d'água alta:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

pool

Conclusão, com arco, nem sempre é óbvio quando usou um objeto de autorlease e quando o libera explicitamente quando a variável cai do escopo. Você sempre pode confirmar isso examinando o comportamento em instrumentos.

Como um aparte, eu ficaria cauteloso em tirar muitas conclusões gerais de gerenciamento de memória ao usar o NSString A classe, como foi altamente otimizada e nem sempre está em conformidade com as práticas de gerenciamento de memória padrão.

Eu assumiria que quando você atribui um novo objeto a uma referência que costumava segurar um objeto então O objeto original é liberado imediatamente (Se nada mais apontar para isso - a contagem de ref é para zero) usando arco e default asuming strong referência como no seu exemplo de loop.

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

Apple observa Transição para notas de liberação do arco

Tente parar de pensar em onde as chamadas de retenção/liberação são colocadas e pense nos algoritmos de seu aplicativo. Pense em indicadores "fortes e fracos" em seus objetos, sobre a propriedade dos objetos e sobre possíveis ciclos de retenção.

Parece que o release é chamado em objeto quando é atribuído um novo valor, de clang Clang 3.4 Documentação Objetiva-C Contagem de referência automática (ARC)

A atribuição ocorre ao avaliar um operador de atribuição. A semântica varia de acordo com a qualificação:

Para __ objetos, o novo Pointee é retido pela primeira vez; Segundo, o LValue é carregado com semântica primitiva; Terceiro, o novo Pointee é armazenado no LValue com semântica primitiva; E, finalmente, o velho Pointee é lançado. Isso não é realizado atomicamente; A sincronização externa deve ser usada para tornar isso seguro diante de cargas e lojas simultâneas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top