Örneğin Objective-C'de milisaniye cinsinden kesin bir zamanı nasıl elde edebilirim?


99

Kesinlikle zaman geçirmenin kolay bir yolu var mı?

Yöntem çağrıları arasındaki bazı gecikmeleri hesaplamam gerekiyor. Daha spesifik olarak, bir UIScrollView'da kaydırma hızını hesaplamak istiyorum.


buradaki cevapları anlamanıza yardımcı olabilecek çok ilgili bir soru burada .. lütfen bir göz atın!
abbood


Yanıtlar:


127

NSDateve timeIntervalSince*yöntemler NSTimeIntervalmilisaniyenin altında doğrulukla bir double olan a döndürür . NSTimeIntervalsaniye cinsindendir, ancak size daha fazla hassasiyet sağlamak için iki katını kullanır.

Milisaniye zaman doğruluğunu hesaplamak için şunları yapabilirsiniz:

// Get a current time for where you want to start measuring from
NSDate *date = [NSDate date];

// do work...

// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
double timePassed_ms = [date timeIntervalSinceNow] * -1000.0;

TimeIntervalSinceNow ile ilgili belgeler .

Bu aralığı kullanarak hesaplamanın birçok başka yolu vardır NSDateve NSDate Sınıf Referansı'ndaNSDate bulunan sınıf belgelerine bakmanızı tavsiye ederim .


3
Aslında bu, genel kullanım durumu için yeterince kesin.
logancautrell

4
Geçen zamanı hesaplamak için NSDates kullanmak güvenli midir? Bana öyle geliyor ki, sistem zamanı harici zaman kaynaklarıyla senkronize edilirken ileri veya geri gidebilir, bu yüzden sonuca güvenemezsiniz. Gerçekten monoton olarak artan bir saat istiyorsun, değil mi?
Kristopher Johnson

1
Sadece karşılaştırdım NSDateve mach_absolute_time()yaklaşık 30ms seviyesinde. 27'ye karşı 29, 36'ya karşı 39, 43'e 45. NSDatebenim için kullanımı daha kolaydı ve sonuçlar umursamayacak kadar benzerdi.
nevan king

7
Sistem saati herhangi bir zamanda değişebileceğinden (NTP, DST geçişleri, artık saniyeler ve diğer birçok nedenden dolayı) geçen zamanı karşılaştırmak için NSDate kullanmak güvenli değildir. Bunun yerine mach_absolute_time kullanın.
nevyn

@nevyn az önce internet hız testimi kurtardın, ty! Kahretsin, kodu yapıştırmadan önce yorumları okuduğuma sevindim heh
Albert Renshaw

41

mach_absolute_time() hassas ölçümler elde etmek için kullanılabilir.

Bkz. Http://developer.apple.com/qa/qa2004/qa1398.html

Ayrıca CACurrentMediaTime(), aslında aynı şey olan ancak kullanımı daha kolay bir arayüze sahip olan da mevcuttur.

