http://seorenn.blogspot.com/2014/11/swiftobjc.html


[Swift/ObjC] 딜레이 이후 실행하기

특정 사유로 특정 코드를 특정 시간 이후에 실행시켜야 할 경우가 있다. 이유야 어쨌든, 이렇게 특정 시간(딜레이, Delay) 이후에 코드를 실행시키도록 하는 코드를 알아보자.

1. Objective-C의 경우

작성한 클래스에 아래와 같은 메소드가 있다고 치자. 이 메소드를 특정 시간 이후에 호출시켜 보고자 한다.
- (void)runningAfter5Seconds {
    NSLog(@"runningAfter5Seconds");
}

1.1. performSelector:withObject:afterDelay:

NSObject에 선언되어 있는 메소드이기 때문에 사실상 거의 모든 클래스에서 이용이 가능한 메소드이다.
[self performSelector:@selector(runningAfter5Seconds) withObject:nil afterDelay:5.0];
코드 내용 만으로도 언제 특정 셀렉터를 호출시키는지 알 수 있다. 위의 경우는 5초 후에 runningAfter5Seconds 메소드를 실행시킨다. 딜레이를 5.0을 줬다는 것에서 밀리세컨드 단위도 무리없이 호출시킬 수 있다고 판단 가능할 것이다.

1.2. dispatch_after

dispatch_after 함수는 GCD(Grand Central Dispatch) 기능을 통해 제공되는 함수이다. (메소드가 아니다) 아래와 같이 실행시켜 줄 수 있다. 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self runningAfter5Seconds];
});
여기서는 시간단위가 좀 다르니 주의하자. 최신의 Xcode라면 dispatch_after를 입력하면 자동으로 스니펫 자동완성 항목이 뜰 것이다.

dispatch_after의 경우 실행시킬 셀렉터가 아닌 코드를 블럭으로 넘기기 때문에 별도로 메소드 등으로 뽑아낼 필요는 없어서 개인적으로 편하다고 생각한다.

참고로 실수가 아닌 정수 초단위로 딜레이를 준다면 좀 더 단순한 방법으로도 호출이 가능하다. 아래 Swift편에서 코드를 올려놨는데, 그냥 시간 부분에 정수를 넣으면 된다. -_-;

1.3. NSTimer

아주 단순하게 NSTimer를 이용해 나중에 실행되도록 스케쥴링 하는 방법이 있다. 
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(runningAfter5Seconds) userInfo:nil repeats:NO];
역시 코드 내용만으로 파악 가능할 것이다.

마지막의 repeats를 YES로 주게 되면 매 시간 마다 호출되기 때문에 지속적인 스케쥴링에 응용 할 수 있다.

2. Swift의 경우

Swift의 경우 NSObject를 상속받을 경우 거의 비슷하게 이용 할 수 있지만 Selector와 관련해서 Type-Safe를 위배하는 문제가 생기기 때문에 약간 제한이 있다.

동일하게 이번에도 아래와 같은 메소드를 선언해 놓았다고 치고 살펴보자. 
func runningAfter5Seconds() {
    println("runningAfter5Seconds")
}

2.1. dispatch_after

역시 GCD의 dispatch_after를 활용하는 방법이다.
dispatch_after(5, dispatch_get_main_queue()) {
    self.runningAfter5Seconds()
}
dispatch_after의 클로저 인수가 제일 마지막에 오기 때문에 위와 같이 클로져 표현을 좀 더 단순화 시킬 수 있다. 역시나 클로져를 넘기기 때문에 별도의 메소드로 분리할 필요가 없어서 편하다.

만약 밀리세컨드 등 좀 더 세분화된 딜레이가 필요하다면 dispatch_time을 이용해보자. 
let delay = 5.0 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
    self.runningAfter5Seconds()
}
위 코드는 위 Objective-C에서 언급한 dispatch_after와 모양이 비슷하다. :-)

Swift 3 의 경우 DispatchQueue 로 단일화 시켜놨기 때문에 이전 코드 그대로는 사용 할 수 없고 아래와 같은 식으로 쓸 수 있다. 
let time = DispatchTime.now() + .seconds(5)
DispatchQueue.main.asyncAfter(deadline: time) {
    self.runningAfter5Seconds()
}
모양세가 이전보단 쓰기 편해졌다.

2.2. NSTimer

Objective-C와 동일하게 NSTimer를 이용 할 수 있다. 다만 이 경우 셀렉터(Selector)를 사용하기 때문에 사용자의 클래스가 NSObject를 상속받고 있어야 한다는 제한이 있다. 
NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "runningAfter5Seconds", userInfo: nil, repeats: false)
형식은 동일하다. repeats를 true를 줘서 지속적인 호출을 시키는 것 또한 가능하다. 다만 selector라 불리우는 Swift와는 어울리지 않는 기능을 써야 한다는 점은 좀 안타깝다.

관련글: