본문 바로가기
연구하기/iOS App Dev

UIImage 의 imageNamed:

by 썰렁황제 2011. 12. 20.
  iOS 개발 경험이 많지 않은 시점에서 위의 메소드를 사용하다가 종종 비정상종료를 경험한 사람들이 있으리라 생각한다. 오늘은 이에 대해 언급해 볼까 한다.

  일반적인 iOS 의 메모리 정책에 따르면, 해당 클래스의 정적 메소드들이 개체값을 반환 시, 자신이 직접 관리하는 형태가 아니라 생성하고 끝내는 형태라면, 해당 개체에 autorelease 를 걸어서 자신이 더 이상 소유권이 없다고 명시하고 반환하는 것을 원칙으로 하고 있다. 따라서, 이러한 개체를 반환받았을 때, 이 개체를 런타임 중에 지속적으로 사용하려 한다면, 해당 개체에 retain 을 걸어 해당 개체의 지속성을 직접 연장해야만 하는 것이 iOS 상의 기본적인 개체 사용 패턴이다.

  하지만 위에서 이야기했듯이, 자신이 직접 관리하는 형태, 즉 개체 지속성을 유지하는 개체 내에서라면 이것이 달라지게 된다. 그들 중 자주 사용되며 더불어 정책을 정확히 파악하지 못해 자주 실수하는 항목 중 하나가 오늘 언급하려는 UIImage 의 imageNamed: 메소드이다.

  UIImage 의 imageNamed: 는 시스템 내부의 캐시를 사용하여 이미지를 캐싱하는 것이 포함되어 있다. 이 방식의 장점은, 같은 이름의 리소스를 반복 사용할 경우 중복로딩하지 않기 때문에 로딩 속도도 줄어들고 메모리도 적게 먹는다는 장점이 있다. 물론 캐시에 데이터를 넣으므로 사용을 오래하면 할 수록 점진적으로 전체 메모리 사용량은 늘어나겠지만.

  그렇기 때문에 반환되는 UIImage 개체는 내부적인 캐시 테이블에 retain 이 되어 있고, 이 때문에 외부에서 사용할 때에는 반환받은 UIImage 개체에 굳이 retain 을 걸지 않더라도 메모리가 release 되지 않아 지속적으로 남아있게 된다. 따라서 개발자는 무심코 그냥 사용해도 되나보다라는 생각으로 retain 을 하지 않게 된다. 뭐 사실 여기까지는 큰 문제가 없다.

  문제는 이 시스템 캐시에 쓸 수 있는 공간이 무한정인 것은 아니므로 어느 시점이 되면 해제를 시작하게 된다는 것이다. 해제를 시작하게 되면 내부 캐시 테이블에서 release 가 호출되므로, 외부에서 별도로 retain 을 하지 않았다면 메모리는 그 즉시 해제된다. 만약 외부에서 imageNamed: 함수를 통해 반환받은 개체를 retain 하지 않고 지속적으로 사용하고 있었다면, 이 해제된 개체의 참조를 그대로 들고 있게 되므로 이 개체에 접근하는 순간 EXC_BAD_ACCESS 를 보게 된다.

  특히 이 해제는 iOS 5에서는 어플 실행 중 Home 버튼을 누르면 거의 100% 발생하게 된다. 그러므로 위와 같이 참조를 유지하고 있었다면 어플리케이션이 다시 활성화되어 해당 이미지를 그리려는 순간 비정상 종료되어버릴 것이다.

  따라서 이 메소드로 반환된 개체를 안전하게 사용하려면, 해법은 다음과 같다.

  1. 반환받은 직후 retain 을 걸어둔다.
  2. 특정 멤버변수에 할당하지 말고 함수에서 반환된 개체 그 자체로 사용한다.

  2번의 경우는 메인 스레드가 아닌 스레드에서는 어떨지 파악하기 어렵다. 좀 더 고려해 봐야 할 듯.


  P.S 틀린 점이 있다면 지적 부탁드립니다.
반응형