시간이 오래 걸리는 작업(ex. 네트워크)을 할 때, 이 작업이 끝날 때까지 ui thread 가 기다리고 있는 건 바람직하지 않다.
특정 상황에서 앱이 특정 시간동안 응답하지 않으면 강제 종료가 될 것이다.
- system watchdog
https://ko.wikipedia.org/wiki/%EC%9B%8C%EC%B9%98%EB%8F%85_%ED%83%80%EC%9D%B4%EB%A8%B8
사람이 쉽게 접근해서 고치기 힘든 컴퓨터에 대해 오작동을 감지하고 복구하기 위한 전자 타이머
따라서 시간이 오래 걸리는 작업은 비동기로 작업하고, 작업이 끝났을 때 알림을 받고 싶을 것이다.
- delegate 패턴
아래와 같이 서로 다른 여러 개의 클래스에 대해 처리해야 할 때 delegate 에서 구분해줘야 한다는 단점이 있다.
// Multiple fetchers with delegation
- (void)fetchFooData {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"];
_fooFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
_fooFetcher.delegate = self;
[_fooFetcher start];
}
- (void)fetchBarData {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/bar.dat"];
_fooFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
_fooFetcher.delegate = self;
[_fooFetcher start];
}
- (void)networkFetcher:(EOCNetworkFetcher*)networkFetcher didFinishWithData:(NSData*)data {
if (networkFetcher == _fooFetcher) {
_fetchedFooData = data;
_fooFetcher = nil;
} else if (networkFetcher == _barFetcher) {
_fetchedBarData = data;
_barFetcher = nil;
}
// etc
}
- 핸들러 블록
블록이 정의된 scope 안에 있는 모든 변수에 접근할 수 있다는 장점이 있다.
// Multiple fetchers with completion handler block
- (void)fetchFooData {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/foo.dat"];
EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data){
_fetchedFooData = data;
}];
}
- (void)fetchBarData {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/bar.dat"];
EOCNetworkFetcher *fetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data){
_fetchedBarData = data;
}];
}
사용할 수 있는 방법은 크게 두 가지
- 성공했을 때와 실패했을 때의 로직을 분리해서 사용한다.
[fetcher startWithCompletionHander:^(NSData *data){
// Handle success
} failureHandler:^(NSError *error){
// Handle failure
}];
이렇게 쓰면 가독성이 좋아진다.
- 성공했을 때와 실패했을 때의 로직을 블록 하나에 넣는다.
[fetcher startWithCompletionHander:^(NSData *data, NSError *error){
if (error) {
// Handle failure
} else {
// Handle success
}
}];
이렇게 쓰면 처리가 유연하다는 장점이 있다. 데이터와 에러 둘 다 필요한 상황에 대응할 수 있다.
핸들러 블록을 사용하는 API를 설계할 때 블록이 들어갈 큐를 parameter 로 주게 만들 수 있다.