-performSelector : withObject : afterDelay :와 같이 지연 후 블록을 어떻게 트리거합니까?


735

/ / performSelector:withObject:afterDelay:과 같은 인수를 사용 하는 것과 같이 지연 후 기본 매개 변수로 블록을 호출하는 방법이 있습니까?intdoublefloat


이것은 GCD가 NSOperation이 할 수없는 일을 할 수있는 드문 시점입니까?
Anonymous White

답변:


1175

찾고 계신 것 같습니다 dispatch_after(). 블록이 매개 변수를 허용하지 않아야하지만 블록이 대신 로컬 범위에서 해당 변수를 캡처하도록 할 수 있습니다.

int parameter1 = 12;
float parameter2 = 144.1;

// Delay execution of my block for 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    NSLog(@"parameter1: %d parameter2: %f", parameter1, parameter2);
});

더 : https://developer.apple.com/documentation/dispatch/1452876-dispatch_after


88
사실, 그것은 사실이 아닙니다. __block 스토리지에있는 것으로 표시되지 않은 블록으로 캡처 된 오브젝트는 블록에 의해 보유되며 블록이 파괴 될 때 (보유 횟수가 0이 될 때) 블록에 의해 해제됩니다. developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
Ryan

9
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)스 니펫은 불쾌합니다. 더 깨끗한 방법이 없습니까?
samvermette 2016 년

7
예, dispatch_get_current_queue()항상 코드가 실행되는 큐를 반환합니다. 따라서이 코드가 메인 스레드에서 실행될 때 블록도 메인 스레드에서 실행됩니다.
Ryan

20
dispatch_get_current_queue()더 이상 사용되지 않습니다
Matej

9
밀리 초를 지정하려는 경우 NSEC_PER_SEC 외에 NSEC_PER_MSEC도 존재합니다.;)
cprcrack

504

dispatch_after나중에 블록을 호출하는 데 사용할 수 있습니다 . Xcode에서 입력을 시작 하고 다음과 같이 자동 완성을 dispatch_after누르십시오 Enter.

여기에 이미지 설명을 입력하십시오

다음은 "인수"로 두 개의 부동 소수점이있는 예입니다. 어떤 유형의 매크로에도 의존 할 필요가 없으며 코드의 의도는 분명합니다.

스위프트 3, 스위프트 4

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

스위프트 2

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
        println("Sum of times: \(time1 + time2)")
}

목표 C

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});

45
지연 시간이 두 배가되지 않도록주의하십시오. 따라서 0.5 초 동안 NSEC_PER_SEC * 0.5를 시도하지 마십시오! 밀리 초로 드롭하고 NSEC_PER_MSEC * 500을 사용해야합니다. 따라서 사람들이 NSEC_PER_SEC의 분수를 사용할 수 없음을 나타내려면 코드 샘플을 int delayInSeconds = 2로 변경해야합니다.
malhal

11
@malhal 사실, NSEC_PER_SEC * 0.5와 동일하게 작동합니다 NSEC_PER_MSEC * 500. dispatch_time64 비트 정수 를 예상 한다는 것은 맞지만 , 예상되는 값은 나노초입니다. NSEC_PER_SEC는로 정의되며 1000000000ull부동 소수점 상수 0.5를 사용하여 부동 소수점 산술을 암시 적으로 수행 500000000.0하여 64 비트 정수로 명시 적으로 캐스팅하기 전에 부동 소수점 산술을 수행합니다 . 따라서 분수를 사용하는 것은 완벽하게 허용됩니다 NSEC_PER_SEC.
junjie

202

Xcode 내장 코드 스 니펫 라이브러리를 사용하는 것은 어떻습니까?

여기에 이미지 설명을 입력하십시오

스위프트 업데이트 :

많은 투표로이 답변을 업데이트 할 수있었습니다.

내장 Xcode 코드 스 니펫 라이브러리는 언어 dispatch_after전용 objective-c입니다. 사람들은에 대한 자신 만의 맞춤 코드 스 니펫 을 만들 수도 있습니다 Swift.

이것을 Xcode로 작성하십시오.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(<#delayInSeconds#> * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
        <#code to be executed after a specified delay#>
    })

이 코드를 끌어서 코드 스 니펫 라이브러리 영역에 놓으십시오. 여기에 이미지 설명을 입력하십시오

코드 스 니펫 목록의 맨 아래에라는 이름의 새 엔티티가 있습니다 My Code Snippet. 제목을 편집하십시오. Xcode를 입력 할 때 제안 사항을 입력하십시오 Completion Shortcut.

자세한 내용은 CustomCodeSnippet 만들기를 참조하십시오 .

스위프트 3 업데이트

이 코드를 끌어서 코드 스 니펫 라이브러리 영역에 놓으십시오.

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
    <#code to be executed after a specified delay#>
}

19
Xcode에서 실제로이 기능을 사용하는 사람이 있습니까? 코드 제안 팝업으로 입력하고 사용하기 쉽습니다.
Supertecnoboff

