Existe-t-il un moyen d'appeler un bloc avec un paramètre primitif après un délai, comme en utilisant performSelector:withObject:afterDelay:
mais avec un argument comme int
/ double
/ float
?
Existe-t-il un moyen d'appeler un bloc avec un paramètre primitif après un délai, comme en utilisant performSelector:withObject:afterDelay:
mais avec un argument comme int
/ double
/ float
?
Réponses:
Je pense que vous cherchez dispatch_after()
. Il nécessite que votre bloc n'accepte aucun paramètre, mais vous pouvez simplement laisser le bloc capturer ces variables à partir de votre portée locale à la place.
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);
});
Plus: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
extrait est méchant. N'y a-t-il pas un moyen plus propre pour cela?
dispatch_get_current_queue()
renvoie toujours la file d'attente à partir de laquelle le code est exécuté. Ainsi, lorsque ce code est exécuté à partir du thread principal, le bloc sera également exécuté sur le thread principal.
dispatch_get_current_queue()
est désormais obsolète
Vous pouvez utiliser dispatch_after
pour appeler un bloc ultérieurement. Dans Xcode, commencez à taper dispatch_after
et appuyez sur Enter
pour compléter automatiquement les éléments suivants:
Voici un exemple avec deux flottants comme «arguments». Vous n'avez pas à vous fier à n'importe quel type de macro, et l'intention du code est assez claire:
let time1 = 8.23
let time2 = 3.42
// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Sum of times: \(time1 + time2)")
}
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)")
}
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);
});
NSEC_PER_SEC * 0.5
fonctionnerait de la même manière que NSEC_PER_MSEC * 500
. Bien que vous ayez raison de noter qu'il dispatch_time
attend un entier 64 bits, la valeur attendue est en nanosecondes. NSEC_PER_SEC
est défini comme 1000000000ull
, et multiplier cela avec une constante à 0.5
virgule flottante effectuerait implicitement une arithmétique à virgule flottante, produisant 500000000.0
, avant qu'elle ne soit explicitement convertie en un entier 64 bits. Il est donc parfaitement acceptable d'utiliser une fraction de NSEC_PER_SEC
.
Que diriez-vous d'utiliser la bibliothèque d'extraits de code Xcode intégrée?
Mise à jour pour Swift:
Beaucoup de votes positifs m'ont inspiré pour mettre à jour cette réponse.
La bibliothèque d'extraits de code Xcode intégrée a dispatch_after
pour seule objective-c
langue. Les utilisateurs peuvent également créer leur propre extrait de code personnalisé pour Swift
.
Écrivez ceci dans 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#>
})
Faites glisser ce code et déposez-le dans la zone de bibliothèque d'extraits de code.
En bas de la liste d'extraits de code, il y aura une nouvelle entité nommée My Code Snippet
. Modifiez ceci pour un titre. Pour des suggestions en tapant le Xcode, remplissez le Completion Shortcut
.
Pour plus d'informations, voir Création d'un SnapCodeCustomCode .
Faites glisser ce code et déposez-le dans la zone de bibliothèque d'extraits de code.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
En développant la réponse de Jaime Cham, j'ai créé une catégorie NSObject + Blocks comme ci-dessous. Je pensais que ces méthodes correspondaient mieux aux performSelector:
méthodes NSObject existantes
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
et utiliser comme ça:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
devrait être de NSTimeInterval
(qui est un double
). #import <UIKit/UIKit.h>
n'est pas nécessaire. Et, je ne vois pas pourquoi cela - (void)performBlock:(void (^)())block;
pourrait être utile, donc peut être supprimé de l'en-tête.
Peut-être plus simple que de passer par GCD, dans une classe quelque part (par exemple "Util"), ou une catégorie sur un objet:
+ (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];
}
Donc à utiliser:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Pour Swift, j'ai créé une fonction globale, rien de spécial, en utilisant la dispatch_after
méthode. J'aime plus cela car il est lisible et facile à utiliser:
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)
}
Que vous pouvez utiliser comme suit:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Ensuite, vous pouvez écrire:after(2.0){ print("do somthing") }
Voici mes 2 cents = 5 méthodes;)
J'aime encapsuler ces détails et demander à AppCode de me dire comment terminer mes phrases.
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 prend toujours un objet, donc pour passer des arguments comme int / double / float etc ..... Vous pouvez utiliser quelque chose comme ça.
// NSNumber est un objet ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
De la même manière, vous pouvez utiliser [NSNumber numberWithInt:] etc .... et dans la méthode de réception, vous pouvez convertir le nombre dans votre format en tant que [nombre entier] ou [numéro double].
La fonction dispatch_after distribue un objet bloc dans une file d'attente de répartition après un laps de temps donné. Utilisez le code ci-dessous pour effectuer certaines tâches liées à l'interface utilisateur après 2,0 secondes.
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")
})
Dans swift 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,
au lieu demainQueue)
Voici une aide pratique pour éviter de faire des appels GCD ennuyeux encore et encore:
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)
}
}
}
Maintenant, vous retardez simplement votre code sur le fil principal comme ceci:
delay(bySeconds: 1.5) {
// delayed code
}
Si vous souhaitez retarder votre code sur un thread différent :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Si vous préférez un Framework qui possède également des fonctionnalités plus pratiques, passez à HandySwift . Vous pouvez l'ajouter à votre projet via Carthage puis l'utiliser exactement comme dans les exemples ci-dessus:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Dans swift 3, nous pouvons simplement utiliser la fonction DispatchQueue.main.asyncAfter pour déclencher toute fonction ou action après le délai de 'n' secondes. Ici, dans le code, nous avons défini le délai après 1 seconde. Vous appelez n'importe quelle fonction à l'intérieur du corps de cette fonction qui se déclenchera après le délai de 1 seconde.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Vous pouvez soit encapsuler l'argument dans votre propre classe, soit encapsuler l'appel de méthode dans une méthode qui n'a pas besoin d'être passée dans le type primitif. Appelez ensuite cette méthode après votre délai, et dans cette méthode, effectuez le sélecteur que vous souhaitez effectuer.
Voici comment déclencher un blocage après un retard dans 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)
}
Il est inclus en tant que fonction standard dans mon référentiel .
Swift 3 et Xcode 8.3.2
Ce code vous aidera, j'ajoute aussi une explication
// 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 :)
}
}
}
Je crois que l'auteur ne demande pas comment attendre un temps fractionnaire (retard), mais plutôt comment passer un scalaire comme argument du sélecteur (withObject :) et le moyen le plus rapide dans l'objectif moderne C est:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
votre sélecteur doit changer son paramètre en NSNumber et récupérer la valeur en utilisant un sélecteur comme floatValue ou doubleValue