2018년 4월 9일 월요일

[Objective-C] Block implicitly retains 'self' 경고 해결하기

Xcode 를 9.3 으로 업데이트 하기 전만 해도 별 문제 없던 프로젝트가 갑자기 아래와 같은 경고를 내뿜기 시작 했습니다.
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
블록이 self 를 잡아 물고 가버릴 수도 있는데 명확하지 않다는 식으로 이해가 됩니다. 그런데 경고가 발생한 위치를 보면 위 경고 메시지가 뭘 원하는 것인지 도통 이해 할 수가 없었습니다. 경고라 가볍게 넘어 갈 수도 있겠지만 일단 해결하는 방법을 알아봅시다.

문제의 코드

기본 프로젝트 설정(?)에서 아래와 같은 코드를 Xcode 9.3 으로 빌드해 보면 경고를 볼 수 있습니다.
@interface ViewController () {
    int _privateMember;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    
    [self someBlockOperation:^(int value) {
        NSLog(@"value = %d", _privateMember);
    }];
}

- (void)someBlockOperation:(void (^)(int value))block
{
    block(10);
}

@end
특정 뷰 컨트롤러를 구현하는 예제이긴 합니다만 뷰 컨트롤러에 연관된 문제는 아닙니다. 여기서 굵게 표시해 둔 프라이빗 멤버를 액세스 하는 곳의 코드에서 문제의 경고가 발생합니다. 코드에서 확인 할 수 있다시피 블록(Block) 내부에서 self 와 같은 키워드를 전혀 사용하고 있지 않음에도 self 가 납치(?) 될 것 같다고 Xcode 가 걱정해 줍니다.
분명 이전에는 별 문제 없어야 할 코드인데 왜 경고가 나는 것일까요?

프로퍼티가 아닌 인터페이스에 멤버 변수를 선언해서 사용하는 스타일은 이미 오래전부터 쓰여오던 관행이기도 했기에 적잖이 당황 할 수도 있는 경고일지도 모르겠습니다. 심지어 예전에는 프로퍼티를 쓰려면 반드시 이런 멤버를 만들고 @synthesize 로 프로퍼티와 멤버를 묶어 주는 코드를 만들어야 했습니다. 이런걸 기억하시는 분은 얼마나 계실지 모르겠지만...

해법

블럭 내부의 코드를 아래와 같이 self 와 프라이빗 멤버와의 관계를 명확하게 제시(?)해 주면 경고는 사라집니다.
- (void)viewDidLoad
{
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    
    [self someBlockOperation:^(int value) {
        NSLog(@"value = %d", self->_privateMember);
    }];
}
'->' 오퍼레이터를 썼다는 점이 특이합니다. 마치 C 에서 구조체 포인터의 멤버 액세스가 떠오르는 문법을 오랜만에 보게 되는 것 같습니다. 경험 상 Objective-C 에서 저런 문법을 쓰는 경우는 C++ 코드를 가져오는 경우 아니면 거의 없었으니깐요.
그럼 경고에서 운운하던 retain 문제는 해결되는 것일까요? 아직 명확한 근거(?)는 찾지 못 하고 있습니다만 각종 커뮤니티(?)의 이야기들을 볼 때 'self->' 코드는 weak 참조를 만드는 것으로 판단하는 것 같습니다. 해결되는 것으로 대충 생각하죠 ;-)

뭐 하여간 이렇게 오늘도 문제 하나를 해결해 봅니다.

사족

빌드 설정을 고쳐서 해결하는 방법도 있는 것 같지만, 해당 경고가 발생하는 이유는 명확하다고 생각되기에 이런 식의 해법을 써 보았습니다.

어쨌든 간에, Swift 에 밀려 소외 될 줄 알았던 Objective-C 도 여전히 발전하고 있었습니다. 블록(스위프트로 치자면 클로져) 내에서 액세스 하는 변수가 누구 소유인지를 명확히 해서 코드 읽기 겸 메모리 참조 문제를 해결 하려는 의도로 이해하고 있습니다.

어려울 건 없으니 익숙해지는 것이 문제인 것 같습니다. :-)

[관련글] [Objective-C] Block Syntax 초간단정리

댓글 없음 :