2013-10-23 1 views
8

Google 앱에서 UIImages 용 NSCache를 사용하고 있습니다. 이것은 7보다 작은 iOS 버전에서 정상적으로 작동합니다. 메모리 경고가 발생하면 NSCache는 의도 한대로 객체를 해제합니다. 그러나 iOS 7에서는 첫 번째 메모리 경고 직후에 앱이 다운됩니다. 따라서 NSCache로 저장된 개체는 절대로 출시되지 않지만 캐시가 응용 프로그램이 다운 될 때까지 커지는 것처럼 보입니다. 계측기로 프로파일 링하면 이러한 의심을 확인할 수 있습니다.메모리 제한에 도달하면 NSCache 충돌이 발생합니다 (iOS 7에서만 사용)

다른 사람이이 문제를 겪었습니까? 해결 방법을 찾았거나 이미 버그를 추적 했습니까?

http://www.photosmithapp.com/index.php/2013/10/photosmith-3-0-2-photo-caching-and-ios-7/ 내가 문제를 설명하기 위해 작은 샘플 응용 프로그램을 만들어 :

그것은 그 사람들이 동일한 문제가 있었다 것 같습니다. 버튼을 누르면 -(IBAction)fillCache:(id)sender 메서드가 호출됩니다. 그 이후로 타이머는 100 ms마다 -(void)addImageToCache:(id)sender을 호출합니다. 이 방법에서는 UIImage가 생성되어 캐시에 기록됩니다.

iOS 7.0.3 및 512MB 메모리가있는 iPad Mini에서는 ~ 350 회 반복 된 후 충돌이 발생합니다.

iOS 5 및 512MB 메모리가 장착 된 iPad 2에서도 특정 시점에서 충돌이 발생하지만 적어도 3000 회 반복해야합니다. 인 스트 루먼트는 메모리 경고가 발생할 때마다 UIImage 인스턴스 수가 감소 함을 보여줍니다. 이는 iOS 7의 경우에는 해당하지 않습니다.

- (IBAction)fillCache:(id)sender 
{ 
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(addImageToCache:) userInfo:nil repeats:YES]; 
} 

- (void)addImageToCache:(id)sender 
{ 
    @autoreleasepool { 

     CGRect rect = CGRectMake(0, 0, 500, 500); 
     UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); 
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
     UIGraphicsEndImageContext(); 

     NSString *poolKey = [NSString stringWithFormat:@"junk_%d", count++]; 
     [self.cache setObject:image forKey:poolKey]; 

    } 
} 

답변

15

NSCache은 메모리 경고에 절대로 응답하지 않았지만 일반적으로 실제 메모리 압력에 반응하는 것으로 나타났습니다. 메모리 경고에 대한 응답 실패는 항상 약간의 골칫거리였습니다 (예 : 메모리 압력에서 앱의 동작을 테스트하기 위해 "메모리 경고 시뮬레이트"를 사용할 수 없음).

그런 말로하면, 나는 당신이 묘사하는 것과 똑같은 행동을 보게됩니다. iOS 7이 NSCache 동작을 변경 한 것 같습니다.

개인적으로, 난 그냥 단지 UIApplicationDidReceiveMemoryWarningNotification 통지를 수신하면 모든 개체를 제거하는 간단한 생각을 가진 NSCache 서브 클래스가이 제정신 미세 보인다

@implementation AutoPurgeCache 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 
} 

@end 
+1

을 ... 나는 손으로 조금 무거운 경우 궁금해 , 대신 totalCostLimit 또는 countLimit을 줄일 수 있습니까? –

+1

@GradyPlayer 동의 함. 나는 당신의 아이디어가 마음에 들지만, 당신이 제시 한 한계를 바꾸면 앱을 안정된 상황으로 복구 할 수 있다고 확신 할 수 있을지 확신 할 수 없습니다. 합리적인 한계를 앞당겨 설정하고 메모리 경고가 처음부터 발생하는 것을 막을 수도 있습니다.그러나 그렇게하고 아직도 기억의 경고를 받으면이 무거운 손으로 접근하는 것은 격변 적 실패에 대비하는 보루가 될 수 있습니다. – Rob

+1

당신은 아마 맞을 것입니다. 스핀 2 개가 runloop 중 하나를 통과하는지 모르겠지만 ... 나중에 그 곳에서 다시 만들 수없는 것이 있으면 안됩니다. –

3

NSCache 개체는 자체 규칙에 따라 데이터를 제거합니다. 그렇다고해서 메모리 경고가 발생하는 동안 내용이 해제되지는 않습니다.
여기에 어떤 문서 상태 :

NSCache 클래스는 이 캐시는 시스템의 메모리를 너무 많이 사용하지 않도록 다양한 자동 퇴거 정책을 통합합니다. 다른 응용 프로그램에서 메모리가 필요한 경우이 정책은 캐시에서 일부 항목을 제거하여 메모리 사용 공간을 최소화합니다.

아마도 iOS7의 일부 정책이 변경되었을 수 있습니다. 메모리 경고 알림을 수신하여 모든 내용을 제거 할 수 있습니다. 이 answer을 완전하게하기 위해 링크합니다.

관련 문제