0

iOS ARC 앱 (요청시 더 많은 코드 제공)을 준비 중이며 이전에는 다수의 이미지를 제작하고 폐기했습니다. 나는 removeFromSuperview를 호출하고 더 이상 사용되지 않는 이미지에 대한 모든 참조를 제거하려고 했더라도 어딘가에 여전히 이미지에 대한 참조가 있다고 생각했습니다. 나는 Leaks를 시도했고 Leaks는 대략 17M을 시작으로 시간이 지남에 따라 메모리 사용량이 대략 선형 적으로 증가했다고보고했습니다.메모리 소비가 올라 갔지만 누수가 누출을 감지하지 못하면 iOS에서 누수를 처리하는 방법은 무엇입니까?

이미지에 대한 모든 참조가 인스턴스 변수로 바뀌었기 때문에 한정된 크기의 고정 된 메모리를 사용하고 시계 바늘에 사용되는 이미지를 제거하는 대신 변형되었습니다. 불행히도 시간이 지남에 따라 메모리 사용이 서서히 증가하는 결과를 낳았습니다. 17M이 아닌 5M에서 시작했지만, 그렇지 않은 경우 동일한 문제가 더 나은 출발점으로 전환되었습니다.

내 코드의 트림 된 버전은 다음과 같습니다. 이것에 관한 누설 (또는 "누수가 누설을 나타내지 않았으므로"의사 누설 ")이 무엇인지, 그리고 어떻게 시작될 때 코드가 사용하는 메모리 경계에 가까이 머무를 수 있는지 말해 줄 수 있습니까? I는 두 가지 방법으로, 초기 표시를위한 하나의 증분 업데이트 하나에 하나의 방법 리팩토링 한

- (void) renderScreen 
{ 
int height = floor([[UIScreen mainScreen] bounds].size.height + .4); 
int width = floor([[UIScreen mainScreen] bounds].size.width + .4); 

if (height == 2048 || height == 2008 || height == 1024 || height == 1004 || height == 984) 
{ 
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; 
    } 
    else 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Landscape.png"]; 
    } 
} 
else if (height == 1536 || height == 768 || height == 748 || height == 728) 
{ 
    _backgroundImage = [UIImage imageNamed:@"Background-Default-Landscape.png"]; 
} 
else if (height == 1136 || height == 1116 || height == 1096) 
{ 
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-568.png"]; 
    } 
    else 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated-568.png"]; 
    } 
} 
else if (height == 960 || height == 940 || height == 920 || height == 480 || height == 460 || height == 440) 
{ 
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default.png"]; 
    } 
    else 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated.png"]; 
    } 
} 
else if ((height == 640 || height == 620 || height == 600) && (width == 1136 || width == 1116 || width == 1096)) 
{ 
    _backgroundImage = [UIImage imageNamed:@"Background-Rotated-568.png"]; 
} 
else if ((height == 640 || height == 620 || height == 600 || height == 320 || height == 300 || height == 280) && (width == 960 || width == 940 || width == 920 || width == 480 || width == 470 || width == 410)) 
{ 
    if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; 
    } 
    else 
    { 
     _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated.png"]; 
    } 
} 
else 
{ 
    _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; 

} 
if (!UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
{ 
    int juggle = height; 
    height = width; 
    width = juggle; 
} 
NSUInteger centerX = width * .5; 
NSUInteger centerY = height * .5; 

_containerRect = CGRectZero; 
_containerRect.size = [[UIScreen mainScreen] bounds].size; 

self.view.backgroundColor = [UIColor colorWithPatternImage:_backgroundImage]; 
_backgroundView = [[UIImageView alloc] initWithImage:_backgroundImage]; 
[self.view addSubview:_backgroundView]; 
if (_changed) 
{ 
    _containerView = [[UIView alloc] initWithFrame:_containerRect]; 
} 

double timeStampSeconds = [[NSDate date] timeIntervalSince1970]; 
double hours = fmod(timeStampSeconds/86400, 24); 
double minutes = fmod(timeStampSeconds/3600, 60); 
double seconds = fmod(timeStampSeconds,  60); 
NSLog(@"Milliseconds: %lf, Hours: %.0f, minutes: %.0f, seconds: %.0f", timeStampSeconds * 1000.0, hours, minutes, seconds); 
[_containerView removeFromSuperview]; 
_containerView = [[UIView alloc] initWithFrame:_containerRect]; 
_hourHandImage = [UIImage imageNamed:@"hour-hand.png"]; 
_hourHandView = [[UIImageView alloc] initWithImage:_hourHandImage]; 
_hourHandImage = [UIImage imageNamed:@"hour-hand.png"]; 
_hourHandView = [[UIImageView alloc] initWithImage:_hourHandImage]; 
[self.view addSubview:_hourHandView]; 

_hourHandView.layer.anchorPoint = CGPointMake(0.5f, 0.5f); 
_hourTransform = CGAffineTransformMakeTranslation(centerX, centerY); 
_hourTransform = CGAffineTransformTranslate(_hourTransform, -17, -127); 
_hourTransform = CGAffineTransformRotate(_hourTransform, hours/12.0 * M_PI * 2.0); 
_minuteTransform = CGAffineTransformMakeTranslation(centerX, centerY); 
_minuteTransform = CGAffineTransformTranslate(_minuteTransform, -10, -182); 
_minuteTransform = CGAffineTransformRotate(_minuteTransform, minutes/60.0 * M_PI * 2.0); 
_hourHandView.transform = _hourTransform; 
_minuteHandImage = [UIImage imageNamed:@"minute-hand.png"]; 
_minuteHandView = [[UIImageView alloc] initWithImage:_minuteHandImage]; 
_minuteHandView.transform = _minuteTransform; 
[self.view addSubview:_minuteHandView]; 
_minuteTransform = CGAffineTransformRotate(_minuteTransform, minutes/60.0 * M_PI * 2.0); 
_secondHandImage = [UIImage imageNamed:@"second-hand.png"]; 
_secondTransform = CGAffineTransformMakeTranslation(centerX, centerY); 
_secondTransform = CGAffineTransformTranslate(_secondTransform, -10, -189); 
_secondTransform = CGAffineTransformRotate(_secondTransform, seconds/60.0 * M_PI * 2.0); 
_secondHandView = [[UIImageView alloc] initWithImage:_secondHandImage]; 
_secondHandView.transform = _secondTransform; 
[self.view addSubview:_secondHandView]; 
} 

