É possível evitar que um NSURLRequest armazene dados em cache ou remova os dados em cache após uma solicitação?

89

No iPhone, eu executo uma solicitação HTTP usando NSURLRequest para um bloco de dados. A alocação de objetos aumenta e eu atribuo os dados de acordo. Quando eu termino com os dados, eu os libero de acordo - no entanto, os instrumentos não mostram que nenhum dado foi liberado!

Minha teoria é que, por padrão, as solicitações de HTTP são armazenadas em cache - não quero que meu aplicativo para iPhone armazene esses dados em cache.

Existe uma maneira de limpar esse cache após uma solicitação ou evitar que qualquer dado seja armazenado em cache em primeiro lugar?

Tentei usar todas as políticas de cache documentadas um pouco como a seguir:

NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
theRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

mas nada parece liberar a memória!

Nick Cartwright
fonte
Seria possível não estar relacionado ao cache? Você já tentou inspecionar os dados para ver se os dados que deveriam ter sido recarregados são de fato os antigos? Talvez o seu alho-poró esteja vindo de outro lugar. Como você inicializa e libera os NSURLRequests? Isso pode ajudar a diagnosticar o problema.
lpfavreau
Para sua informação - se você deseja remover os arquivos por força bruta, há um exemplo de código para fazer isso aqui: salesforce.stackexchange.com/a/69736
zekel

Respostas:

157

Normalmente é mais fácil criar a solicitação como esta

NSURLRequest *request = [NSURLRequest requestWithURL:url
      cachePolicy:NSURLRequestReloadIgnoringCacheData
      timeoutInterval:60.0];

Em seguida, crie a conexão

NSURLConnection *conn = [NSURLConnection connectionWithRequest:request
       delegate:self];

e implemente o método connection: willCacheResponse: no delegado. Apenas retornar nulo deve bastar.

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
  return nil;
}
tcurdt
fonte
Muito obrigado aqui por sua ajuda! Usuario.
Nick Cartwright
1
Obrigado por essa ideia - eu estava chamando um serviço da web SOAP como este repetidamente e o heap estava aumentando de maneira incontrolável, embora os vazamentos não mostrassem que havia algo errado. Otimizei por dias e finalmente tentei evitar o armazenamento em cache, pois muitos objetos CFURL * da estrutura interna estavam pendurados. Retornar nil de willCacheResponse foi a única coisa que funcionou!
Bron Davies
15
Por que é necessário fazer ambos NSURLRequestReloadIgnoringCacheData e implementar connection:willCacheResponse:?
fabb 01 de
1
Olá, posso usar isso para carregar conteúdo local? O acima é para NSUrlConnection, mas estou carregando dados HTML locais em UIWebView usando NSUrlRequest. Preciso rejeitar qualquer armazenamento em cache, pois há imagens indo para o webview do SQLite e a memória está aumentando a cada carregamento de página. Obrigado.
Jim
7
@fabb, a substituição connection:willCacheResponse:permite que você não armazene a resposta no cache. NSURLRequestReloadIgnoringCacheDataespecifica que a conexão deve carregar a solicitação sem verificar o cache. O primeiro é provavelmente o que ajuda a gerenciar a alocação de memória.
Christopher Pickslay de
12

Eu tenho o mesmo problema no meu aplicativo quando solicitei informações do twitter. No meu caso, não precisei preservar essas credenciais, então simplesmente as apago usando o próximo código:

- (void) eraseCredentials{
NSURLCredentialStorage *credentialsStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *allCredentials = [credentialsStorage allCredentials];

//iterate through all credentials to find the twitter host
for (NSURLProtectionSpace *protectionSpace in allCredentials)
    if ([[protectionSpace host] isEqualToString:@"twitter.com"]){
        //to get the twitter's credentials
        NSDictionary *credentials = [credentialsStorage credentialsForProtectionSpace:protectionSpace];
        //iterate through twitter's credentials, and erase them all
        for (NSString *credentialKey in credentials)
            [credentialsStorage removeCredential:[credentials objectForKey:credentialKey] forProtectionSpace:protectionSpace];
    }
}

Espero que funcione para alguém :)


fonte
esta foi uma solução perfeita para o meu problema, tive problema de relogin, pois as credenciais estavam armazenadas e estavam sendo enviadas automaticamente pelo NSURLConnection, muito obrigado, isso me ajudou muito :)
RVN
Obrigado, é um trabalho para mim, agradeço muito a sua sugestão. Na verdade, meu problema é que NSURLRequest armazena nome de usuário e senha. Então, isso ajuda a remover a credencial do usuário do cache ...
Nilesh Kikani,
10

Se você usar NSURLConnection, dê uma olhada no delegado:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse

Valor de retorno

A resposta real em cache para armazenar no cache. O delegado pode retornar cachedResponse inalterado, retornar uma resposta em cache modificada ou retornar nulo se nenhuma resposta em cache deve ser armazenada para a conexão.

Catlan
fonte
Muito obrigado aqui. A pegada de memória do meu aplicativo caiu pela metade! Usuario.
Nick Cartwright
9

Se você estiver usando NSURLSession, outra solução para evitar que solicitações e parâmetros sejam gravados no Cache.dbiOS cria dentro do Cachesdiretório do aplicativo , é definir o NSURLCachepara a configuração da sessão para memória de tamanho 0 e cache de disco de tamanho 0, por exemplo

let configuration = URLSessionConfiguration.default    
configuration.urlCache = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let session = URLSession(configuration: configuration)

ou conforme mencionado acima, definido em um nível de cache global

URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)

Presumivelmente, é 0 para o tamanho do disco que impede a gravação do iOS no disco, mas se você tiver uma política para reloadIgnoringLocalCacheDataisso, provavelmente também não está interessado no cache de memória.

Observação Isso impedirá que qualquer Caches/Cache.db(solicitações e respostas) ou Caches/fsCachedData/pasta (dados de resposta) seja criada. Decidimos adotar essa abordagem em um aplicativo para fins de segurança, pois não queremos que nossas solicitações sejam armazenadas no cache de disco nunca.

Se alguém souber que existe uma maneira de interromper apenas o cache de solicitação, mas manter o cache de dados de resposta do mecanismo de carregamento de URL do iOS, estou interessado em saber. (não há API ou documentação oficial sobre isso pelo que posso dizer)

Shagun Madhikarmi
fonte
7

Se não for específico para uma única solicitação (você deseja desativar o cache para todo o aplicativo), abaixo de um é a melhor opção. Adicione este código no delegado do aplicativo ou com base em sua necessidade em qualquer lugar

        int cacheSizeMemory = 0*4*1024*1024; // 0MB
        int cacheSizeDisk = 0*32*1024*1024; // 0MB
        NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
        [NSURLCache setSharedURLCache:sharedCache];
Sanjeev Rao
fonte
7
Funciona muito bem para mim, mas você pode apenas usar 0 em vez de fazer a multiplicação que acaba em 0 de qualquer maneira.
Gary Riches
1
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] url];
[request setValue:@"no-store" forHTTPHeaderField:@"Cache-Control"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

Assumindo que o servidor está implementado corretamente, colocar o Cache-Control:no-storecabeçalho na solicitação irá gerar uma resposta do servidor com o mesmo cabeçalho, fazendo NSURLCachecom que os dados da resposta não sejam armazenados no disco.

Portanto, não há necessidade de uma abordagem rápida para desativar NSURLCacheo cache de disco.

PS: Adicionar o cabeçalho deve funcionar para todas as estruturas HTTP, como AFNetworking

Yuri Brigance
fonte