Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save linearhw/68dd744e3a24dc4e15a421e661c3a86f to your computer and use it in GitHub Desktop.
Save linearhw/68dd744e3a24dc4e15a421e661c3a86f to your computer and use it in GitHub Desktop.
Effective Objective-C 40
// Using the network fetcher
@implementation EOCClass {
    EOCNetworkFetcher *_networkFetcher; // self -> fetcher 
    NSData *_fetchedData;
}

- (void)downloadData {
    NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData *data){ // fetcher -> block
        NSLog(@"Request URL %@ finished", _networkFetcher.url);
        _fetchedData = data; // block -> self
    }];
}
@end

retain cycle 이 생기기 때문에 memory leak 이 발생한다.
이를 해결하는 방법은? 하나를 nil 로 만들어버리면 된다.

  • block 안에서 fetcher를 nil 로 만들어버리면 cycle 이 끊긴다
  • fetcher 안에서 block 을 실행한 후 block 자체를 nil 로 만들어버려도 cycle 이 끊긴다

그런데 만약 block 을 public 으로 공개했다면? 나중에 내 코드를 사용하게 되는 사람이 block 을 직접 nil 로 만들어야 한다.
그리고 사용자가 그렇게 잘 해주리라는 보장이 없다. 그러므로 조심조심

... block 에 weak reference 를 넣으면? 책엔 없지만 다뤄야 할 것 같았다

// Using the network fetcher
@implementation EOCClass {
    EOCNetworkFetcher *_networkFetcher; // self -> fetcher 
    NSData *_fetchedData;
}

- (void)downloadData {
    NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
    _networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];

    __weak EOCClass *weakSelf = self;
    [_networkFetcher startWithCompletionHandler:^(NSData *data){ // fetcher -> block
        NSLog(@"Request URL %@ finished", weakSelf.networkFetcher.url);
        weakSelf.fetchedData = data; // block -> self (하지만 약한 참조)
    }];
}
@end

그런데 만약...

[_networkFetcher startWithCompletionHandler:^(NSData *data){ // fetcher -> block
    NSLog(@"Request URL %@ finished", weakSelf.networkFetcher.url);

    // 여기까진 weakSelf 가 살아있다가
    // 갑자기 dealloc 이 되어서 (View Controller 가 dismiss 됐다거나) weakSelf 가 nil이 되었다면?
    // 앞에 까지만 실행되고 여기부터 실행되지 않을 것이다. 
    weakSelf.fetchedData = data; // block -> self

    // 그렇다고 crash 가 나는 건 아님. weakSelf->fetchData 같은 문법을 쓰면 crash가 나지만 . 문법은 괜찮다.
    // 그러나 중간까지만 실행되고 갑자기 끝나는 건 확실히 정상적인 작동은 아니다. 
}];
@end

그러니까 이렇게 되어야 한다.

[_networkFetcher startWithCompletionHandler:^(NSData *data){ // fetcher -> block
    EOCClass *strongSelf = weakSelf;
    if (strongSelf == nil) { return; }

    // block -> self (약한 참조. 따라서 self 가 dealloc 되었을 가능성이 있다)
    // dealloc 되었으면 그대로 종료. 
    // 그렇지 않으면 self 가 예기치 못하게 dealloc 되는 걸 막기 위해 retain 한다.

    NSLog(@"Request URL %@ finished", strongSelf.networkFetcher.url);
    strongSelf.fetchedData = data; 
}];
@end

근데 또 만약

[_networkFetcher startWithCompletionHandler:^(NSData *data){ // fetcher -> block
    EOCClass *strongSelf = weakSelf;
    if (strongSelf == nil) { return; }

    [strongSelf.dataManager insertData:data withCompletionHandler:^(Bool completed){
        // 여기서 self 를 참조하고 싶다면? 가령 self 의 completed 라는 property 에 저장하고 싶다거나?
    }]; 
}];
@end

1) strongSelf
2) EOCClass *newStrongSelf = weakSelf;
3) __weak EOCClass *newWeakSelf = strongSelf; { EOCClass *newStrongSelf = newWeakSelf; }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment