Bắt thời gian trôi qua trong Mục tiêu-C


153

Tôi cần phải có thời gian trôi qua giữa hai sự kiện, ví dụ, sự xuất hiện của một UIView và phản ứng đầu tiên của người dùng.

Làm thế nào tôi có thể đạt được nó trong Objective-C?

Câu trả lời:


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

timeInterval là sự khác biệt giữa bắt đầu và bây giờ, tính bằng giây, với độ chính xác dưới một phần nghìn giây.


18
@NicolasZozol bạn có thể sử dụng fabs(...)để có được giá trị tuyệt đối của một số float. Ví dụ. NSTimeInterval timeInterval = fabs([start timeIntervalSinceNow]);
Vậy là hết

1
có vẻ như giá trị của timeInterval là âm.
Jacky

nếu bạn nhận được giá trị âm - nhân với -1 và bạn tốt
PinkFloydRocks

10
Dựa vào [NSDate date]có thể dẫn đến khó theo dõi lỗi, xem câu trả lời này để biết thêm.
Nhận thức

@PinkFloydRocks - Cái gì? Làm thế nào một giá trị tiêu cực bao giờ xảy ra hợp pháp?
Todd Lehman

228

Bạn không nên dựa vào [NSDate date]mục đích thời gian vì nó có thể báo cáo quá mức hoặc quá thời gian đã trôi qua. Thậm chí có những trường hợp máy tính của bạn dường như du hành thời gian vì thời gian trôi qua sẽ âm tính! (Ví dụ: nếu đồng hồ di chuyển ngược trong thời gian.)

Theo Aria Haghighi trong bài giảng "Nhận dạng cử chỉ iOS nâng cao" của khóa học Stanford mùa đông 2013 Stanford (34:00), bạn nên sử dụng CACurrentMediaTime()nếu bạn cần một khoảng thời gian chính xác.

Mục tiêu-C:

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

Nhanh:

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

Lý do là [NSDate date]đồng bộ hóa trên máy chủ, do đó, nó có thể dẫn đến "trục trặc đồng bộ hóa thời gian" có thể dẫn đến các lỗi rất khó theo dõi. CACurrentMediaTime()mặt khác, là thời gian thiết bị không thay đổi với các đồng bộ mạng này.

Bạn sẽ cần phải thêm các khuôn khổ QuartzCore tới cài đặt của mục tiêu của bạn.


17
Xin hãy ủng hộ điều này. Mặc dù tôi nghĩ mọi người có thể đồng ý rằng NSDate nên là lựa chọn đầu tiên của bạn, nhưng đề xuất này đã giải quyết vấn đề của tôi. Khi gọi [NSDate date]trong một khối hoàn thành trong một khối công văn, tôi đã nhận được cùng thời gian "hiện tại" đã được báo cáo [NSDate date]ngay lập tức trước khi thực hiện đạt đến điểm mà các khối của tôi được tạo. CACurrentMediaTime()đã giải quyết vấn đề này
n00neimp0rtant

Làm thế nào chúng ta sẽ sử dụng thời gian trôi qua để hiển thị như mm: ss?
CyberMew

@CyberMew: Đó là một vấn đề riêng biệt. Xem Làm cách nào để chia NSTimeInterval thành năm, tháng, ngày, giờ, phút và giây trên iPhone? cho một số giải pháp.
Nhận thức

12
Lưu ý rằng CACurrentMediaTime()dừng đánh dấu khi thiết bị đi vào giấc ngủ. Nếu bạn kiểm tra thiết bị bị ngắt kết nối với máy tính, hãy khóa thiết bị, sau đó đợi ~ 10 phút bạn sẽ thấy rằng CACurrentMediaTime()không theo kịp thời gian của đồng hồ treo tường.
russbishop

thêm và nhập #import <QuartzCore / QuartzCore.h>
steveen zoleko

23

Sử dụng timeIntervalSinceDatephương pháp

NSTimeInterval secondsElapsed = [secondDate timeIntervalSinceDate:firstDate];

