Додавання затримки між виконанням двох наступних рядків


81

Мені потрібно додати затримку між виконанням двох рядків у (тій самій) функції. Чи є якісь сприятливі варіанти для цього?

Примітка: Для цього мені не потрібні дві різні функції, і затримка не повинна впливати на виконання інших функцій.

наприклад:

line 1: [executing first operation];

line 2: Delay                        /* I need to introduce delay here */

line 3: [executing second operation];

Будь-яка допомога помітна. Спасибі заздалегідь...

Відповіді:


215

Ви можете використовувати gcd для цього, не створюючи іншого методу

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  NSLog(@"Do some work");
});

Ви все одно повинні запитати себе, "чи справді мені потрібно додати затримку", оскільки це часто може ускладнити код і спричинити перегони


2
Правильно попереджати людей про це, але якщо ви кодуєте контролери, яким потрібно чекати зовнішнього пристрою з фіксованим часом обробки, це НЕ глупо.
user2161301

3
А як щодо затримки запиту HTTP під час запису в рядок пошуку? Фільтрування результатів під час набору тексту? Зачекавши одну секунду перед тим, як запитувати відфільтрований список, замість того, щоб запитувати сервер про кожен клавішу, яку натискає користувач, для мене це не здається нісенітницею.
Алехандро Іван

26

Ви можете використовувати NSThreadметод:

[NSThread sleepForTimeInterval: delay];

Однак, якщо ви зробите це в основному потоці, ви заблокуєте програму, тому робіть це лише у фоновому потоці.


або в Свіфт

NSThread.sleepForTimeInterval(delay)

у Swift 3

Thread.sleep(forTimeInterval: delay)

2
Я вважаю, що це мала бути прийнята відповідь відповідно до вимог запитання.
ACLima,

1
@ACLima: Вибачте, 'sleepForTimeInterval' will Спить потік протягом заданого інтервалу часу, що може вплинути на виконання одночасних функцій.
Кришна Радж Салім,

його другий чи пам'ятник?
Шива,

20

Цей рядок викликає селектор secondMethod через 3 секунди:

[self performSelector:@selector(secondMethod) withObject:nil afterDelay:3.0 ];

Використовуйте його під час другої операції з бажаною затримкою. Якщо у вас багато коду, помістіть його у свій власний метод і викличте цей метод за допомогою performSelector:. Це не буде блокувати інтерфейс, якsleep

Змінити: Якщо ви не хочете другого методу, ви можете додати категорію, щоб мати можливість використовувати блоки з performSelector:

@implementation NSObject (PerformBlockAfterDelay)

- (void)performBlock:(void (^)(void))block 
          afterDelay:(NSTimeInterval)delay
{
    block = [block copy];
    [self performSelector:@selector(fireBlockAfterDelay:) 
               withObject:block 
               afterDelay:delay];
}

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

@end

Або, можливо, навіть чистіше:

void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),
      dispatch_get_current_queue(), block);
}

6
ОП прямо заявляє, що вони не хочуть другого методу: S
Paul. 11.03.13

Він міг би додати категорію , щоб мати можливість використовувати блоки performSelector: stackoverflow.com/questions/4007023 / ...
Sunkas

@Sunkas: Дякую за відповідь. Але я вже згадував, що не хочу додавати другу функцію.
Кришна Радж Салім,

3
Використання цієї відповіді дасть вам досить чистий спосіб запуску коду після затримки без другого методу. [self performBlock:^{ your_code } afterDelay:0.1];
Sunkas

4
Якщо ОП не бажає додавати інший метод, вони повинні пояснити, чому, оскільки це дуже хороший спосіб використовувати інструменти, оскільки вони призначені для роботи. Методи погані? Чи платять вони (недійсним)?
tooluser

8

У мене є кілька покрокових ігор, де мені потрібен ШІ, щоб зробити паузу, перш ніж приймати свою чергу (і між кроками по черзі). Я впевнений, що є й інші, більш корисні ситуації, коли затримка є найкращим рішенням. Свіфт:

        let delay = 2.0 * Double(NSEC_PER_SEC) 
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) 
        dispatch_after(time, dispatch_get_main_queue()) { self.playerTapped(aiPlayView) }

Я щойно повернувся сюди, щоб перевірити, чи відрізняються дзвінки Objective-C (мені потрібно додати це ще до цього).


4

Якщо ви націлюєтесь на iOS 4.0+, ви можете зробити наступне:

[executing first operation];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [executing second operation];
});

1
Ви маєте на увазі iOS 4? Посилання на
головну

4

[перевірено 27 листопада 2020 р. та підтверджено як і раніше точним за допомогою Xcode 12.1]

Найзручніший спосіб у наші дні: Xcode надає для цього фрагмент коду, де вам просто потрібно ввести значення затримки та код, який ви хочете запустити після затримки.

  1. натисніть на +кнопку вгорі праворуч від Xcode.
  2. шукати after
  3. Він поверне лише 1 результат пошуку, який є бажаним фрагментом (див. Знімок екрана). Двічі клацніть на ньому, і все готово.

скріншот, що ілюструє, як отримати фрагмент із самого Xcode


1

Як писав @Sunkas, performSelector:withObject:afterDelay:це кулон dispatch_afterсаме тому, що він коротший і у вас нормальний objective-cсинтаксис. Якщо вам потрібно передати аргументи в блок, який ви хочете затримати, ви можете просто передати їх через параметр, withObjectі ви отримаєте їх у selectorвашому дзвінку:

[self performSelector:@selector(testStringMethod:) 
           withObject:@"Test Test" 
           afterDelay:0.5];

- (void)testStringMethod:(NSString *)string{
    NSLog(@"string  >>> %@", string);
}

Якщо ви все-таки хочете вибрати себе, якщо виконувати його в основному потоці або в поточному потоці, існують конкретні методи, які дозволяють це вказати. Документація яблук говорить про це:

Якщо ви хочете, щоб повідомлення виводилося з черги, коли цикл запуску перебуває в режимі, відмінному від режиму за замовчуванням, використовуйте замість цього метод performSelector: withObject: afterDelay: inModes: Якщо ви не впевнені, чи поточний потік є основним, можна скористатися методом performSelectorOnMainThread: withObject: waitUntilDone: або performSelectorOnMainThread: withObject: waitUntilDone: mode:, щоб гарантувати, що ваш селектор виконує основний потік. Щоб скасувати повідомлення в черзі, використовуйте cancelPreviousPerformRequestsWithTarget: або cancelPreviousPerformRequestsWithTarget: selector: object: method.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.