6
알기 전까지는 복사 및 붙여 넣기가 가장 쉬운 코딩 방법이라고 생각했습니다. 이제 나는 그냥 드래그 앤 드롭 .... hahaha
arshu

58

Jaime Cham의 답변을 확장하여 아래와 같이 NSObject + Blocks 범주를 만들었습니다. 이 방법이 기존 performSelector:NSObject 방법 과 더 잘 일치한다고 느꼈습니다.

NSObject + Blocks.h

#import <Foundation/Foundation.h>

@interface NSObject (Blocks)

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay;

@end

NSObject + Blocks.m

#import "NSObject+Blocks.h"

@implementation NSObject (Blocks)

- (void)performBlock:(void (^)())block
{
    block();
}

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay
{
    void (^block_)() = [block copy]; // autorelease this if you're not using ARC
    [self performSelector:@selector(performBlock:) withObject:block_ afterDelay:delay];
}

@end

다음과 같이 사용하십시오.

[anyObject performBlock:^{
    [anotherObject doYourThings:stuff];
} afterDelay:0.15];

5
delay이어야 NSTimeInterval(a이다 double). #import <UIKit/UIKit.h>필요하지 않습니다. 그리고 왜 - (void)performBlock:(void (^)())block;유용한 지 알 수 없으므로 헤더에서 제거 할 수 있습니다.
의미 문제

@ meaning-matters, 유효 포인트 +1, 그에 따라 답변을 업데이트했습니다.
Oliver Pearmain

이, 더 정확한는 dispatch_after 사용하는 것입니다의 performSelector는 할당 해제에 명시 적으로 제거 할 수있다, 그렇지 않으면 당신은 정말 이상한 행동과 충돌로 실행됩니다 전혀 정확하지 않습니다
피터 Lapisu

21

어딘가의 클래스 (예 : "Util") 또는 Object의 Category에서 GCD를 사용하는 것보다 간단 할 수 있습니다.

+ (void)runBlock:(void (^)())block
{
    block();
}
+ (void)runAfterDelay:(CGFloat)delay block:(void (^)())block 
{
    void (^block_)() = [[block copy] autorelease];
    [self performSelector:@selector(runBlock:) withObject:block_ afterDelay:delay];
}

그래서 사용하려면 :

[Util runAfterDelay:2 block:^{
    NSLog(@"two seconds later!");
}];

3
@Jaimie Cham 왜 GCD를 통과하는 것이 어렵다고 생각합니까?
Besi

2
GCD를 거치는 것은 PerformSelector : afterDelay :와 약간 다른 동작을하므로 GCD를 사용하지 않는 이유가있을 수 있습니다. 예를 들어 다음 질문을 참조하십시오. stackoverflow.com/questions/10440412/…
fishinear

블록을 performSelector에 전달하기 전에 왜 블록을 복사합니까?
c roald

1
지연 돼서 죄송합니다. @ croald : 스택에서 힙으로 블록을 이동하려면 복사본이 필요하다고 생각합니다.
Jaime Cham

@Besi : 더 장황하고 의도를 숨 깁니다.
Jaime Cham

21

스위프트의 경우 dispatch_after메소드를 사용하여 전역 함수를 만들었습니다. 특별한 것은 없습니다 . 읽기 쉽고 사용하기 쉽기 때문에 이것을 더 좋아합니다.

func performBlock(block:() -> Void, afterDelay delay:NSTimeInterval){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), block)
}

다음과 같이 사용할 수 있습니다.

performBlock({ () -> Void in
    // Perform actions
}, afterDelay: 0.3)

1
인수를 바꾸고 이름을 바꾸는 것이 좋습니다 after. 다음과 같이 쓸 수 있습니다 :after(2.0){ print("do somthing") }
Lars Blumberg

16

여기 내 2 센트 = 5 방법이 있습니다.)

나는 이러한 세부 사항을 캡슐화하고 AppCode가 문장을 완성하는 방법을 알려주도록합니다.

void dispatch_after_delay(float delayInSeconds, dispatch_queue_t queue, dispatch_block_t block) {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, queue, block);
}

void dispatch_after_delay_on_main_queue(float delayInSeconds, dispatch_block_t block) {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after_delay(delayInSeconds, queue, block);
}

void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

void dispatch_async_on_background_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void dispatch_async_on_main_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_main_queue(), block);
}

8

PerformSelector : WithObject는 항상 객체를 취하므로 int / double / float 등과 같은 인수를 전달하기 위해 이와 같은 것을 사용할 수 있습니다.

// NSNumber는 객체입니다.

[self performSelector:@selector(setUserAlphaNumber:)
     withObject: [NSNumber numberWithFloat: 1.0f]       
     afterDelay:1.5];



-(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];

}

