Development Tip

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

yourdevel 2020. 9. 29. 18:49
반응형

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


사용하는 것과 같이, 지연 후 기본 매개 변수 블록을 호출하는 방법이 performSelector:withObject:afterDelay:있지만, 같은 인수는 int/ double/ float?


나는 당신이 찾고 있다고 생각합니다 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


dispatch_after나중에 블록을 호출하는 데 사용할 수 있습니다 . 엑스 코드에서 입력을 시작 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);
});

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

여기에 이미지 설명 입력

Swift 업데이트 :

많은 찬성 투표를 통해이 답변을 업데이트했습니다.

내장 Xcode 코드 조각 라이브러리에는 언어 dispatch_afterobjective-c있습니다. .NET사용자 지정 코드 조각만들 수도 있습니다 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.

자세한 내용은 CreatingaCustomCodeSnippet을 참조하십시오 .

Swift 3 업데이트

이 코드를 코드 조각 라이브러리 영역에 끌어다 놓습니다.

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

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];

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

+ (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!");
}];

Swift의 경우 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)

여기 내 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);
}

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] 형식으로 변환 할 수 있습니다.


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: {

          })

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

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

다음 성가신 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
}

If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via Carthage then use it exactly like in the examples above:

import HandySwift    

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

There's a nice one in the BlocksKit framework.

BlocksKit

(and the class)

BBlocksKit.m


In swift 3, We can simply use DispatchQueue.main.asyncAfter function to trigger any function or action after the delay of 'n' seconds. Here in code we have set delay after 1 second. You call any function inside the body of this function which will trigger after the delay of 1 second.

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

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

}

You can either wrap the argument in your own class, or wrap the method call in a method that doesn't need to be passed in the primitive type. Then call that method after your delay, and within that method perform the selector you wish to perform.


Here is how you can trigger a block after a delay in 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)
}

Its included as a standard function in my repo.


Swift 3 & Xcode 8.3.2

This code will help you, i add an explanation too

// 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 :)
        }
    }   
}

I believe the author is not asking how to wait for a fractional time (delay), but instead how to pass a scalar as argument of the selector (withObject:) and the fastest way in modern objective C is:

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

your selector have to change its parameter to NSNumber, and retrieve the value using a selector like floatValue or doubleValue


Xcode 10.2 and Swift 5 and above

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

참고 URL : https://stackoverflow.com/questions/4139219/how-do-you-trigger-a-block-after-a-delay-like-performselectorwithobjectafter

반응형