2009-03-03 3 views
15

간단한 일을하려고합니다. 인터넷에서 이미지를 읽고, 아이폰에있는 앱의 문서 디렉토리에 저장하고, 나중에 그 파일로 다른 일을 할 수 있도록 파일에서 다시 읽습니다. 파일을 작성 잘 작동하지만 내가 그것을 다시 읽으려고 GDB에서 EXC_BAD_ACCESS 오류가 나는 방법을 잘 모르겠다. 내가 파일에서있는 UIImage를 초기화 할 때 코드는 return 문에 실패어떻게 iphone에서 발생하는 EXC_BAD_ACCESS 오류를 해결합니까

-(UIImage *) downloadImageToFile { 
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

NSString *documentsDirectory = [paths objectAtIndex:0]; 

[paths release] 
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

[data writeToFile:path atomically:YES]; 

return [[UIImage alloc] initWithContentsOfFile:path]; 
} 

: 여기처럼 내 코드는 기본적으로 모습입니다. 어떤 아이디어?

편집 : 원래 코드의 문제점이었던 릴리스를 추가하지 않았습니다. 메모리를 잘못 관리하는 경우

답변

8

코드는 Objective-C에서 메모리 관리가 작동하는 방식에 대한 심각한 지식이 부족함을 보여줍니다. 부적절한 메모리 관리로 인해 EXC_BAD_ACCESS 오류 외에 메모리 누수가 발생하여 iPhone과 같은 소형 장치에서 임의의 충돌이 발생할 수 있습니다.

나는 읽고 thorogh 이것을를 줄 것이 좋습니다

Introduction to Memory Management Programming Guide for Cocoa

+0

링크를 제공해 주셔서 감사합니다. 내 실제 코드에서는 내가 명시 적으로 할당하지 않았을 때 경로를 해제하려고했는데 나중에 문제가 발생했습니다. 그리고 당신 말이 맞습니다. 저는 obj-c로 시작하고 있습니다. 그리고 나는 그것이 생각했던 것보다 훨씬 더 복잡합니다. – Kevlar

+0

아무 문제도, 내가 너무 가혹한 소리하지 않았 으면 좋겠다. 그 문서는 읽을만한 가치가 있습니다. 당신의 개발에 행운을 비네! – August

+2

최근에 EXC_BAD_ACCESS에 대한 많은 질문을 던지면서 디버깅 기술 목록과 함께 http://loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html에 설명해 드렸습니다. 이 경우 (릴리스가 너무 많음) Build and Analyze/scan-build가 아마 플래그를 지정했을 것입니다. 그렇지 않으면 좀비를 사용하면 분명히 가질 수 있습니다. –

0

이러한 오류는

내가 실험 많은 시간을 보낸 다음 ..

UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path]; 
return [myImage autorelease]; 

같은 일을 시도 (예. 객체가 조기에 해제 또는 유사한되고있다) 발생 release/autorelease의 개념을 익히는 동안. 경우에 따라 retain 키워드를 재생해야 할 수도 있습니다 (이 경우는 아닐 수도 있음)

다른 옵션은 단순히 경로가 없거나 읽을 수없는 경우 일 수 있습니다.

+0

파일이 생성되어 파일 시스템에 있기 때문에 존재한다는 것을 알고 있습니다. 나는 내가 찾을 수있는 것을보기 위해 메모리 관리와 관련하여 더 많은 것을 할 것이다. – Kevlar

+0

경로에서 슬래시를 가져 와서 프로젝트에 있는지 확인하십시오. 그것이 해당 디렉토리에 있다면 중요하지 않지만 액세스하려면 프로젝트에 추가해야합니다. – Genericrich

+0

이것은 틀린 것입니다. 반환 된 값을 자동 렌더링해야하지만 클래스의 인스턴스 변수로 유지하면 안됩니다. –

-1

아마도 initWithContentsOfFile은 경로 인수를 사용하지 않습니까? 주위에 UIImage에 대한 다른 init 메서드를 둘러보십시오, 나는 거기에 다른 하나의 경로를 받아 들일 것 같아요.

경로를 만들기 위해해야 ​​할 일이 더 좋을 수도 있습니다. "묶음"으로 무언가를하는 것을 기억합니까? 너무 모호하게해서 유감스럽게 생각합니다.

-2

경로에서 슬래시를 가져 와서 프로젝트에 있는지 확인하십시오. 그것이 해당 디렉토리에 있다면 중요하지 않지만 액세스하려면 프로젝트에 추가해야합니다.

+0

프로그램을 실행하기 전에 파일을 프로젝트에 추가 할 수 없습니다. 다른 곳에서 읽은 후에 파일을 저장하고 있습니다. – Kevlar

