Отримання часу в Objective-C


153

Мені потрібно пройти час між двома подіями, наприклад, появою UIView та першою реакцією користувача.

Як я можу досягти цього в Objective-C?

Відповіді:


267
NSDate *start = [NSDate date];
// do stuff...
NSTimeInterval timeInterval = [start timeIntervalSinceNow];

timeInterval - це різниця між початком і зараз, у секундах, з субмілісекундною точністю.


18
@NicolasZozol можна використовувати fabs(...)для отримання абсолютного значення поплавця. Напр. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
Так над цим

1
Здається, значення timeInterval від'ємне.
Джекі

якщо ви отримаєте від’ємне значення - помножте на -1 і ви добрі
PinkFloydRocks

10
Посилання на це [NSDate date]може призвести до важкого відстеження помилок, див. Цю відповідь для отримання додаткової інформації.
Розсудливий

@PinkFloydRocks - Що? Як негативно може виникнути негативне значення?
Тодд Леман

228

Не слід покладатися на [NSDate date]часові цілі, оскільки це може завищувати або недооцінювати минулий час. Існують навіть випадки, коли ваш комп'ютер, здавалося б, подорожує часом, оскільки минулий час буде негативним! (Наприклад, якщо годинник перемістився назад під час синхронізації.)

За словами Aria Haghighi у лекції "Розширене розпізнавання жестів iOS" курсу з курсу iOS 2013 в Stanford (34:00), вам слід скористатися, CACurrentMediaTime()якщо вам потрібен точний часовий проміжок.

Завдання-C:

#import <QuartzCore/QuartzCore.h>
CFTimeInterval startTime = CACurrentMediaTime();
// perform some action
CFTimeInterval elapsedTime = CACurrentMediaTime() - startTime;

Швидкий:

let startTime = CACurrentMediaTime()
// perform some action
let elapsedTime = CACurrentMediaTime() - startTime

Причина полягає в тому, що [NSDate date]синхронізується на сервері, тому це може призвести до "ікки синхронізації часу", що може призвести до дуже важких помилок, які відстежуються. CACurrentMediaTime()з іншого боку - це час роботи пристрою, який не змінюється за допомогою цих мережевих синхронізацій.

Вам потрібно буде додати в рамки QuartzCore настройки вашої мети.


17
Будь ласка, підтвердіть це. Хоча я думаю, що всі можуть погодитися, що NSDate має бути вашим першим варіантом, ця пропозиція вирішила мою проблему. Під час виклику [NSDate date]в блоці завершення в блоці відправлення я отримував той самий "поточний" час, про який повідомлялося [NSDate date]безпосередньо перед виконанням, потрапивши в точку, в якій були створені мої блоки. CACurrentMediaTime()вирішив це питання.
n00neimp0rtant

Як би ми використовували пройдений час для відображення, наприклад, мм: ss?
CyberMew


12
Зауважте, що CACurrentMediaTime()пристрій перестає галочувати, коли пристрій переходить у сон. Якщо ви протестуєте пристрій, відключений від комп'ютера, заблокуйте його, а потім почекайте ~ 10 хвилин, ви побачите, що CACurrentMediaTime()не йде в ногу з настінним годинником.
russbishop

додати та імпортувати #import <QuartzCore / QuartzCore.h>
steveen zoleko

23

Використовуйте timeIntervalSinceDateметод

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalце лише a double, визначте NSDateтак:

typedef double NSTimeInterval;

15

Для всіх, хто приходить сюди, шукаючи реалізацію getTickCount () для iOS, ось моя після зведення різних джерел.

Раніше я мав помилку в цьому коді (я поділив його на 1000000 спочатку), що спричиняло певну кількісну оцінку результатів на моєму iPhone 6 (можливо, це не було проблемою на iPhone 4 / тощо, або я просто ніколи цього не помічав). Зауважте, що, не виконуючи цей поділ спочатку, існує певний ризик переповнення, якщо чисельник часової бази досить великий. Якщо хтось цікавий, тут є посилання з набагато більшою інформацією: https://stackoverflow.com/a/23378064/588476

З огляду на цю інформацію, можливо, безпечніше використовувати функцію Apple CACurrentMediaTime!

Я також орієнтував mach_timebase_infoвиклик, і він займає приблизно 19ns на моєму iPhone 6, тому я видалив (не потік) код, який кешував вихід цього виклику.

#include <mach/mach.h>
#include <mach/mach_time.h>

uint64_t getTickCount(void)
{
    mach_timebase_info_data_t sTimebaseInfo;
    uint64_t machTime = mach_absolute_time();

    // Convert to milliseconds
    mach_timebase_info(&sTimebaseInfo);
    machTime *= sTimebaseInfo.numer;
    machTime /= sTimebaseInfo.denom;
    machTime /= 1000000; // convert from nanoseconds to milliseconds

    return machTime;
}

Будьте в курсі потенційного ризику переповнення залежно від виходу дзвінка тимчасової бази. Я підозрюю (але не знаю), що це може бути постійною для кожної моделі iPhone. на моєму iPhone 6 це було 125/3.

Рішення з використанням CACurrentMediaTime()досить тривіально:

uint64_t getTickCount(void)
{
    double ret = CACurrentMediaTime();
    return ret * 1000;
}

Хтось знає, чому на виклику mach_timebase_info виникає надлишковий (недійсний) виклик?
Саймон Тілсон

@SimonTillson - це гарне запитання - або потрібно було заглушити попередження, або я скопіював його з іншого місця та просто не помітив, щоб видалити його. Я не можу згадати.
Уейн Урода

2
Це не є безпечним для потоків. mach_timebase_info()скине передане mach_timebase_info_data_tщоб {0,0}перед установкою його фактичних значень, які можуть викликати розподіл на нуль , якщо код викликається з декількох потоків. Використовуйте dispatch_once()натомість (або CACurrentMediaTimeякий просто час перетворення, перетворений на секунди).
wonder.mice

Дякую @ wonder.mice, я оновив свою відповідь (знайшов питання і щодо кількісної оцінки, я звинувачую мене в 6-річному
віці


4

використовуйте функцію timeIntervalSince1970 класу NSDate, як показано нижче:

double start = [startDate timeIntervalSince1970];
double end = [endDate timeIntervalSince1970];
double difference = end - start;

в основному, це те, що я використовую для порівняння різниці в секундах між двома різними датами. також перевірте це посилання тут


0

Інші відповіді правильні (із застереженням *). Цю відповідь я додаю просто, щоб показати приклад використання:

- (void)getYourAffairsInOrder
{
    NSDate* methodStart = [NSDate date];  // Capture start time.

    // … Do some work …

    NSLog(@"DEBUG Method %s ran. Elapsed: %f seconds.", __func__, -([methodStart timeIntervalSinceNow]));  // Calculate and report elapsed time.
}

На консолі налагодження ви бачите щось подібне:

DEBUG Method '-[XMAppDelegate getYourAffairsInOrder]' ran. Elapsed: 0.033827 seconds.

* Caveat: Як уже згадували інші, використовуйте NSDate для обчислення минулого часу лише для випадкових цілей. Однією з таких цілей може бути звичайне тестування, непрофільне профілювання, де потрібно просто грубе уявлення про те, як довго триває метод.

Ризик полягає в тому, що поточний час роботи пристрою може змінитися в будь-який момент через синхронізацію мережевих годин. Тож NSDateчас у будь-який момент міг стрибати вперед чи назад.

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