NSTimeIntervalchỉ là một double, định nghĩa NSDatenhư thế này:

typedef double NSTimeInterval;

15

Đối với bất kỳ ai đến đây đang tìm kiếm một triển khai getTickCount () cho iOS, đây là của tôi sau khi kết hợp nhiều nguồn khác nhau.

Trước đây tôi có một lỗi trong mã này (tôi chia cho 1000000 trước), điều này gây ra một số lượng hóa đầu ra trên iPhone 6 của tôi (có lẽ đây không phải là vấn đề trên iPhone 4 / etc hoặc tôi chỉ không bao giờ nhận thấy nó). Lưu ý rằng bằng cách không thực hiện phép chia đó trước, sẽ có một số rủi ro tràn nếu tử số của cơ sở thời gian là khá lớn. Nếu có ai tò mò, có một liên kết với nhiều thông tin hơn ở đây: https://stackoverflow.com/a/23378064/588476

Trước thông tin đó, có thể an toàn hơn khi sử dụng chức năng của Apple CACurrentMediaTime!

Tôi cũng đã điểm chuẩn mach_timebase_infocuộc gọi và mất khoảng 19ns trên iPhone 6 của mình, vì vậy tôi đã xóa mã (không phải luồng an toàn) đang lưu trữ đầu ra của cuộc gọi đó.

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

Hãy lưu ý về nguy cơ tràn tràn tùy thuộc vào đầu ra của lệnh gọi cơ sở thời gian. Tôi nghi ngờ (nhưng không biết) rằng nó có thể là một hằng số cho mỗi mẫu iPhone. trên iPhone 6 của tôi 125/3.

Giải pháp sử dụng CACurrentMediaTime()khá tầm thường:

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

Bất cứ ai cũng biết tại sao có một lệnh thừa (void) trong cuộc gọi mach_timebase_info?
Simon Tillson

@SimonTillson đó là một câu hỏi hay - hoặc là bắt buộc phải tắt tiếng cảnh báo hoặc tôi đã sao chép nó từ một nơi khác và chỉ không thông báo để xóa nó. Tôi không thể nhớ.
Wayne Uroda

2
Đây không phải là chủ đề an toàn. mach_timebase_info()sẽ thiết lập lại thông qua trong mach_timebase_info_data_tđể {0,0}trước khi đặt nó vào giá trị thực tế, có thể gây ra phép chia cho không nếu mã được gọi là từ nhiều chủ đề. Sử dụng dispatch_once()thay thế (hoặc CACurrentMediaTimechỉ là thời gian mach được chuyển đổi thành giây).
wonder.mice

Cảm ơn @ wonder.mice, tôi đã cập nhật câu trả lời của mình (cũng tìm thấy một vấn đề liên quan đến lượng hóa, tôi đổ lỗi cho tôi 6 tuổi ...)
Wayne Uroda


4

sử dụng hàm timeIntervalSince1970 của lớp NSDate như dưới đây:

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

về cơ bản, đây là những gì tôi sử dụng để so sánh sự khác biệt tính bằng giây giữa 2 ngày khác nhau. cũng kiểm tra liên kết này ở đây


0

Các câu trả lời khác là chính xác (với một cảnh báo *). Tôi thêm câu trả lời này chỉ đơn giản để hiển thị một ví dụ sử dụng:

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

Trên bảng điều khiển trình gỡ lỗi, bạn thấy một cái gì đó như thế này:

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

* Hãy cẩn thận: Như những người khác đã đề cập, chỉ sử dụng NSDateđể tính thời gian trôi qua cho các mục đích thông thường. Một mục đích như vậy có thể là thử nghiệm phổ biến, hồ sơ thô, trong đó bạn chỉ muốn một ý tưởng sơ bộ về thời gian thực hiện một phương pháp.

Rủi ro là cài đặt thời gian hiện tại của đồng hồ có thể thay đổi bất cứ lúc nào do đồng bộ hóa đồng hồ mạng. Vì vậy, NSDatethời gian có thể nhảy về phía trước hoặc lùi lại bất cứ lúc nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.