+0

NSString의 파일 시스템 경로 조작 방법을 사용하면 슬래시를 포함시키지 않아도 될지 걱정할 필요가 없습니다. 그것은이 문제와 관련이 없습니다. –

1

은 확실히 메모리 관리가 빠른 검토를 지배 제공합니다. 당신이 얻는 오류를 일으킬만한 것은 없지만, 당신은 당신이 할당 한 모든 객체를 유출하고 있습니다. retain/release 패턴을 이해하지 못한다면, 코드에서 오브젝트를 올바르게 유지하지 못하는 또 다른 부분이있을 수 있으며, 이것이 EXC_BAD_ACCESS 오류의 원인입니다.

또한 NSString은 파일 시스템 경로를 다루는 메소드를 가지고 있으므로 분리 기호에 대해 걱정할 필요가 없습니다.

10

나를 많이 도와주는 한 가지는 objc_exception_throw에 중단 점이있는 것입니다. 던져 질 예외가 생길 때마다,이 중단 점을 맞았고 스택 체인을 디버깅 할 수 있습니다. 나는이 중단 점을 내 iPhone 프로젝트에서 항상 사용 가능하게 유지합니다.

이렇게하려면 xcode에서 왼쪽 창 "Groups & 파일"의 맨 아래로 이동하여 "중단 점"을 찾으십시오. 프로젝트 브레이크 포인트를 열고 세부 정보 창 (위)에 "심볼을 두 번 클릭하십시오"라는 파란색 필드가 나타납니다. 그것을 두 번 클릭하고 "objc_exception_throw"를 입력하십시오.

다음 번에 예외가 발생하면 중단되고 디버거에서 예외를 유발 한 코드로 스택 체인을 걸어 갈 수 있습니다.

+0

감사합니다. 이것은 매우 유용한 팁입니다. –

+0

나는 이것이 Xcode의 Run 메뉴에서 "Objective-C 예외에서 멈추다"라고 생각한다. –

1

일반적으로 코드에서 EXC_BAD_ACCESS를 얻는다면 그 이유를 알 수 없으므로 NSZombie를 사용해보십시오 (아니요, 농담이 아닙니다).

Xcode에서 왼쪽에있는 실행 파일 섹션을 확장하십시오. 프로젝트와 동일한 이름을 가진 목록을 두 번 클릭하십시오 (유일한 것이어야합니다). 팝업 창에서 인수로 이동하고 하단에서 더하기 단추를 클릭하십시오. 이름은 NSZombieEnabled해야하며 값은 릴리스 된 개체에 액세스하려고 할 때, 당신은 당신이 무슨 일을하는지의 더 나은있을 것이다, YES

이 방법으로 설정해야합니다. 버그를 알게되면 값을 NO으로 설정하십시오.

희망이 있으면 도움이됩니다.

46

주 : 이것은 비 ARC 메모리 관리에 특별히 적용.

이것은 많은 의견을 갖고 있으며 "Objective-C에서 메모리 관리가 작동하는 방식에 대한 지식이 심하게 부족하다는 것을 확인한 답변이 적절합니다"라고 말한 적이 있지만 특정 오류를 지적한 사람은 아무도 없습니다. 나는 그 (것)들에 만진 대답을 추가 할 것입니다.

우리가 호출하는 방법에 대해 기억해야하는 기본 수준의 규칙 : 메서드 호출이, 또는 복사 ALLOC, 새로운을 유지 단어를 포함하는 경우

  • , 우리 생성 된 객체의 소유권을 갖는다. ¹ 우리가 물건의 소유권을 가지고 있다면, 우리는 그것을 풀어주는 책임이 있습니다.

  • 이라는 메서드 호출에이 포함되어 있지 않으면 생성 된 개체의 소유권이 없습니다. ¹ 에 개체의 소유권이없는 경우이를 공개하는 것은 이 아니며 우리의 책임이므로 결코 수행해서는 안됩니다.

