Three types of timers in OC: Cadisplaylink, Nstimer, GCD
Let's take a look at Cadiskplaylink, go inside the header file, and use a note to illustrate the following.
@interface CADisplayLink: NSObject
{
@private
void * _impl; // pointer
}
+ (CADisplayLink *) displayLinkWithTarget: (id) target selector: (SEL) sel;
// The only initialization method
-(void) addToRunLoop: (NSRunLoop *) runloop forMode: (NSRunLoopMode) mode;
// Add the created instance to RunLoop
-(void) removeFromRunLoop: (NSRunLoop *) runloop forMode: (NSRunLoopMode) mode;
// Remove from RunLoop
-(void) invalidate;
// Destroy the instance
@property (readonly, nonatomic) CFTimeInterval timestamp; // The last time the Selector was called, read-only
@property (readonly, nonatomic) CFTimeInterval duration; // screen refresh interval, the current iOS refresh frequency is 60HZ, so the refresh interval is 16.7ms
@property (readonly, nonatomic) CFTimeInterval targetTimestamp CA_AVAILABLE_IOS_STARTING (10.0, 10.0, 3.0);
// The next time it is called
@property (getter = isPaused, nonatomic) BOOL paused; // When set to YES, the event trigger will be paused
@property (nonatomic) NSInteger frameInterval
CA_AVAILABLE_BUT_DEPRECATED_IOS (3.1, 10.0, 9.0, 10.0, 2.0, 3.0, "use preferredFramesPerSecond");
// Event trigger interval. It refers to the screen refresh interval between two selector triggers. The default value is 1, which means that the selector is executed every time the screen is refreshed. This can also be used to control the animation speed
@property (nonatomic) NSInteger preferredFramesPerSecond CA_AVAILABLE_IOS_STARTING (10.0, 10.0, 3.0);
// How many frames are displayed per second
@end
From the beginning of the document to see the use of Cadisplaylink is quite simple, the following code:
-(void) viewDidLoad {
[super viewDidLoad];
self.displayLink = [CADisplayLink displayLinkWithTarget: self
selector: @selector (logCount)];
self.displayLink.frameInterval = 2; // Call the selector 2 times when the screen is refreshed
[self.displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
}
-(void) logCount {
self.count ++;
NSLog (@ "Count =% ld", self.count);
if (self.count> 99) {
self.count = 0;
[self.displayLink invalidate]; // Direct destruction
}
}
The code is simple enough to not explain.
It is important to note that Cadisplaylink must be added to Runloop that can be executed, and the timer will pause if the runloop is paused or if the Runloop model changes after adding to a runloop.
For example, when we add a timer to TableView to the current Runloop Nsdefaultrunloopmode model, the timer can be called normally when half of the screen is displayed, but when we slide the tableview with our hands, the timer pauses.
Because when sliding, the Runloop will enter into the Uitrackingrunloopmode
So when we find that the timer is not running, we can check to see if there is a correct mode in the
So let's take a look at some of the Runloop mode:
Definition:NSDefaultRunLoopMode(COCOA) Kcfrunloopdefaultmode (Core Foundation)
Description: The default mode contains almost all input sources (except nsconnection), which should be used in general.
Definition: Nsconnectionreplymode (COCOA)
Description: Handling Nsconnection Object related events, internal use of the system, the user is basically not used.
Definition: Nsmodalpanelrunloopmode (COCOA)
Description: Handles the modal panels event.
Definition:UITrackingRunLoopMode(IOS)
Nseventtrackingrunloopmode (Cocoa)
Description: In this mode when dragging loops or other user interface tracking loops, the handling of input events is restricted in this mode. For example, you will be in this mode when you drag the finger while holding down uitableview.
Definition:NSRunLoopCommonModes(COCOA) kcfrunloopcommonmodes (Core Foundation)
Description: This is a pseudo-pattern, which is a set of run loop modes, and adding the input source to this mode means that all modes contained in the common modes can be processed. In the cocoa application, common modes contains default Modes,modal modes,event Tracking modes. You can use the Cfrunloopaddcommonmode method to common Add custom modes in modes.
Note: Only three types of mode are available in iOS nsdefaultrunloopmode,uitrackingrunloopmode,nsrunloopcommonmodes.
Cadisplaylink
Basic usage has just been introduced.
Advantage: Depending on the device screen refresh frequency trigger event, so its trigger time is the most accurate. Also最适合做UI不断刷新的事件, the transition is relatively smooth, without a sense of Kaka.
Disadvantages:
- Depending on the screen refresh rate, if the CPU is overwhelmed and the screen refresh is affected, our triggering event will also be affected accordingly.
- The selector trigger interval can only be an integer multiple of duration.
- Selector events can cause dropped frames if they are larger than their trigger interval.
- Cadisplaylink cannot be inherited.
-------------------I'm a split line---------------------
Let's talk about Nstimer, just like we're just seeing files and commenting on them.
@interface NSTimer: NSObject
+ (NSTimer *) timerWithTimeInterval: (NSTimeInterval) ti invocation: (NSInvocation *) invocation repeats: (BOOL) yesOrNo;
// Instantiation method, NSInvocation for response event, need to be manually added to RunLoop to take effect
+ (NSTimer *) scheduledTimerWithTimeInterval: (NSTimeInterval) ti invocation: (NSInvocation *) invocation repeats: (BOOL) yesOrNo;
// Instantiation method, NSIvocation for responding to events, the system automatically adds the timer to currentRunLoop for you, defaultMode
+ (NSTimer *) timerWithTimeInterval: (NSTimeInterval) ti target: (id) aTarget selector: (SEL) aSelector userInfo: (nullable id) userInfo repeats: (BOOL) yesOrNo;
// Instantiation method, PerformanceSelector for responding to events, userInfo can be used to pass parameters, you need to manually add to RunLoop
+ (NSTimer *) scheduledTimerWithTimeInterval: (NSTimeInterval) ti target: (id) aTarget selector: (SEL) aSelector userInfo: (nullable id) userInfo repeats: (BOOL) yesOrNo;
// Instantiation method, PerformanceSelector for responding to events, userInfo can be used to pass parameters, the system will automatically help you add timer to currentRunLoop, defaultMode
+ (NSTimer *) timerWithTimeInterval: (NSTimeInterval) interval repeats: (BOOL) repeats block: (void (^) (NSTimer * timer)) block API_AVAILABLE (macosx (10.12), ios (10.0), watchos (3.0), tvos ( 10.0));
// Instantiation method, passing in the content to be executed in a block manner, which needs to be manually added to RunLoop
+ (NSTimer *) scheduledTimerWithTimeInterval: (NSTimeInterval) interval repeats: (BOOL) repeats block: (void (^) (NSTimer * timer)) block API_AVAILABLE (macosx (10.12), ios (10.0), watchos (3.0), tvos ( 10.0));
// Instantiate method, pass in the content to be executed in block mode, the system will automatically help you add timer to currentRunLoop, defaultMode
-(instancetype) initWithFireDate: (NSDate *) date interval: (NSTimeInterval) interval repeats: (BOOL) repeats block: (void (^) (NSTimer * timer)) block API_AVAILABLE (macosx (10.12), ios (10.0), watchos (3.0), tvos (10.0));
// Similar to the above, except that an additional start time is specified
-(instancetype) initWithFireDate: (NSDate *) date interval: (NSTimeInterval) ti target: (id) t selector: (SEL) s userInfo: (nullable id) ui repeats: (BOOL) rep NS_DESIGNATED_INITIALIZER;
// Similar to the above, except that an additional start time is specified
-(void) fire; // Execute the timer method immediately, note that the timer is not turned on immediately
@property (copy) NSDate * fireDate; // Trigger event of the current event, generally used to pause and resume
@property (readonly) NSTimeInterval timeInterval; // Read-only property, get the trigger interval of the current timer
@property NSTimeInterval tolerance NS_AVAILABLE (10_9, 7_0); // Allowable error value
-(void) invalidate; // Destroy the timer immediately
@property (readonly, getter = isValid) BOOL valid; // Read-only property to get whether the current timer is valid
@property (nullable, readonly, retain) id userInfo; // Read-only property, user parameters passed in during initialization
@end
The content of NSTimer is relatively more but also more flexible. One thing to note is that the instantiation methods at the beginning of the timer need to be manually added to RunLoop, and the ones at the beginning of Schedule will be added to RunLoop by the system for you
fireDate, set the trigger time of the event of the current timer. Usually we use this property to pause and resume the timer.
/// pause the timer
self.timer.fireDate = [NSDate distantFuture];
/// Resume timer
self.timer.fireDate = [NSDate distantPast];
tolerance, the tolerance time. We know that the triggering event of the NSTimer event is inaccurate and depends entirely on the current runloop processing time. If the current runloop is processing a complex operation, the timer execution time will be postponed until the trigger event is executed immediately after the complex operation ends, and then executed at the rhythm set initially. When the tolerance is set, a delay within the allowable range can trigger the event, and the delay is not triggered. Default is 1/10 of the time interval
Many people on the Internet's interpretation of the fire method is actually incorrect. Fire does not activate the timer immediately, but executes the timer method immediately.
When added to a runloop, the timer does not need to be activated to trigger events at a set time. fire is equivalent to manually letting the timer trigger an event.
If the repeat set by the timer is NO, the timer will be destroyed immediately after fire.
If the timer's repeat is YES, he will still trigger the event step by step when the time set before.
Fire only triggers an event separately, and does not affect the rhythm of the original timer.
About the invalid method
We know that if NSTimer is used without paying attention, it will cause a memory leak. The reason is that when we generate the instance, we retain the controller. If it is not managed, VC's never reference count will be zero, which will cause a memory leak.
So when we don't need the timer, please do as follows:
[self.timer invalid];
self.timer = nil;
In this way, Timer will release a VC. So don't forget to call the invalid method.
By the way, if the repeat is NO when the timer instance is generated, then the system will automatically call invalid once after the trigger event ends.
Advantages of NSTimer: relatively flexible and widely used
Disadvantages: seriously affected by runloop, and easy to cause memory leaks (call the invalid method to solve)
---------------------------- I am the dividing line -----------------------------------------
Let's talk about the GCD timer: dispatch_source_t
In fact, dispatch_source_t says that the timer is not completely correct. It is actually a source object that GCD uses for us.
Or first directly on the code:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, strong) dispatch_source_t tTimer; // GCD timer must be set as a member variable, otherwise it will be released immediately
@end
@implementation ViewController
@synthesize tTimer;
-(void) viewDidLoad {
[super viewDidLoad];
// Create a GCD timer resource. The first parameter is the source type, and the second parameter is the queue to which the resource will be added.
self.tTimer = dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue ());
// Set the timer information. The first parameter is our timer object, the second is the timer's first trigger delay time, the third parameter is the trigger time interval, and the last is the delay value allowed by the timer trigger. The recommended value is ten. One part
dispatch_source_set_timer (self.tTimer,
dispatch_walltime (NULL, 0 * NSEC_PER_SEC),
0.32 * NSEC_PER_SEC,
0);
// Set the timer trigger event
dispatch_source_set_event_handler (self.tTimer, ^ {
[self logCount];
});
// Activate the timer object
dispatch_resume (self.tTimer);
}
-(void) logCount {
self.count ++;
NSLog (@ "Count =% ld", self.count);
if (self.count> 99) {
self.count = 0;
// Pause the timer object
dispatch_suspend (self.tTimer);
// Destroy the timer. Note that suspended timer resources cannot be directly destroyed. You need to resume and cancel, otherwise it will cause a memory leak.
//dispatch_source_cancel(self.tTimer);
}
}
The comments are very clear, so I will not explain them one by one (the above code will solve the problem of circular references, and everyone will change it)
It should be noted that the GCD timer resource must be set as a member variable, otherwise it will be released immediately after creation.
The suspended or suspended timer must be resumed before it can be canceled. If the suspended timer is directly canceled, it will cause a memory leak.
GCDTimer advantage: not affected by the current runloopMode.
Disadvantage: Although it is not affected by runloopMode, its timing effect is still not 100% accurate.
In addition, his trigger event may be blocked. When all threads managed by GCD are occupied, its trigger event will be delayed.
Well GCD I have no use to play around, just say that. I'll find time later
Objective-C three Use of Timer CADisplayLink / NSTimer / GCD