--EDIT--

감사합니다. 증분 업데이트 메서드는 시계가 고정되어 서명 로깅 문이 정확히 한 번 호출되므로 한 번만 호출됩니다.

는 지금 업데이트 부분에 대해 가지고 :

- (void)render 
{ 
    [self renderScreenInitial]; 
    [NSTimer scheduledTimerWithTimeInterval:0.002 
            target:self 
            selector:@selector(renderingTimer:) 
            userInfo:nil 
            repeats:YES]; 
} 

- (void) renderScreenIncremental 
{ 
    int height = floor([[UIScreen mainScreen] bounds].size.height + .4); 
    int width = floor([[UIScreen mainScreen] bounds].size.width + .4); 
    if (!UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) 
    { 
     int juggle = height; 
     height = width; 
     width = juggle; 
    } 
    double decibelAngle = M_PI/4 + (_decibel/60) * M_PI/2; 
    double decibelAngleDifference = decibelAngle - _previousDecibelAngle; 
    _previousDecibelAngle = decibelAngle; 
    NSLog(@"%lf %lf", _decibel, decibelAngle); 
    _decibelNeedleView = [[UIImageView alloc] initWithImage:_decibelNeedle]; 
    // CGAffineTransform decibelTransform = CGAffineTransformMakeTranslation(centerX, centerY); 
    CGAffineTransform decibelTransform = CGAffineTransformMakeRotation(decibelAngleDifference); 
    decibelTransform = CGAffineTransformTranslate(decibelTransform, sin(decibelAngle - .06) * -298, -cos(decibelAngle - .06) * 298); 
    _decibelNeedleView.transform = decibelTransform; 
    [self.view addSubview:_decibelNeedleView]; 
    double timestampSeconds = [[NSDate date] timeIntervalSince1970]; 
    double timestampSecondsDifference = timestampSeconds - _previousTimestampSeconds; 
    _previousTimestampSeconds = timestampSeconds; 
    double hoursDifference = fmod(timestampSecondsDifference/86400, 24); 
    double minutesDifference = fmod(timestampSecondsDifference/3600, 60); 
    double secondsDifference = fmod(timestampSecondsDifference,  60); 
    NSLog(@"Milliseconds: %lf, Hours: %.0f, minutes: %.0f, seconds: %.0f", timestampSecondsDifference * 1000.0, hoursDifference, minutesDifference, secondsDifference); 
    _hourHandView.transform = CGAffineTransformMakeRotation(hoursDifference); 
    _minuteHandView.transform = CGAffineTransformMakeRotation(minutesDifference); 
    _secondHandView.transform = CGAffineTransformMakeRotation(secondsDifference); 
} 