(Not: Bu cevap 2009'da yazılmıştır clock_gettime(). MacOS ve iOS'un daha yeni sürümlerinde bulunan daha basit POSIX arayüzleri için Pavel Alexeev'in cevabına bakın .)


1
Bu kodda garip bir dönüşüm var - ilk örneğin son satırı "return * (uint64_t *) & elapsedNano;" neden sadece "return (uint64_t) elapsedNano" değil?
Tyler

8
Çekirdek Canlandırma (QuartzCore.framework) de kolaylık yöntem sağlar CACurrentMediaTime(), dönüştüren mach_absolute_time()doğrudan içine double.
otto

1
@Tyler, elapsedNanotiptedir Nanosecondsdüz bir tamsayı türü olmadığı. Bu UnsignedWide, iki 32 bitlik tam sayı alanı olan bir yapı olan bir takma adıdır . İsterseniz UnsignedWideToUInt64()alçı yerine kullanabilirsiniz .
Ken Thomases

CoreServices yalnızca bir Mac kitaplığıdır. İOS'ta bir eşdeğeri var mı?
mm24

1
@ mm24 iOS'ta, Core Animation'da bulunan CACurrentMediaTime () kullanın.
Kristopher Johnson

28

Lütfen kullanmayın NSDate, CFAbsoluteTimeGetCurrentya da gettimeofdaygeçen süreyi ölçmek için. Bunların hepsi , ağ zaman senkronizasyonu (NTP) saatin güncellenmesi (genellikle kaymayı ayarlamak için olur), DST ayarlamaları, artık saniyeler vb. Gibi birçok farklı nedenden dolayı herhangi bir zamanda değişebilen sistem saatine bağlıdır .

Bu, indirme veya yükleme hızınızı ölçüyorsanız, sayılarınızda gerçekte ne olduğu ile bağlantılı olmayan ani artışlar veya düşüşler olacağı anlamına gelir; performans testlerinizde garip, yanlış aykırı değerler olacaktır; ve manuel zamanlayıcılarınız yanlış sürelerden sonra tetiklenecektir. Zaman bile geriye gidebilir ve sonunda negatif deltalarla sonuçlanırsınız ve sonunda sonsuz özyineleme veya ölü kodla sonuçlanabilir (evet, ikisini de yaptım).

Kullanın mach_absolute_time. Çekirdeğin başlatılmasından bu yana gerçek saniyeleri ölçer. Monoton olarak artıyor (asla geriye gitmeyecek) ve tarih ve saat ayarlarından etkilenmiyor. Üzerinde çalışılması zor olduğundan, işte size NSTimeIntervals veren basit bir paketleyici :

// LBClock.h
@interface LBClock : NSObject
+ (instancetype)sharedClock;
// since device boot or something. Monotonically increasing, unaffected by date and time settings
- (NSTimeInterval)absoluteTime;

- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute;
@end

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

@implementation LBClock
{
    mach_timebase_info_data_t _clock_timebase;
}

+ (instancetype)sharedClock
{
    static LBClock *g;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        g = [LBClock new];
    });
    return g;
}

- (id)init
{
    if(!(self = [super init]))
        return nil;
    mach_timebase_info(&_clock_timebase);
    return self;
}

- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute
{
    uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom;

    return nanos/1.0e9;
}

- (NSTimeInterval)absoluteTime
{
    uint64_t machtime = mach_absolute_time();
    return [self machAbsoluteToTimeInterval:machtime];
}
@end

2
Teşekkürler. Peki ya CACurrentMediaTime()QuartzCore'dan?
Cœur

1
Not: @import Darwin;yerine de kullanılabilir#include <mach/mach_time.h>
Cœur

@ CACurrentMediaTime aslında benim koduma tam olarak eşdeğer olmalıdır. İyi bul! (Üstbilgi "Bu, mach_absolute_time () 'ı çağırmanın ve birimleri saniyeye dönüştürmenin sonucudur" diyor)
nevyn

"Ağ zaman senkronizasyonu (NTP) saatin güncellenmesi (genellikle kaymaya göre ayarlanır), DST ayarlamaları, artık saniyeler vb. gibi birçok farklı nedenden dolayı herhangi bir zamanda değişebilir." - Bu başka ne sebep olabilir? İnternet bağlantısı yokken yaklaşık 50 ms'lik geri sıçramalar gözlemliyorum. Öyleyse, görünüşe göre, bu nedenlerin hiçbiri geçerli olmamalı ...
Falko

@Falko emin değilim, ancak tahmin etmem gerekirse, farklı donanım saatlerinin senkronize olmadığını fark ederse işletim sisteminin kendi başına drift telafisi yapacağına bahse girerim?
nevyn

14

CFAbsoluteTimeGetCurrent()Bir mutlak zaman döndüren doubledeğer, ama ben onun hassas olduğunu bilmiyorum - bu olabilir sadece her düzine milisaniyede güncellemek ya da her mikrosaniye, ben bilmiyor musun güncelleyebilirsiniz.