는의 각 줄에 영업 이익의 코드를 살펴 보자 :
-(UIImage *) downloadImageToFile { 

우리는 새로운 방법을 시작했다. 이렇게함으로써 우리는 생성 된 각 객체가 살아있는 새로운 맥락을 시작했습니다. 이것을 명심하십시오.다음 라인 :

NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

우리는 url 소유 : 단어 ALLOC는 우리는 개체의 소유권을 가지고 우리는 그것을 자신을 해제해야합니다 것을 우리가 알 수 있습니다. 코드가 없으면 메모리가 누수됩니다. 네 개의 마법의 단어 더 사용, 그래서 우리는 소유권을 가지고 있지 않고, 그것을 자신을 해제해서는 안 :

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

우리는 paths를 소유하지 않습니다. 마법의 단어 = 소유권 :

NSString *documentsDirectory = [paths objectAtIndex:0]; 

우리는 documentsDirectory를 소유하지 않습니다.

[paths release] 

다시 우리가 경로를 보유하고 있지 않습니다 것을 볼 줄 몇가는, 그래서 우리는 더 이상 존재하지 않는 무언가를 액세스하려고으로이 릴리스는 EXC_BAD_ACCESS 충돌의 원인이됩니다. 마법의 단어 = 소유권 :

NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

우리는 path를 소유하지 않습니다.

NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

우리는 data 소유 : 단어를 ALLOC를이 우리가 개체의 소유권을 가지고 사용을 지시하고 우리가 그것을 자신을 해제 할 필요가있다. 코드가 없으면 메모리가 누수됩니다.

다음 두 줄은 아무것도 만들지 않거나 해제하지 않습니다. 그런 다음 마지막 줄이옵니다.

} 

메서드가 끝났기 때문에 변수의 컨텍스트가 종료되었습니다. 코드를 살펴보면 우리는 urldata을 모두 소유하고 있었지만 어느 쪽도 공개하지 않았다는 것을 알 수 있습니다. 따라서이 메서드가 호출 될 때마다 코드에서 메모리가 누수됩니다.

NSURL 개체 url은 그리 크지 않기 때문에 누출을 알지 못할 수도 있습니다. 그래도 정리해야하지만 누출 될 이유는 없습니다.

NSData 개체 data은 매우 큰 이미지 일 수 있습니다. 이 메서드가 호출 될 때마다 개체의 전체 크기가 누출됩니다. 테이블 셀이 그려 질 때마다 이것이 호출되었다고 상상해보십시오 : 전체 앱을 충돌시키는 데 오랜 시간이 걸리지는 않습니다.

그렇다면 문제를 해결하기 위해 무엇을해야합니까? 바로 전에,

-(UIImage *) downloadImageToFile { 

    // We own this object due to the alloc 
    NSURL * url = [[NSURL alloc] initWithString: self.urlField.text]; 

    // We don't own this object 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 

    // We don't own this object 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 

    //[paths release] -- commented out, we don't own paths so can't release it 

    // We don't own this object 
    NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"]; 

    // We own this object due to the alloc 
    NSData * data = [[NSData alloc] initWithContentsOfURL:url]; 

    [url release]; //We're done with the url object so we can release it 

    [data writeToFile:path atomically:YES]; 

    [data release]; //We're done with the data object so we can release it 

    return [[UIImage alloc] initWithContentsOfFile:path]; 

    //We've released everything we owned so it's safe to leave the context 
} 

어떤 사람들은 한 번에 모든 것을 공개하는 것을 선호 : 그것은 우리가 단순히 그들이 사용하고 일반적으로 바로 마지막 시간 이후, 곧 우리가 더 이상 필요하지 않기 때문에 개체를 해제 할 필요가 꽤 간단합니다 컨텍스트는 메소드의 끝에서 닫힙니다. 이 경우 [url release];[data release];은 닫는 } 중괄호 바로 앞에 나타납니다. 코드를 더 빨리 공개하면 코드가 명확 해 지므로 나중에 오브젝트를 사용하여 완료 한 부분을 정확하게 볼 수 있습니다.

은 요약하면 : 우리는 상황이 종료되기 전에 그래서 그들을 해제해야 호출 방법에 alloc, new, copy, 또는 retain로 만든 개체를 소유하고 있습니다. 우리는 다른 것을 소유하지 않으며 절대 공개해서는 안됩니다. 네 개의 단어에서 실제로 마법 거기에 아무것도


¹, 그들은 단지 문제의 방법을 만든 애플에서의 사람들에 의해 지속적으로 사용되는 알림입니다. 자체 클래스의 초기화 또는 복사 메서드를 만드는 경우 해당 메서드에 alloc, new, copy 또는 retain이라는 단어를 포함시키는 것은 우리의 책임이며 우리 이름에 사용하지 않으면 소유권이 지났는지를 스스로 기억할 필요가 있습니다.

+0

굉장한 설명 !!! 이것은 나의 벽지에 이렇게 가고있다 !!! – doNotCheckMyBlog

+0

동의하고 훌륭하게 명확한 설명. – Benjamin

+1

아마도이 사이트에서 읽은 가장 좋은 답변 일 것입니다. 질문에 답할뿐만 아니라 설명하기 위해 넘어갑니다. – amcc

관련 문제