-(void)renderingTimer:(NSTimer *)timer { 
    [self renderScreenIncremental]; 
} 

내가 도움을 주셔서 감사합니다. 디스플레이를 한 번 업데이트 한 다음 계속 업데이트하지 않는 이유를 알고 있습니까?

감사합니다.

+0

'renderingTimer :'의 호출 여부를 확인할 수 있습니까? 그리고 메인 큐에서'render'를 호출했다고 가정합니다. BTW, 0.002 초마다 호출 할 필요는 없습니다. 그 타이머를 합리적인 수준으로 줄일 것입니다 (초당 30-60 번). – Rob

답변

0

사용중인 API를 사용하여 메모리가 점진적으로 증가하는 것은 예상되는 동작입니다. [UIImage imageNamed:]will cache the image. 캐시가 플러시 될 때 캐시가 해제되는지 여부를 테스트하려면 메모리 경고가 발생하도록하십시오. 시뮬레이터에서 Hardware 메뉴로 가서 Simulate Memory Warning을 선택하면됩니다.

또한 다음 코드를 사용하여 프로그래밍 방식에-장치에이 작업을 수행 할 수 있습니다

[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification 
                object:[UIApplication sharedApplication]]; 

는이 코드

+0

1. 예, 'imageNamed' 캐시를 사용하면 앱이 점점 더 다양한 이미지를 사용하므로 메모리가 증가하게됩니다. 그러나 앱이 동일한 이미지에 반복적으로 액세스하는 경우 메모리 사용량이 이렇게 늘어나는 것은 아닙니다. 그래서 이것은 드물게 사용되는 이미지를 위해'imageNamed'를 사용하는 것에 대해 부주의 한 많은 애플 리케이션을 괴롭히는 우수한 관찰입니다, 그러나 나는 그것이 문제라고 생각하지 않습니다. – Rob

2

첫 번째와 생산 APP를 발송해서는 안 무엇보다도, 당신은 할당 도구를 사용한다 인스 트루먼 테이션을 통해 할당 된 내용과 릴리즈되지 않은 내용을 파악할 수 있습니다. 나는 WWDC 2012 비디오 iOS App Performance: Memory을 제안합니다. 다른 것들 중에서도 이것을 보여줍니다.

악기의 할당 도구를 사용하면 할당되는 내용과 해제되지 않은 내용을 추측 할 수는 없지만 힙샷/세대를 사용하여 할당되는 실제 개체를 식별하고 사용자의 특정 시간 범위 내에서 해제되지 않는 개체를 식별 할 수 있습니다. 선발. 문제가있는 객체가 원래 할당 된 위치를 드릴 업하고 볼 수 있습니다.

코드를 훑어 보면, 컨테이너를 제거하고 다시 추가하는 것처럼 보이지만 컨테이너에는 아무 것도 나타나지 않습니다. 그리고시/분/초침을 추가하고 있지만 절대로 제거하지 않습니다.아마 그걸 컨테이너에 넣으려고 한거야?

또한, 코드는 _changed이 무엇을 설명하지만 그때 당신은 효과적으로 (그것이 수퍼에 추가되지 않았다하더라도)의 수퍼에서 제거하는 _containerView을 만들 YES, 등을인지 폐기하지 않습니다 그것을 만들고 다른 _containerView을 생성하십시오. 아주 이상한.

나는이 코드에서 renderScreen을 여러 번 호출한다고 추측합니다. 그것이 사실이라면 뷰의 "변경"(뷰 변경 (배경 추가, 다양한 이미지 추가 등)의 "생성"(transform 값 조정)을 제안 할 수 있습니다. 가능한 경우보기를 한 번 추가 한 다음 업데이트가있을 때 최소한 변환 (변환 변경)을 수행해야합니다.

+0

감사합니다. iOS 앱 성능 : 메모리를보고 싶습니다. 불행히도 Safari는 처음 3 분만 티저를 재생하기 전에 재생하며 iTunes에서로드하지 않습니다.) - : _containerView는 이전 효과의 잔재입니다. 그러나 내가 실천할 수있는 것은 주로 점진적으로 반복되는 조정에서 원래의 설정을 분리하는 것입니다. On this that ... – JonathanHayward

+0

유료 iOS 개발자 계정을 갖고 있다면 추가 비용없이 모든 WWDC 비디오를 사용할 수 있습니다. (https://developer.apple.com/wwdc/videos/). iOS 개발자 계정의 비용은 99 달러입니다. – zaph