Objective-Cでミリ秒単位などの正確な時刻を取得するにはどうすればよいですか?


99

時間を正確に取得する簡単な方法はありますか?

メソッド呼び出し間の遅延を計算する必要があります。具体的には、UIScrollViewでのスクロール速度を計算したいと思います。


ここに、非常に関連する質問があります。これも、ここでの回答を理解するのに役立ちます。.見てください!
abbood


回答:


127

NSDateそして、timeIntervalSince*メソッドはNSTimeIntervalサブミリ秒の精度でdoubleであるa を返します。NSTimeInterval秒単位ですが、精度を上げるためにdoubleを使用しています。

ミリ秒の時間精度を計算するには、次のようにします。

// 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に関するドキュメント

そこ使用して、この間隔を計算するための他の多くの方法がありますがNSDate、私はのためのクラスのドキュメントを見て推薦NSDateで発見されました。NSDateクラスリファレンスに参照ます。


3
実際、これは一般的なユースケースでは十分正確です。
ローガンコートレル2012

4
NSDatesを使用して経過時間を計算しても安全ですか?外部の時刻ソースと同期すると、システム時刻が前後する可能性があるため、結果を信頼できないように思えます。あなたは本当に単調に増加する時計を望んでいますね?
クリストファージョンソン

1
私はちょうど30msレベルで比較NSDatemach_absolute_time()ました。27対29、36対39、43対45 NSDateは私にとって使いやすく、結果は気にしないほど十分に似ていました。
ネヴァンキング2013

7
NSDateを使用して経過時間を比較するのは安全ではありません。システムクロックはいつでも(NTP、DSTの移行、うるう秒、およびその他の多くの理由により)変更される可能性があるためです。代わりにmach_absolute_timeを使用してください。
2015年

@nevynあなたは私のインターネット速度テストを保存しました、ty!こんにちは、貼り付けコードをコピーする前にコメントを読んでよかったです。
アルバートレンショー

41

mach_absolute_time() 正確な測定値を取得するために使用できます。

http://developer.apple.com/qa/qa2004/qa1398.htmlを参照してください

も利用できますCACurrentMediaTime()。これは基本的に同じものですが、使いやすいインターフェースを備えています。

(注:この回答は2009年に書かれたものです。clock_gettime()新しいバージョンのmacOSおよびiOSで利用できるより単純なPOSIX インターフェイスについては、Pavel Alexeevの回答を参照してください。)


1
そのコードは奇妙な変換です-最初の例の最後の行は「return *(uint64_t *)&elapsedNano;」です。「return(uint64_t)elapsedNano」だけではないのはなぜですか?
タイラー、

8
Core Animation(QuartzCore.framework)は、直接ににCACurrentMediaTime()変換mach_absolute_time()する便利なメソッドも提供しますdouble
オットー2013

1
@Tyler elapsedNanoはタイプNanosecondsであり、単純な整数タイプではありません。これUnsignedWideは、2つの32ビット整数フィールドを持つ構造体であるのエイリアスです。UnsignedWideToUInt64()キャストの代わりに使用することもできます。
Ken Thomases 2013

CoreServicesはMacライブラリのみです。iOSで同等のものはありますか?
mm24 2016

1
@ mm24 iOSでは、Core AnimationにあるCACurrentMediaTime()を使用します。
クリストファージョンソン2016

28

使用しないでくださいNSDateCFAbsoluteTimeGetCurrentまたはgettimeofday経過時間を計測します。これらはすべてシステムクロックに依存します。システムクロックは、ネットワークタイム同期(NTP)によるクロックの更新(頻繁にドリフトの調整が行われる)、DST調整、うるう秒など、さまざまな理由によりいつでも変更できます。

これは、ダウンロードまたはアップロードの速度を測定している場合、実際に何が起こったかとは関係のない、急激な数値の急上昇または低下が生じることを意味します。パフォーマンステストには、奇妙な不正確な外れ値が含まれます。手動タイマーは、不適切な期間の後にトリガーされます。時間は逆戻りする可能性があり、最終的に負のデルタが発生し、無限再帰またはデッドコードが発生する可能性があります(そうです、私はこれらの両方を実行しました)。

を使用しmach_absolute_timeます。カーネルがブートされてからの実際の秒数を測定します。単調に増加し(後戻りすることはありません)、日付と時刻の設定の影響を受けません。作業が面倒なので、ここにNSTimeIntervals を提供する単純なラッパーを示します。

// 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
ありがとう。何についてCACurrentMediaTime()QuartzCoreから?
・クール

1
注:1も使用することができます@import Darwin;代わりに#include <mach/mach_time.h>
・クール

@CœurCACurrentMediaTimeは、実際には私のコードとまったく同じです。良い発見!(ヘッダーは「これはmach_absolute_time()を呼び出して単位を秒に変換した結果である」と言います)
nevyn

「ネットワーク時刻同期(NTP)によるクロックの更新(頻繁にドリフトの調整が行われる)、DST調整、うるう秒など、さまざまな理由によりいつでも変更される可能性があります。」-それ以外の理由は何ですか?インターネットに接続していないときに約50ミリ秒の逆方向ジャンプを観測しています。だから、どうやら、これらの理由はどれも当てはまるはずがない...
Falko

@Falkoはわかりませんが、推測しなければならない場合、異なるハードウェアクロックが同期していないことに気付いた場合、OSが独自にドリフト補正を行うことを期待しますか?
nevyn 2017

