2016-11-04 2 views
0

우리 회사 팀은 기존 애플리케이션에서 작동합니다. 이 프로젝트는 비 ARC (자동 참조 계산)입니다. 코드를 따르는 객체를 해제 할 때 의심의 여지가 있습니다.비 ARC 프로젝트 : NSMutableArray, NSString 메모리 누수

코드 1 :이 코드를 실행할 때 크래시가없는 이유는 무엇입니까?

NSMutableArray *arraytest=[[NSMutableArray alloc]init]; 
for(int i=0;i<100;i++) 
{ 
    NSString *str=[NSString stringWithFormat:@"string:%d",i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@",arraytest); 
[arraytest release]; 
NSLog(@"arraytest after:%@",arraytest); 

유사 코드 : 가변 사본

코드 2 : 다음 코드는 마지막 줄에 충돌 변경 후.

NSMutableArray *arraytest=[[NSMutableArray alloc]init]; 
for(int i=0;i<100;i++) 
{ 
    NSString *str=[NSString stringWithFormat:@"string:%d",i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray=[arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@",copyarray); 
NSLog(@"arraytest after:%@",arraytest); 

왜이 줄에 메모리 누수가 있습니까?

enter image description here

왜이 라인에서 메모리 누수가? 메모리 누출없이 상기 코드를 실행하기위한 적절한 방법은

enter image description here

무엇인가? 우리 회사는 autorelease가 코드 위에 사용되면 안된다고 말합니다.

답변

0

귀하의 첫 번째 예제를 생각해 봅시다 :

마지막 NSLog 문이 때문에, 객체가 arraytest이되었을 것이다 가리키는 ( +1에서 0에 배열의 유지 수를 감소) [arraytest release]라고 한 후, 대단히 위험하다
NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 

// `arraytest` populated ... 

NSLog(@"arraytest before:%@", arraytest); 
[arraytest release]; 
NSLog(@"arraytest after:%@", arraytest); // DANGER: referencing dangling pointer!!! 

deallocated이고 arraytest은 할당 해제 된 메모리에 대해 dangling pointer이되었습니다. 포인터가 할당 해제 된 후에는 절대 포인터를 참조하면 안됩니다. 때로는 사용할 수있는 것처럼 보일 수도 있지만 안전하지 않으며 앱이 예기치 않게 중단 될 수 있습니다. (당신이 좀비를 사용한 경우,하지만, 안전하게 잘못이 허상 포인터를 사용하도록 시도의 당신을 경고 할 것입니다.)


을 고려하여 두 번째 예제 :이 예에서

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 

// `arraytest` populated ... 

NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray = [arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@", copyarray); 
NSLog(@"arraytest after:%@", arraytest); // DANGER: referencing dangling pointer!!! 

을, 당신이 여전히 당신이 그것을 풀어 준 후에 NSLogarraytest의 매우 위험한 것을 가지고 있고, 그 매달린 포인터의 사용은 쉽게 추락 할 수 있습니다. 그래서 그걸 없애고 싶습니다.

하지만 이제는 누수가 도입되었습니다. arraytest가 처음에 가리킨 대상을 해제 한 후에는 copyarray이 가리키는 대상을 해제하지 않았으므로 결과는 mutableCopy입니다. 따라서이 새로운 copyarray 인스턴스는 누출됩니다. 따라서 원래 arraytest을 만들 때 원래 할당 된 문자열은 모두 누출 된 copyarray에 의해 참조되며 누출됩니다.

이 루틴의 끝 부분에 [copyarray release]을 추가하면 누수되는 배열과 새는 문자열이 모두 해결됩니다.


이제 만 최종 악기 스크린 샷에 표시된 세 번째 예를 들어, 고려 : 마지막 예에서

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 
for (int i=0; i<100; i++) { 
    NSString *str = [NSString stringWithFormat:@"string:%d", i]; 
    [arraytest addObject:str]; 
    [str release];      // DANGER: released `str` whose ownership was never transferred to you!!! 
} 
NSLog(@"arraytest before:%@",arraytest); 
NSMutableArray *copyarray=[arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@",copyarray); 
NSLog(@"arraytest after:%@",arraytest); // DANGER: referencing dangling pointer!!! 

을, 우리는 당신이 단지에 추가 문자열을 overreleasing하여 문제를 합성하는 배열 따라서 자동 회수 객체를 만들었고 (자동 회수 풀이 비게되면 유효 보유 수는 0이됩니다) 배열에 추가 (유효 보유 수를 +1으로 늘림)하고 str을 릴리스했습니다 (보유 수를 +0으로 줄임). 수영장의 배수).

배열이 이제 자동 릴리즈 풀이 빠져 나올 때 릴리스 될 수있는 객체를 참조하기 때문에 응용 프로그램이 불안정한 상황에 처하게되어 매달려있는 포인터의 배열로 끝납니다. 더 나쁜 것은,이 어레이가 적절하게 해제 되었다면, 그 모든 문자열은 과도하게 삭제 될 것입니다.

물론 위의 두 번째 예에서 설명한 것처럼 배열이 계속 누출됩니다. 그러나 만약 당신이 올바르게 copyarray을 출시했다면, 그 모든 문자열은 과량 방출 될 것입니다.


이 시점에서 말할 아마 불필요하지만,이 누출을 제거하는 방법으로 간단하게 해제하는 것입니다 copyarray :

NSMutableArray *arraytest = [[NSMutableArray alloc] init]; 
for (int i=0; i<100; i++) { 
    NSString *str = [NSString stringWithFormat:@"string:%d", i]; 
    [arraytest addObject:str]; 
} 
NSLog(@"arraytest before:%@", arraytest); 
NSMutableArray *copyarray = [arraytest mutableCopy]; 
[arraytest release]; 
NSLog(@"copyarray:%@", copyarray); 
[copyarray release]; 

이것은 당신이에 release를 호출 할 책임이 있습니다 즉 것을의 Basic Memory Management Rules 다음 alloc, new, copy 또는 mutableCopy으로 시작하는 메소드에서 수신 한 오브젝트.


닫는 관찰의 커플 : 수동 참조 카운팅을 사용하려고하는 경우

  1. , 나는 당신이 엑스 코드의 정적 분석기의 빈번한 사용을 만드는 것이 좋을 것 (변화 + 명령 + B 또는 Xcode의 "제품"메뉴에서 "분석"을 선택하십시오. 수동 레퍼런스 카운팅 메모리 문제를 확인하는 것이 놀랍습니다. Instruments는 유용하지만 위의 세 번째 예에서와 같이 잘못된 결론을 쉽게 내릴 수 있습니다 (예 : '이런, 모든 문자열을 해제해야합니다'). 정적 분석기는 이러한 문제 중 일부를 지적했을 것입니다.

    결론 다음으로 진행하기 전에 항상 정적 분석기의 건강 상태를 확인하십시오. 분석기가 문제가 무엇인지 정확하게 말할 수있을 때 인스 트루먼 트에서 어떤 문제가 나타날 수 있는지 리버스 엔지니어링하려고 시도 할 필요는 없습니다.

  2. 특정 매달려있는 포인터가 앱을 충돌시키지 않았다는 결론을 내리지 말고 다른 것을 결론 내릴 것을 권합니다. 그것은 단지 예측할 수 없습니다. 좀비를 켜면 (일시적으로, 프로덕션 애플리케이션이 아닌 개발/테스트 목적으로 만), 이전에 할당 해제 된 오브젝트를 참조하려는 시도에주의를 기울일 것입니다.

  3. 테스트에 NSString을 사용하고 있습니다.NSString에는 비표준 동작을 생성 할 수있는 내부 메모리 최적화 기능이 있습니다. 이러한 종류의 실험에서 NSString을 사용하는 것에 조심해야합니다.

    나를 잘못 이해하지 마십시오. Basic Memory Management Rules을 모두 따르면, NSString이 올바르게 작동합니다. 그러나 의도적으로 이러한 메모리 관리 규칙을 따르지 않을 때 어떤 종류의 오류/충돌이 발생하는지 조사하려는 경우 NSString은 오도 할 수 있음을 유의하십시오.

  4. 물론 ARC를 사용하면 삶이 크게 단순해질 것입니다. 자세한 내용은 Transitioning to ARC Release Notes을 참조하십시오.