2015년 1월 16일 금요일

[Swift] 정체불명의 Dictionary 업데이트 런타임에러

이번 글은 개인적으로 빡쳐서 쓰는 한탄글이다 -_-;

종종 Swift는 아직 준비가 덜되었다고 떠벌리고 다녔는데 이번에는 정체불명의 오류로 인해 다시금 준비가 덜 되었다고 이야기를 해야 할 것 같다. 이번 글은 Swift의 Dictionary(사전형)의 아이템을 업데이트 할 때 발생하는 EXC_BAD_ACCESS에 관한 이야기이다.

컴파일러 최적화 오류일 가능성도 있기 때문에 몇 가지 환경 설명이 필요하다.
  • 사용하는 Xcode 는 버전 6.1.1이다.
  • 프로젝트 코드는 Objective-C 코드와 Swift코드가 섞여있다.
이 점을 감안하자.

아래와 같은 식의 코드가 있다.
private SomeClass _sharedInstance = SomeClass()

class SomeClass {
    private var dict = [String:String]()

    class var sharedInstance: SomeClass {
        return _sharedInstance
    }

    ...

    func doSomeWork(name: String, work: String) {
        ...
        self.dict[name] = work
        ...
    }
}
이 코드 만으론 용도가 좀 불분명 하겠지만 그냥 그러려니 하자. 실제로 문제가 생긴 코드와 구조가 거의 동일하다.

우선 private 로 선언된 전역변수인 _sharedInstance는 싱글턴 객체를 저장해 두기 위한 녀석이다. 그리고 class var로 선언된 sharedInstance 프로퍼티는 이 싱글턴 변수를 리턴하기 위한 녀석이다. 따라서 이 둘은 싱글턴 패턴을 위한 것인데 이것이 과연 문제에 영향을 끼치는지는 파악되지 않은 상태이다.

문제의 코드는 doSomeWork 메소드이다. 여기서는 내부 프로퍼티로 존재하는 self.dict에 값을 넣는 역활을 한다. 그런데 이 메소드가 없는 키(key)를 만들 때는 문제가 없다가 이미 존재하는 키의 데이터를 바꾸려고 하면 EXC_BAD_ACCESS 오류가 발생했다. 즉 아래와 같은 식의 코드가 실행되면 죽었다는 말이다.
SomeClass.sharedInstance.doSomeWork("some name", work: "foobar")
...
SomeClass.sharedInstance.doSomeWork("some name", work: "fooooobaaaaaar")
만약 dict를 var가 아닌 let으로 선언했다면 이미 빌드 단계에서 에러가 발생했을 것이니 관련이 없다. 문제는 왜 값을 업데이트 할 때만 이런 문제가 생기느냐는 점이다. (참고로 updateValue 같은 메소드로 호출해도 동일한 결과였다)

그런데 이 문제는 나만 겪은건 아니었나 보다. 관련된 stackoverflow.com의 스레드를 두 건 찾았다.
불행히도 명확한 해답이 없다. let hack 을 쓰라는둥 뭐라는 둥 써 있는데 뭘 말하는지 모르겠다.

끙끙거리며 오랜 시간 삽질을 하다 결국 다른 방법으로 시도해 봤다. 바로 Dictionary 대신 NSMutableDictionary를 사용해 보는 것.

코드를 아래와 같은 식으로 고쳤다.
private SomeClass _sharedInstance = SomeClass()

class SomeClass {
    private let dict = NSMutableDictionary()

    class var sharedInstance: SomeClass {
        return _sharedInstance
    }

    ...

    func doSomeWork(name: String, work: String) {
        ...
        let d = self.dict as [String:String]
        d[name] = work
        ...
    }
}
이 코드로 테스트 해 보니 아무런 문제가 없었다.

...

개인적으론 위 stackoverflow의 댓글들 처럼 컴파일러 버그 혹은 파운데이션 버그로 추측된다. 문제가 생길 아무런 이유가 없으니까.

하아.......... 삽질로 허비한 내 수 시간을 돌려줘~~~!!

[관련글] 스위프트(Swift) 가이드
[관련글] Swift - 컬렉션 타입(Collection Types)
[관련글] [Swift] 좀 더 단순한 싱글턴 패턴(Singleton Pattern)

댓글 없음 :