14

CFAbsoluteTimeGetCurrent()リターンとして絶対時間double値を、私はその精度があるかわからない-それは可能性があるだけで、すべての十ミリ秒を更新するか、それがすべてのマイクロ秒を更新する可能性があります、私は知りません。


2
ただし、これは倍精度の浮動小数点値であり、ミリ秒未満の精度を提供します。72.89674947369秒という値は珍しいことではありません...
Jim Dovey、

25
@JimDovey:72.89674947369他のすべての値を考慮すると、秒の値はかなり珍しいと言わざるを得ません。;)
FreeAsInBeer 2012

3
@ジム:ミリ秒未満の精度を提供するという引用がありますか(これは正直な質問です)?ここにいる全員が精度と精度の違いを理解してくれることを願っています。
アダムローゼンフィールド

5
CFAbsoluteTimeGetCurrent()は、OS Xではgettimeofday()を呼び出し、WindowsではGetSystemTimeAsFileTime()を呼び出します。これがソースコードです。
ジムドベイ

3
そして、gettimeofday()は、mach_absolute_time()を介してマッハのナノ秒タイマーを使用して実装されています。Darwin / ARMでのgettimeofday()の共通ページ実装のソースは
Jim Dovey

10

mach_absolute_time()ティックを使用して絶対時間(おそらく稼働時間)をカーネルとプロセッサの組み合わせに問い合わせるため、私は使用しません。

私が使うもの:

CFAbsoluteTimeGetCurrent();

この機能は、iOSとOSXのソフトウェアとハ​​ードウェアの違いを修正するために最適化されています。

なんかゲキエ

違いの商mach_absolute_time()とは、AFAbsoluteTimeGetCurrent()常に周り24000011.154871です

これが私のアプリのログです:

最終結果の時間がの差であることをしてくださいノートCFAbsoluteTimeGetCurrent()さん

 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()することにmach_timebase_info_dataなり、その後使用しました(long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));。マッハタイムベースを取得するには、行うことができます(void)mach_timebase_info(&your_timebase);
ネイトSymer

12
CFAbsoluteTimeGetCurrent()のドキュメントによると、「システム時刻は、外部の時刻参照との同期のため、または明示的なユーザーによる時計の変更のために減少する可能性があります。」このようなものを使用して、経過時間を逆算できる場合、なぜ経過時間を測定するのか理解できません。
クリストファージョンソン

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

使用法:

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

出力:

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

NSDateいつでも変更できるシステムクロックに依存し、誤った時間間隔または負の時間間隔になる可能性があります。mach_absolute_time代わりに、正しい経過時間を取得するために使用してください。
・クール

mach_absolute_timeデバイスの再起動によって影響を受ける可能性があります。代わりにサーバー時間を使用してください。
kelin

5

また、NSNumberCoreDataに保存する場合に備えて、Unixエポックで初期化された64ビットをミリ秒単位で計算する方法もここにあります。この方法で日付を保存するシステムと対話する私のアプリにこれが必要でした。

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

3

に基づく関数mach_absolute_timeは、短い測定に適しています。
ただし、長時間の測定では、デバイスがスリープ状態のときにカチカチ音が止まるという重要な注意事項があります。

起動から時間を取得する機能があります。寝ている間に止まりません。また、gettimeofday単調ではありませんが、私の実験では、システム時刻が変わるとブート時刻が変わることが常にあるので、問題なく動作するはずです。

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
}

また、iOS 10およびmacOS 10.12以降では、CLOCK_MONOTONICを使用できます。

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

要約すると:

  • Date.timeIntervalSinceReferenceDate —システム時刻が変化すると変化し、単調ではありません
  • CFAbsoluteTimeGetCurrent() —単調ではなく、後退する可能性があります
  • CACurrentMediaTime() —デバイスがスリープしているときにカチカチと止まる
  • timeSinceBoot() —眠りませんが、単調ではないかもしれません
  • CLOCK_MONOTONIC —スリープしない、単調、iOS 10以降でサポート

1

これは古いものであることは知っていますが、自分が再びそれを通り過ぎていることに気付いたので、ここに自分のオプションを提出したいと思いました。

最善の策は、これに関する私のブログ投稿をチェックすることです: Objective-Cでのタイミングのタイミング:ストップウォッチ

基本的に、私は非常に基本的な方法で監視を停止するクラスを作成しましたが、カプセル化されているため、次のことだけを実行する必要があります。

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

そしてあなたは次のようになります:

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

ログに...

もう一度、もう少し私の投稿をチェックするか、ここからダウンロードしてください: MMStopwatch.zip


実装は推奨されません。NSDateいつでも変更できるシステムクロックに依存し、誤った時間間隔または負の時間間隔になる可能性があります。mach_absolute_time代わりに、正しい経過時間を取得するために使用してください。
・クール

1

NSDateを使用すると、1970年1月1日からの現在の時間をミリ秒単位で取得できます。

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

これはミリ秒単位の時間を提供しますが、それでも秒単位の精度です
dev

-1

それらには、@ Jeff Thompsonの回答のSwiftバージョンが必要です。

// 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

これがお役に立てば幸いです。


2
NSDateいつでも変更できるシステムクロックに依存し、誤った時間間隔または負の時間間隔になる可能性があります。mach_absolute_time代わりに、正しい経過時間を取得するために使用してください。
・クール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.