Memperoleh waktu berlalu di Objective-C


153

Saya perlu waktu antara dua peristiwa, misalnya, penampilan UIView dan reaksi pertama pengguna.

Bagaimana saya bisa mencapainya di Objective-C?

Jawaban:


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

timeInterval adalah perbedaan antara mulai dan sekarang, dalam hitungan detik, dengan presisi sub-milidetik.


18
@NicolasZozol dapat Anda gunakan fabs(...)untuk mendapatkan nilai absolut float. Misalnya. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
So Over It

1
tampaknya nilai timeInterval negatif.
Jacky

jika Anda mendapatkan nilai negatif - kalikan dengan -1 dan Anda baik
PinkFloydRocks

10
Bergantung pada [NSDate date]dapat menyebabkan sulit untuk melacak bug, lihat jawaban ini untuk info lebih lanjut.
Senseful

@PinkFloydRocks - Apa? Bagaimana nilai negatif dapat terjadi secara sah?
Todd Lehman

228

Anda tidak boleh mengandalkan [NSDate date]untuk tujuan pengaturan waktu karena ini dapat membuat laporan yang berlebih atau kurang dilaporkan. Bahkan ada kasus di mana komputer Anda tampaknya akan melakukan perjalanan waktu karena waktu yang berlalu akan menjadi negatif! (Misalnya, jika jam bergerak mundur selama penghitungan waktu.)

Menurut Aria Haghighi dalam kuliah "Pengenalan Gerakan iOS Lanjutan" dari kursus Musim Dingin 2013 Stanford iOS (34:00), Anda harus menggunakan CACurrentMediaTime()jika Anda membutuhkan interval waktu yang akurat.

Tujuan-C:

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

Cepat:

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

Alasannya adalah [NSDate date]sinkronisasi di server, sehingga dapat menyebabkan "cegukan sinkronisasi waktu" yang dapat menyebabkan bug yang sangat sulit dilacak. CACurrentMediaTime(), di sisi lain, adalah waktu perangkat yang tidak berubah dengan sinkronisasi jaringan ini.

Anda akan perlu menambahkan pada kerangka QuartzCore untuk pengaturan target Anda.


17
Harap pilih ini. Meskipun saya pikir semua orang bisa setuju bahwa NSDate harus menjadi pilihan pertama Anda, saran ini menyelesaikan masalah saya. Ketika memanggil [NSDate date]dalam blok penyelesaian dalam blok pengiriman, saya mendapatkan waktu "saat ini" yang sama yang dilaporkan [NSDate date]segera sebelum eksekusi mencapai titik di mana blok saya dibuat. CACurrentMediaTime()memecahkan masalah ini.
n00neimp0rtant

Bagaimana kita menggunakan elapsedTime untuk tampilan seperti mm: ss?
CyberMew


12
Sadarilah bahwa CACurrentMediaTime()berhenti berdetak bila perangkat memasuki mode sleep. Jika Anda menguji dengan perangkat terputus dari komputer, kunci, lalu tunggu ~ 10 menit Anda akan menemukan bahwa CACurrentMediaTime()tidak sesuai dengan waktu jam dinding.
russbishop

tambah dan impor #import <QuartzCore / QuartzCore.h>
steveen zoleko

23

Gunakan timeIntervalSinceDatemetodenya

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalhanya a double, definisikan NSDateseperti ini:

typedef double NSTimeInterval;

15

Bagi siapa pun yang datang ke sini mencari implementasi getTickCount () untuk iOS, inilah milik saya setelah menyatukan berbagai sumber.

Sebelumnya saya memiliki bug dalam kode ini (saya dibagi dengan 10.00000 dulu) yang menyebabkan beberapa quantisation dari output pada iPhone 6 saya (mungkin ini bukan masalah pada iPhone 4 / etc atau saya hanya tidak pernah menyadarinya). Perhatikan bahwa dengan tidak melakukan pembagian itu terlebih dahulu, ada risiko overflow jika pembilang timebase cukup besar. Jika ada yang penasaran, ada tautan dengan informasi lebih lanjut di sini: https://stackoverflow.com/a/23378064/588476

Mengingat informasi itu, mungkin lebih aman menggunakan fungsi Apple CACurrentMediaTime!

Saya juga melakukan benchmark pada mach_timebase_infopanggilan dan membutuhkan sekitar 19ns pada iPhone 6 saya, jadi saya menghapus kode (bukan threadsafe) yang merupakan caching output dari panggilan itu.

#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;
}

Berhati-hatilah dengan potensi risiko melimpah tergantung pada output panggilan timebase. Saya curiga (tetapi tidak tahu) bahwa itu mungkin konstan untuk setiap model iPhone. di iPhone 6 saya itu 125/3.

Solusi yang digunakan CACurrentMediaTime()cukup sepele:

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

Adakah yang tahu mengapa ada cast redundant (void) pada panggilan mach_timebase_info?
Simon Tillson

@SimonTillson itu pertanyaan yang bagus - baik itu diperlukan untuk membungkam peringatan, atau saya menyalinnya dari tempat lain dan hanya tidak memperhatikan untuk menghapusnya. Saya tidak ingat.
Wayne Uroda

2
Ini bukan utas yang aman. mach_timebase_info()akan mengatur ulang pass-in mach_timebase_info_data_tke {0,0}sebelum menyetelnya ke nilai aktual, yang dapat menyebabkan pembagian dengan nol jika kode dipanggil dari banyak utas. Gunakan dispatch_once()sebagai gantinya (atau CACurrentMediaTimeyang hanya diubah waktu menjadi detik).
wonder.mice

Terima kasih @ wonder.mice, saya telah memperbarui jawaban saya (menemukan masalah mengenai quantisation juga, saya menyalahkan saya yang berusia 6 tahun lebih muda ...)
Wayne Uroda


4

gunakan fungsi timeIntervalSince1970 dari kelas NSDate seperti di bawah ini:

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

pada dasarnya, inilah yang saya gunakan untuk membandingkan perbedaan dalam detik antara 2 tanggal yang berbeda. periksa juga tautan ini di sini


0

Jawaban lainnya benar (dengan peringatan *). Saya menambahkan jawaban ini hanya untuk menunjukkan contoh penggunaan:

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

Di konsol debugger, Anda melihat sesuatu seperti ini:

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

* Peringatan: Seperti yang disebutkan sebelumnya, gunakan NSDateuntuk menghitung waktu yang telah berlalu hanya untuk tujuan kasual. Salah satu tujuan tersebut mungkin pengujian umum, profil mentah, di mana Anda hanya ingin gambaran kasar tentang berapa lama suatu metode dibutuhkan.

Risikonya adalah bahwa pengaturan waktu jam perangkat saat ini dapat berubah setiap saat karena sinkronisasi jam jaringan. Sehingga NSDatewaktu bisa melompat maju atau mundur kapan saja.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.