2
Yine de çift hassasiyetli bir kayan nokta değeridir ve milisaniyenin altında doğruluk sağlar. 72.89674947369 saniyelik bir değer nadir değildir ...
Jim Dovey

25
@JimDovey: Olabileceği 72.89674947369diğer tüm değerler göz önüne alındığında , saniye değerinin oldukça nadir olacağını söylemeliyim . ;)
FreeAsInBeer

3
@Jim: Milisaniyenin altında bir doğruluk sağladığına dair bir alıntı var mı (bu dürüst bir soru)? Umarım buradaki herkes doğruluk ve kesinlik arasındaki farkları anlar .
Adam Rosenfield

5
CFAbsoluteTimeGetCurrent (), OS X'te gettimeofday () ve Windows'ta GetSystemTimeAsFileTime () çağırır. İşte kaynak kodu .
Jim Dovey

3
Ve gettimeofday (), mach_absolute_time () aracılığıyla Mach nanosaniye zamanlayıcısı kullanılarak gerçeklenir ; Darwin / ARM üzerinde gettimeofday () 'ın ortak sayfa uygulaması için kaynak: opensource.apple.com/source/Libc/Libc-763.12/arm/sys/…
Jim Dovey

10

Ben KULLANMAYIN ediyorum mach_absolute_time()o çekirdek ve kene kullanarak mutlak süre işlemci (muhtemelen kesintisiz çalışma) bir arada sorgular nedeniyle.

Ne kullanırdım:

CFAbsoluteTimeGetCurrent();

Bu işlev, iOS ve OSX yazılım ve donanımındaki farkı düzeltmek için optimize edilmiştir.

Bir şey Geekier

Farkının bölüm mach_absolute_time()ve AFAbsoluteTimeGetCurrent()her zaman etrafında 24000011.154871 olduğunu

İşte uygulamamın bir günlüğü:

Lütfen nihai sonuç süresinin CFAbsoluteTimeGetCurrent()'s

 2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040
 2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177
 2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137
 2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372
 2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295
 2012-03-19 21:46:36.367 Rest Counter[3776:707] -----------------------------------------------------
 2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637
 2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256
 2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619
 2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833
 2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777
 2012-03-19 21:46:43.177 Rest Counter[3776:707] -----------------------------------------------------
 2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199
 2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985
 2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786
 2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836
 2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500
 2012-03-19 21:46:55.024 Rest Counter[3776:707] -----------------------------------------------------

Ben kullanarak sona erdi mach_absolute_time()bir ile mach_timebase_info_datave daha sonra yaptığımız (long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));. Makine zaman (void)mach_timebase_info(&your_timebase);
tabanını

12
CFAbsoluteTimeGetCurrent () belgelerine göre, "Sistem zamanı, harici zaman referanslarıyla senkronizasyon nedeniyle veya saatin açık bir kullanıcı değişikliği nedeniyle azalabilir." Geriye doğru gidebiliyorsa, geçen zamanı ölçmek için neden böyle bir şey kullanmak isteyeceğini anlamıyorum.
Kristopher Johnson

6
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)

Kullanım:

CTTimeStart();
...
CTTimeEnd(@"that was a long time:");

Çıktı:

2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023

NSDateherhangi bir zamanda değiştirilebilen, potansiyel olarak yanlış veya hatta negatif zaman aralıklarına neden olan sistem saatine bağlıdır. mach_absolute_timeDoğru geçen zamanı elde etmek için kullanın .
Coeur

mach_absolute_timecihazın yeniden başlatılmasından etkilenebilir. Bunun yerine sunucu saatini kullanın.
kelin

5

