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.
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.
Yanıtlar:
NSDate
ve timeIntervalSince*
yöntemler NSTimeInterval
milisaniyenin altında doğrulukla bir double olan a döndürür . NSTimeInterval
saniye 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 NSDate
ve NSDate Sınıf Referansı'ndaNSDate
bulunan sınıf belgelerine bakmanızı tavsiye ederim .
NSDate
ve mach_absolute_time()
yaklaşık 30ms seviyesinde. 27'ye karşı 29, 36'ya karşı 39, 43'e 45. NSDate
benim için kullanımı daha kolaydı ve sonuçlar umursamayacak kadar benzerdi.
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 .)
CACurrentMediaTime()
, dönüştüren mach_absolute_time()
doğrudan içine double
.
elapsedNano
tiptedir Nanoseconds
dü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 .
Lütfen kullanmayın NSDate
, CFAbsoluteTimeGetCurrent
ya da gettimeofday
geç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 NSTimeInterval
s 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
CACurrentMediaTime()
QuartzCore'dan?
@import Darwin;
yerine de kullanılabilir#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
Bir mutlak zaman döndüren double
değ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.
72.89674947369
diğer tüm değerler göz önüne alındığında , saniye değerinin oldukça nadir olacağını söylemeliyim . ;)
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] -----------------------------------------------------
mach_absolute_time()
bir ile mach_timebase_info_data
ve 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);
#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
NSDate
herhangi 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_time
Doğru geçen zamanı elde etmek için kullanın .
mach_absolute_time
cihazın yeniden başlatılmasından etkilenebilir. Bunun yerine sunucu saatini kullanın.
Ayrıca, NSNumber
CoreData'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];
}
Temel alınan işlevler mach_absolute_time
kı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, gettimeofday
tekdü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(¤tTime, &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ğildirCFAbsoluteTimeGetCurrent()
- monoton değil, geriye gidebilirCACurrentMediaTime()
- cihaz uykudayken geçmeyi durdururtimeSinceBoot()
- uyumaz, ancak monoton olmayabilirCLOCK_MONOTONIC
- uyumaz, monoton, iOS 10'dan beri desteklenirBunun 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
NSDate
herhangi 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_time
Doğru geçen zamanı elde etmek için kullanın .
NSDate kullanarak 1 Ocak 1970 tarihinden itibaren geçerli saati milisaniye cinsinden alabilirsiniz:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
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.
NSDate
herhangi 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_time
Doğru geçen zamanı elde etmek için kullanın .