-、NSTimer定时精度
在实现一个坚实系统剪贴板变化当程序中,发现使用NSTimer精度达不到要求,类似当问题可以在stackoverflow中找到,如下:
经过查询文档之后,timer当触发是在runloop当循环中检查是否已经到达触发条件,如果没有到达,就在下一次循环中继续检查。因此NSTimer当精度受制与Runloop的循环时间,并且收到线程调度的影响,文档中说明是50~100ms,对于一般的应用可以满足。
二、更精确的Timer
在stackoverflow上面搜索如何获得更精确的timer之后,发现下面这种方式能解决一般问题,但是精度还是大于200ms,但是比NSTimer更稳定一些,当程序退到后台之后,仍旧能正常触发。
解决办法:
//建立新线程
[NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil]; //make a new thread
//在线程中使用NSdate进行判断
- (void)timer {
@autoreleasepool {
NSDate *startDate = [NSDate date];
while (YES) {
usleep(10000);
if([[NSDate date] timeIntervalSinceDate:startDate] >= watch_interval)
{
startDate = [NSDate date];
objc_msgSend(self,@selector(watchLoop),nil);
}
}
}
}
这个方法仍然有弊端,可能是NSDate到更新间隔,导致200ms到间隔不能缩小。
三、更精确到定时器,如果想获得更精确到定时器,可以参考apple到这份文档,文档中不建议使用该定时器,除非特别需要。
High Precision Timers in iOS / OS X
链接:https://developer.apple.com/library/ios/technotes/tn2169/_index.html
四、监视定时器变化到代码
.h 文件
//
// PastboardWatcher.h
// PastboardWatcher
//
// Created by on 14-3-20.
// Copyright (c) 2014年 master. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol PastboardWatcherDelegate;
@interface PastboardWatcher : NSObject
+ (id)shanreInstance;
- (void)registerObsever:(id<PastboardWatcherDelegate>)delegate;
- (void)removeDelegate:(id<PastboardWatcherDelegate>)delegate;
@end
@protocol PastboardWatcherDelegate <NSObject>
- (void)generalPastboardDidChange;
@end
.m文件
//
// PastboardWatcher.m
// PastboardWatcher
//
// Created by on 14-3-20.
// Copyright (c) 2014年 master. All rights reserved.
//
#import <objc/message.h>
#import "PastboardWatcher.h"
static const NSTimeInterval watch_interval = 0.2;
@interface PastboardWatcher ()
@property (nonatomic, strong) NSMutableArray *obseverArray;
@property (nonatomic, assign) NSInteger changeCount;
@property (nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation PastboardWatcher
+ (id)shanreInstance
{
static PastboardWatcher *instance = nil;
static dispatch_once_t once_token;
dispatch_once(&once_token,^{
instance = [[PastboardWatcher alloc] init];
});
return instance;
}
- (id)init
{
if(self = [super init])
{
_obseverArray = [[NSMutableArray alloc] init];
_changeCount = [[NSPasteboard generalPasteboard] changeCount];
_lock = [[NSRecursiveLock alloc] init];
[NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil]; //make a new thread
}
return self;
}
- (void)registerObsever:(id<PastboardWatcherDelegate>)delegate
{
[_lock lock];
[_obseverArray addObject:delegate];
[_lock unlock];
}
- (void)removeDelegate:(id<PastboardWatcherDelegate>)delegate
{
[_lock lock];
NSInteger findIndex = [_obseverArray indexOfObject:delegate];
if(findIndex != NSNotFound) [_obseverArray removeObjectAtIndex:findIndex];
[_lock unlock];
}
- (void)timer {
@autoreleasepool {
NSDate *startDate = [NSDate date];
while (YES) {
usleep(10000);
if([[NSDate date] timeIntervalSinceDate:startDate] >= watch_interval)
{
startDate = [NSDate date];
objc_msgSend(self,@selector(watchLoop),nil);
}
}
}
}
- (void)watchLoop
{
NSInteger current_changeCount = [NSPasteboard generalPasteboard].changeCount;
if(current_changeCount != _changeCount)
{
_changeCount = current_changeCount;
objc_msgSend(self, @selector(notifyObseverChange),nil);
}
}
- (void)notifyObseverChange
{
[_lock lock];
NSArray *tempArray = [_obseverArray copy];
[_lock unlock];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (id<PastboardWatcherDelegate> delegate in tempArray) {
if([delegate respondsToSelector:@selector(generalPastboardDidChange)])
{
objc_msgSend(delegate, @selector(generalPastboardDidChange));
}
}
});
}
@end