같은 방법으로 [NSNumber numberWithInt :] 등을 사용할 수 있으며 수신 방법에서 숫자를 [number int] 또는 [number double]과 같은 형식으로 변환 할 수 있습니다.


8

dispatch_after 함수는 주어진 시간이 지나면 블록 객체를 디스패치 큐로 디스패치합니다. 2.0 초 후에 UI 관련 탁을 수행하려면 아래 코드를 사용하십시오.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })

스위프트 3.0에서 :

            let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
            DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {

          })

오타가 있습니다. mainQueue, 대신mainQueue)
Bastian

5

다음은 지연 후 작업을 대기열에 넣는 Swift 3 방법입니다.

DispatchQueue.main.asyncAfter(
  DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
    // do work
}

5

다음 은 성가신 GCD 호출이 반복 되는 것을 방지 하는 편리한 도우미입니다 .

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

이제 다음 과 같이 Main 스레드에서 코드를 지연시킵니다 .

delay(bySeconds: 1.5) { 
    // delayed code
}

다른 스레드로 코드지연하려면 다음을 수행하십시오 .

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

좀 더 편리한 기능을 가진 프레임 워크 를 선호한다면 HandySwift 를 확인 하십시오 . Carthage통해 프로젝트에 추가 한 다음 위의 예와 동일하게 사용할 수 있습니다.

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}

이것은 지연 함수가 백그라운드 스레드에서 코드를 실행 한다는 것을 암시합니다 . // 지연된 코드 섹션 안에 UI 관련 코드를 넣으면 예제를 사용하는 사람이 충돌하는 앱을 디버깅하는 데 어려움을 겪을 수 있습니다 .
nalexn

기본적으로 내 방법은 기본 스레드를 사용하므로 발생하지 않아야합니다. dispatchLevel 기본값이 .Main?
Jeehut


4

신속한 3에서는 DispatchQueue.main.asyncAfter 함수를 사용하여 'n'초 지연 후 함수 또는 작업을 트리거 할 수 있습니다. 여기 코드에서는 1 초 후에 지연을 설정했습니다. 이 함수 본문 내에서 1 초 지연 후 트리거되는 함수를 호출합니다.

let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {

    // Trigger the function/action after the delay of 1Sec

}

4

Xcode 10.2 및 Swift 5 이상

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})

1

자신의 클래스에서 인수를 랩핑하거나 기본 유형으로 전달할 필요가없는 메소드에서 메소드 호출을 랩핑 할 수 있습니다. 그런 다음 지연 후 해당 메소드를 호출하고 해당 메소드 내에서 수행하려는 선택기를 수행하십시오.


1

Swift에서 지연 후 블록을 트리거하는 방법은 다음과 같습니다.

runThisAfterDelay(seconds: 2) { () -> () in
    print("Prints this 2 seconds later in main queue")
}

/// EZSwiftExtensions
func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue(), after)
}

그것은 내 repo 의 표준 기능으로 포함되어 있습니다 .


1

스위프트 3 및 Xcode 8.3.2

이 코드는 당신을 도울 것입니다, 나도 설명을 추가합니다

// Create custom class, this will make your life easier
class CustomDelay {

    static let cd = CustomDelay()

    // This is your custom delay function
    func runAfterDelay(_ delay:Double, closure:@escaping ()->()) {
        let when = DispatchTime.now() + delay
        DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
    }
}


// here how to use it (Example 1)
class YourViewController: UIViewController {

    // example delay time 2 second
    let delayTime = 2.0

    override func viewDidLoad() {
        super.viewDidLoad()

        CustomDelay.cd.runAfterDelay(delayTime) {
            // This func will run after 2 second
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
            self.runFunc()
        }
    }

    // example function 1
    func runFunc() {
        // do your method 1 here
    }
}

// here how to use it (Example 2)
class YourSecondViewController: UIViewController {

    // let say you want to user run function shoot after 3 second they tap a button

    // Create a button (This is programatically, you can create with storyboard too)
    let shootButton: UIButton = {
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 15, y: 15, width: 40, height: 40) // Customize where do you want to put your button inside your ui
        button.setTitle("Shoot", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // create an action selector when user tap shoot button
        shootButton.addTarget(self, action: #selector(shoot), for: .touchUpInside)   
    }

    // example shoot function
    func shoot() {
        // example delay time 3 second then shoot
        let delayTime = 3.0

        // delay a shoot after 3 second
        CustomDelay.cd.runAfterDelay(delayTime) {
            // your shoot method here
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
        }
    }   
}

0

저자는 분수 시간 (지연)을 기다리는 방법을 묻지 않고 대신 선택기 (withObject :)의 인수로 스칼라를 전달하는 방법과 현대 목표 C에서 가장 빠른 방법은 다음과 같습니다.

[obj performSelector:...  withObject:@(0.123123123) afterDelay:10]

선택기는 매개 변수를 NSNumber로 변경하고 floatValue 또는 doubleValue와 같은 선택기를 사용하여 값을 검색해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.