-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.