2012-05-23 35 views
5

Tôi đang tìm cách tạo đồng hồ đếm ngược cho SMPTE Timecode (HH: MM: SS: FF) trên iOS. Về cơ bản, nó chỉ là một đồng hồ đếm ngược với độ phân giải 33.33333ms. Tôi không chắc NSTimer có đủ chính xác để tính các sự kiện để tạo bộ hẹn giờ này không. Tôi muốn kích hoạt sự kiện hoặc gọi một đoạn mã mỗi lần tăng/giảm bộ đếm thời gian này.Làm cách nào để tạo sự kiện hẹn giờ chính xác trong Objective-C/iOS?

Tôi mới tham gia Objective-C nên tôi đang tìm kiếm sự khôn ngoan từ cộng đồng. Ai đó đã đề xuất lớp học CADisplayLink, tìm kiếm một số lời khuyên của chuyên gia.

Trả lời

12

Hãy thử CADisplayLink. Nó bắn ở tốc độ làm mới (60 khung hình/giây).

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerFired:)]; 
displayLink.frameInterval = 2; 
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

Điều này sẽ kích hoạt mỗi 2 khung hình, tức là 30 lần mỗi giây, có vẻ như sau bạn là gì.

Lưu ý rằng điều này liên quan đến xử lý khung video, vì vậy bạn cần thực hiện công việc của mình trong gọi lại rất nhanh.

+0

Lưu ý rằng _frameInterval_ không được chấp nhận trong iOS 10.0, sử dụng _preferredFramesPerSecond_, như sau: 'displayLink.preferredFramesPerSecond = 30.0;' cho 30fps – WongWray

1

Nếu bạn đang nhắm mục tiêu iOS 4+, bạn có thể sử dụng Grand Central Dispatch:

// Set the time, '33333333' nanoseconds in the future (33.333333ms) 
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333); 
// Schedule our code to run 
dispatch_after(time, dispatch_get_main_queue(), ^{ 
    // your code to run here... 
}); 

này sẽ gọi mã mà sau 33.333333ms. Nếu được này sẽ là một thỏa thuận vòng sorta, bạn có thể muốn sử dụng dispatch_after_f chức năng thay vì sử dụng một con trỏ hàm thay vì một khối:

void DoWork(void *context); 

void ScheduleWork() { 
    // Set the time, '33333333' nanoseconds in the future (33.333333ms) 
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333); 
    // Schedule our 'DoWork' function to run 
    // Here I pass in NULL for the 'context', whatever you set that to will 
    // get passed to the DoWork function 
    dispatch_after_f(time, dispatch_get_main_queue(), NULL, &DoWork); 
} 

void DoWork(void *context) { 
    // ... 
    // Do your work here, updating an on screen counter or something 
    // ... 

    // Schedule our DoWork function again, maybe add an if statement 
    // so it eventually stops 
    ScheduleWork(); 
} 

Và sau đó chỉ cần gọi ScheduleWork(); khi bạn muốn bắt đầu hẹn giờ. Đối với một vòng lặp lặp đi lặp lại, cá nhân tôi nghĩ rằng đây là một chút sạch hơn so với phương pháp chặn ở trên, nhưng đối với một nhiệm vụ một thời gian tôi chắc chắn thích phương pháp chặn.

Xem Grand Central Dispatch docs để biết thêm thông tin.

+1

Tôi không nghĩ 'dispatch_after' sẽ chính xác hơn' NSTimer'. Trong thực tế, trong iOS 4 tôi khá chắc chắn sau này được thực hiện trong điều khoản của cựu. – benzado

+0

Tôi sẽ nghĩ rằng lập kế hoạch một chủ đề mới mỗi lần sẽ áp đặt một số không chính xác nhưng tôi sẽ thừa nhận tôi đã không thực sự thử nghiệm nó. IOS có thể có một số chi phí thấp, đáng thử. –

4

Về cơ bản, bạn không có bảo đảm nào với NSTimer hoặc dispatch_after; họ lên lịch mã để kích hoạt trên luồng chính, nhưng nếu cái gì khác mất một thời gian dài để thực thi và chặn luồng chính, bộ hẹn giờ của bạn sẽ không kích hoạt.

Điều đó nói rằng, bạn có thể dễ dàng tránh việc chặn luồng chính (chỉ sử dụng I/O không đồng bộ) và mọi thứ sẽ khá tốt.

Bạn không nói chính xác những gì bạn cần làm trong mã bộ hẹn giờ, nhưng nếu tất cả những gì bạn cần làm là hiển thị đếm ngược, bạn sẽ ổn khi bạn tính thời gian SMPTE dựa trên thời gian hệ thống, và không phải số giây bạn cho là đã trôi qua dựa trên khoảng thời gian của bạn. Nếu bạn làm điều đó, bạn gần như chắc chắn sẽ trôi dạt và không đồng bộ với thời gian thực tế. Thay vào đó, hãy lưu ý thời gian bắt đầu của bạn và sau đó thực hiện tất cả toán học dựa trên điều đó:

// Setup 
timerStartDate = [[NSDate alloc] init]; 
[NSTimer scheduledTimer... 

- (void)timerDidFire:(NSTimer *)timer 
{ 
    NSTImeInterval elapsed = [timerStartDate timeIntervalSinceNow]; 
    NSString *smtpeCode = [self formatSMTPEFromMilliseconds:elapsed]; 
    self.label.text = smtpeCode; 
} 

Bây giờ bạn sẽ hiển thị mã thời gian chính xác bất kể tần suất được kích hoạt. (Nếu bộ hẹn giờ không kích hoạt thường xuyên, bộ hẹn giờ sẽ không cập nhật, nhưng khi nó cập nhật nó sẽ chính xác. Nó sẽ không bao giờ bị đồng bộ.)

Nếu bạn sử dụng CADisplayLink, phương pháp của bạn sẽ được gọi nhanh như cập nhật hiển thị. Nói cách khác, nhanh như nó sẽ hữu ích, nhưng không nhanh hơn. Nếu bạn đang hiển thị thời gian, đó có thể là cách để đi.

+0

Cảm ơn! Bởi vì tôi sẽ sử dụng điều này làm cơ sở thời gian để giải nén các khung hình video, tôi không thể sử dụng NSTimer để hiển thị đồng hồ một cách đáng tin cậy. Nhiều khả năng CADisplayLink là con đường để đi, và tôi sẽ khóa nó với tốc độ tối đa (33ms) để nó rút ra khung không quá 33ms ngoài nhưng ít hơn nếu phải mất quá nhiều thời gian để giải nén. –

+0

Tôi đã sử dụng phương pháp này với dispatch_after và nó hoạt động tốt cho mục đích của tôi. Cảm ơn! –

Các vấn đề liên quan