Ayrıca, NSNumberCoreData'da bu şekilde saklamak istediğinizde, Unix epoch ile başlatılmış bir 64-bitin milisaniye cinsinden nasıl hesaplanacağı burada . Tarihleri ​​bu şekilde depolayan bir sistemle etkileşime giren uygulamam için buna ihtiyacım vardı.

  + (NSNumber*) longUnixEpoch {
      return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
  }

3

Temel alınan işlevler mach_absolute_timekısa ölçümler için iyidir.
Ancak uzun ölçümler için önemli bir uyarı, cihaz uyurken durmalarıdır.

Önyüklemeden bu yana zaman almak için bir işlev var. Uyurken durmuyor. Ayrıca, gettimeofdaytekdüze değildir, ancak deneylerimde sistem zamanı değiştiğinde önyükleme süresinin değiştiğini her zaman gördüm, bu yüzden iyi çalışması gerektiğini düşünüyorum.

func timeSinceBoot() -> TimeInterval
{
    var bootTime = timeval()
    var currentTime = timeval()
    var timeZone = timezone()

    let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
    mib[0] = CTL_KERN
    mib[1] = KERN_BOOTTIME
    var size = MemoryLayout.size(ofValue: bootTime)

    var timeSinceBoot = 0.0

    gettimeofday(&currentTime, &timeZone)

    if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
        timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec)
        timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0
    }
    return timeSinceBoot
}

Ve iOS 10 ve macOS 10.12'den beri CLOCK_MONOTONIC kullanabiliriz:

if #available(OSX 10.12, *) {
    var uptime = timespec()
    if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 {
        return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0
    }
}

Özetlersek:

  • Date.timeIntervalSinceReferenceDate - sistem zamanı değiştiğinde değişir, monoton değildir
  • CFAbsoluteTimeGetCurrent() - monoton değil, geriye gidebilir
  • CACurrentMediaTime() - cihaz uykudayken geçmeyi durdurur
  • timeSinceBoot() - uyumaz, ancak monoton olmayabilir
  • CLOCK_MONOTONIC - uyumaz, monoton, iOS 10'dan beri desteklenir

1

Bunun eski olduğunu biliyorum ama kendimi yine geçerken buldum, bu yüzden burada kendi seçeneğimi sunacağımı düşündüm.

En iyi bahis, bu konudaki blog yazıma göz atmaktır : Objective-C'deki şeyleri zamanlama: Bir kronometre

Temel olarak, çok basit bir şekilde izlemeyi bırakan ancak özetlenmiş bir ders yazdım, böylece yalnızca aşağıdakileri yapmanız gerekir:

[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];

Ve sonunda:

MyApp[4090:15203]  -> Stopwatch: [My Timer] runtime: [0.029]

günlükte ...

Yine, biraz daha fazlası için gönderime bakın veya buradan indirin: MMStopwatch.zip


Uygulamanız tavsiye edilmez. NSDateherhangi bir zamanda değiştirilebilen, potansiyel olarak yanlış veya hatta negatif zaman aralıklarına neden olan sistem saatine bağlıdır. mach_absolute_timeDoğru geçen zamanı elde etmek için kullanın .
Novur

1

NSDate kullanarak 1 Ocak 1970 tarihinden itibaren geçerli saati milisaniye cinsinden alabilirsiniz:

- (double)currentTimeInMilliseconds {
    NSDate *date = [NSDate date];
    return [date timeIntervalSince1970]*1000;
}

Bu size milisaniye cinsinden zaman verir, ancak yine de saniye hassasiyetinde
dev

-1

Bunlar için @ Jeff Thompson'ın cevabının Swift versiyonuna ihtiyacımız var:

// Get a current time for where you want to start measuring from
var date = NSDate()

// do work...

// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0

Umarım bu sana yardımcı olur.


2
NSDateherhangi bir zamanda değiştirilebilen, potansiyel olarak yanlış veya hatta negatif zaman aralıklarına neden olan sistem saatine bağlıdır. mach_absolute_timeDoğru geçen zamanı elde etmek için kullanın .
Coeur
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.