เวลาผ่านไปใน 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 เป็นค่าลบ
Jacky

ถ้าคุณได้รับค่าลบ - คูณกับ -1 และคุณก็ดี
PinkFloydRocks

10
การใช้งาน[NSDate date]อาจนำไปสู่การติดตามข้อผิดพลาดได้ยากดูคำตอบนี้สำหรับข้อมูลเพิ่มเติม
เหตุผล

@PinkFloydRocks - อะไรนะ คุณค่าเชิงลบจะเกิดขึ้นอย่างถูกกฎหมายได้อย่างไร?
ทอดด์เลห์แมน

228

คุณไม่ควรใช้[NSDate date]เพื่อจุดประสงค์ในการจับเวลาเนื่องจากอาจมีเวลามากหรือน้อยเกินไปในการรายงานเวลาที่ผ่านไป มีบางครั้งที่คอมพิวเตอร์ของคุณอาจต้องเดินทางข้ามเวลาเนื่องจากเวลาที่ผ่านไปจะเป็นค่าลบ! (เช่นถ้านาฬิกาเดินถอยหลังในช่วงเวลา)

ตาม Aria Haghighi ในการบรรยาย "Advanced iOS Gesture Recognition" ของหลักสูตร Winter 2013 Stanford iOS (34:00) คุณควรใช้CACurrentMediaTime()หากคุณต้องการช่วงเวลาที่แม่นยำ

Objective-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]ซิงค์บนเซิร์ฟเวอร์ดังนั้นจึงอาจนำไปสู่ ​​"hiccups การซิงค์เวลา" ซึ่งอาจทำให้เกิดข้อบกพร่องที่ติดตามได้ยากมาก CACurrentMediaTime()ในทางกลับกันเวลาของอุปกรณ์ที่ไม่เปลี่ยนแปลงด้วยการซิงค์เครือข่ายเหล่านี้

คุณจะต้องเพิ่มกรอบ QuartzCoreการตั้งค่าเป้าหมายของคุณ


17
โปรดโหวตสิ่งนี้ แม้ว่าฉันคิดว่าทุกคนสามารถยอมรับว่า NSDate ควรเป็นตัวเลือกแรกของคุณคำแนะนำนี้แก้ไขปัญหาของฉันได้ เมื่อโทร[NSDate date]ภายในบล็อกที่เสร็จสมบูรณ์ภายในบล็อกการจัดส่งฉันได้รับเวลา "ปัจจุบัน" ที่เหมือนกันซึ่งถูกรายงานโดย[NSDate date]ทันทีก่อนที่การดำเนินการจะเข้าสู่จุดที่บล็อกของฉันถูกสร้าง CACurrentMediaTime()แก้ไขปัญหานี้
n00neimp0rtant

เราจะใช้ elapsedTime เพื่อแสดงเช่น mm: ss อย่างไร
CyberMew

@CyberMew: นั่นเป็นปัญหาแยกต่างหาก ดูฉันจะแยก NSTimeInterval ออกเป็นปีเดือนวันชั่วโมงนาทีและวินาทีบน iPhone ได้อย่างไร สำหรับการแก้ปัญหาบางอย่าง
มีความหมาย

12
ระวังที่CACurrentMediaTime()จะหยุดฟ้องเมื่ออุปกรณ์เข้าสู่โหมดสลีป หากคุณทดสอบว่าอุปกรณ์ถูกตัดการเชื่อมต่อจากคอมพิวเตอร์ให้ล็อกแล้วรอประมาณ 10 นาทีคุณจะพบว่าไม่ได้CACurrentMediaTime()ตามเวลานาฬิกาปลุก
russbishop

เพิ่มและนำเข้า #import <QuartzCore / QuartzCore.h>
steveen zoleko

23

ใช้timeIntervalSinceDateวิธีการ

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalเป็นเพียง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 ของฉันดังนั้นฉันจึงลบรหัส (ไม่ใช่ threadsafe) ซึ่งแคชการแสดงผลของการโทรนั้น

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

มีใครรู้บ้างไหมว่าทำไมมีคาสต์ (void) ซ้ำซ้อนในการโทร mach_timebase_info
Simon Tillson

@SimonTillson เป็นคำถามที่ดี - ไม่ว่าจะเป็นการเงียบคำเตือนหรือฉันคัดลอกมาจากที่อื่นและไม่ได้สังเกตเห็นว่าจะลบมัน ฉันจำไม่ได้
Wayne Uroda

2
นี่ไม่ปลอดภัยเธรด mach_timebase_info()จะรีเซ็ตรหัสผ่านmach_timebase_info_data_tเป็น{0,0}ก่อนที่จะตั้งค่าเป็นค่าจริงซึ่งอาจทำให้เกิดการหารด้วยศูนย์หากรหัสถูกเรียกจากหลายเธรด ใช้dispatch_once()แทน (หรือCACurrentMediaTimeซึ่งเป็นเพียงเวลา mach ปรับเป็นวินาที)
wonder.mice

ขอบคุณ @ wonder.mice ฉันได้อัปเดตคำตอบของฉันแล้ว (พบปัญหาเกี่ยวกับการวัดเช่นกันฉันโทษฉันอายุน้อยกว่า 6 ปี ... )
Wayne Uroda


4

ใช้ฟังก์ชัน timeIntervalSince1970 ของคลาส NSDate ดังนี้:

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

นี่คือสิ่งที่ฉันใช้เพื่อเปรียบเทียบความแตกต่างในไม่กี่วินาทีระหว่าง 2 วันที่แตกต่างกัน ตรวจสอบลิงค์นี้ที่นี่


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เวลาสามารถกระโดดไปข้างหน้าหรือข้างหลังในเวลาใดก็ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.