Skip to content

Commit 62a487a

Browse files
TheSisbIvan K
authored andcommitted
DCDDisplayLink
1 parent b61fe7f commit 62a487a

File tree

6 files changed

+234
-8
lines changed

6 files changed

+234
-8
lines changed

React/Base/DCDDisplayLink.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// RCTDisplayLink.h
3+
// React
4+
//
5+
// Created by Stanislav Vishnevskiy on 6/8/15.
6+
// Copyright (c) 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import <QuartzCore/CABase.h>
10+
#import <Foundation/NSObject.h>
11+
12+
@class NSString, NSRunLoop;
13+
14+
@interface DCDDisplayLink : NSObject
15+
16+
/* Create a new display link object for the main display. It will
17+
* invoke the method called 'sel' on 'target', the method has the
18+
* signature '(void)selector:(CADisplayLink *)sender'. */
19+
20+
+ (DCDDisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
21+
22+
/* Adds the receiver to the given run-loop and mode. Unless paused, it
23+
* will fire every vsync until removed. Each object may only be added
24+
* to a single run-loop, but it may be added in multiple modes at once.
25+
* While added to a run-loop it will implicitly be retained. */
26+
27+
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
28+
29+
/* Removes the receiver from the given mode of the runloop. This will
30+
* implicitly release it when removed from the last mode it has been
31+
* registered for. */
32+
33+
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;
34+
35+
/* Removes the object from all runloop modes (releasing the receiver if
36+
* it has been implicitly retained) and releases the 'target' object. */
37+
38+
- (void)invalidate;
39+
40+
/* The current time, and duration of the display frame associated with
41+
* the most recent target invocation. Time is represented using the
42+
* normal Core Animation conventions, i.e. Mach host time converted to
43+
* seconds. */
44+
45+
@property(readonly, nonatomic) CFTimeInterval timestamp;
46+
@property(readonly, nonatomic) CFTimeInterval duration;
47+
48+
/* When true the object is prevented from firing. Initial state is
49+
* false. */
50+
51+
@property(getter=isPaused, nonatomic) BOOL paused;
52+
53+
/* Defines how many display frames must pass between each time the
54+
* display link fires. Default value is one, which means the display
55+
* link will fire for every display frame. Setting the interval to two
56+
* will cause the display link to fire every other display frame, and
57+
* so on. The behavior when using values less than one is undefined. */
58+
59+
@property(nonatomic) NSInteger frameInterval;
60+
61+
@end

React/Base/DCDDisplayLink.m

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//
2+
// RCTDisplayLink.m
3+
// React
4+
//
5+
// Created by Stanislav Vishnevskiy on 6/8/15.
6+
// Copyright (c) 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import "DCDDisplayLink.h"
10+
#import <UIKit/UIKit.h>
11+
12+
@interface DCDDisplayLink ()
13+
14+
@property(nonatomic) NSRunLoop *runloop;
15+
@property(nonatomic) NSString *mode;
16+
@property(nonatomic) id target;
17+
@property(nonatomic) SEL selector;
18+
@property(nonatomic) NSTimer *timer;
19+
@property(nonatomic) CADisplayLink *displayLink;
20+
21+
// CADisplayLink is not thread safe.
22+
// Add a flag to avoid the crash of removing invalidated CADisplayLink from the run loop.
23+
@property(nonatomic) BOOL resourcesLoaded;
24+
25+
@end
26+
27+
@implementation DCDDisplayLink
28+
29+
+ (DCDDisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel {
30+
return [[self alloc] initWithTarget:target selector:sel];
31+
}
32+
33+
- (instancetype)initWithTarget:(id)target selector:(SEL)sel {
34+
if (self = [super init]) {
35+
_target = target;
36+
_selector = sel;
37+
_displayLink = [CADisplayLink displayLinkWithTarget:target selector:sel];
38+
_resourcesLoaded = YES;
39+
40+
[[NSNotificationCenter defaultCenter] addObserver:self
41+
selector:@selector(switchToTimer)
42+
name:UIApplicationDidEnterBackgroundNotification
43+
object:nil];
44+
[[NSNotificationCenter defaultCenter] addObserver:self
45+
selector:@selector(switchToDisplayLink)
46+
name:UIApplicationWillEnterForegroundNotification
47+
object:nil];
48+
}
49+
return self;
50+
}
51+
52+
- (void)dealloc {
53+
[[NSNotificationCenter defaultCenter] removeObserver:self];
54+
}
55+
56+
- (void)switchToDisplayLink {
57+
if (_timer) {
58+
[_timer invalidate];
59+
_timer = nil;
60+
[self setPaused:_paused];
61+
if (_runloop) {
62+
[_displayLink addToRunLoop:_runloop forMode:_mode];
63+
}
64+
}
65+
}
66+
67+
- (void)switchToTimer {
68+
if (!_timer) {
69+
[self maybeResetTimer];
70+
[self setPaused:_paused];
71+
if (_runloop && _resourcesLoaded) {
72+
[_displayLink removeFromRunLoop:_runloop forMode:_mode];
73+
[_runloop addTimer:_timer forMode:_mode];
74+
}
75+
}
76+
}
77+
78+
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode {
79+
_runloop = runloop;
80+
_mode = mode;
81+
if (_timer) {
82+
[self maybeResetTimer];
83+
[runloop addTimer:_timer forMode:mode];
84+
}
85+
else {
86+
[_displayLink addToRunLoop:runloop forMode:mode];
87+
}
88+
}
89+
90+
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode {
91+
_runloop = nil;
92+
_mode = nil;
93+
if (_timer) {
94+
[_timer invalidate];
95+
}
96+
else {
97+
[_displayLink removeFromRunLoop:runloop forMode:mode];
98+
}
99+
}
100+
101+
- (void)invalidate {
102+
_resourcesLoaded = NO;
103+
if (_timer) {
104+
[_timer invalidate];
105+
}
106+
else {
107+
[_displayLink invalidate];
108+
}
109+
}
110+
111+
- (void)setPaused:(BOOL)paused {
112+
_paused = paused;
113+
if (_timer) {
114+
if (paused) {
115+
[_timer invalidate];
116+
}
117+
else {
118+
[self maybeResetTimer];
119+
if (_runloop) {
120+
[_runloop addTimer:_timer forMode:_mode];
121+
}
122+
}
123+
}
124+
else {
125+
_displayLink.paused = paused;
126+
}
127+
}
128+
129+
- (CFTimeInterval)timestamp {
130+
if (_timer) {
131+
// TODO: Does React Native actually need this?
132+
return 0;
133+
}
134+
return _displayLink.timestamp;
135+
}
136+
137+
- (CFTimeInterval)duration {
138+
if (_timer) {
139+
// TODO: Does React Native actually need this?
140+
return 0;
141+
}
142+
return _displayLink.duration;
143+
}
144+
145+
- (void)maybeResetTimer {
146+
if (!_timer || ![_timer isValid]) {
147+
_timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerLoop) userInfo:nil repeats:YES];
148+
}
149+
}
150+
151+
- (void)timerLoop {
152+
if (_target) {
153+
IMP imp = [_target methodForSelector:_selector];
154+
void (*func)(id, SEL, DCDDisplayLink *) = (void *)imp;
155+
func(_target, _selector, self);
156+
}
157+
}
158+
159+
@end

React/Base/RCTDisplayLink.m

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#import "RCTDisplayLink.h"
1111

1212
#import <Foundation/Foundation.h>
13-
#import <QuartzCore/CADisplayLink.h>
13+
#import "DCDDisplayLink.h"
1414

1515
#import "RCTAssert.h"
1616
#import "RCTBridgeModule.h"
@@ -24,7 +24,7 @@
2424

2525
@implementation RCTDisplayLink
2626
{
27-
CADisplayLink *_jsDisplayLink;
27+
DCDDisplayLink *_jsDisplayLink;
2828
NSMutableSet<RCTModuleData *> *_frameUpdateObservers;
2929
NSRunLoop *_runLoop;
3030
}
@@ -33,7 +33,7 @@ - (instancetype)init
3333
{
3434
if ((self = [super init])) {
3535
_frameUpdateObservers = [NSMutableSet new];
36-
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
36+
_jsDisplayLink = [DCDDisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
3737
}
3838

3939
return self;
@@ -104,7 +104,7 @@ - (void)dispatchBlock:(dispatch_block_t)block
104104
}
105105
}
106106

107-
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
107+
- (void)_jsThreadUpdate:(DCDDisplayLink *)displayLink
108108
{
109109
RCTAssertRunLoop();
110110

React/Base/RCTFrameUpdate.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#import <Foundation/Foundation.h>
1111

12-
@class CADisplayLink;
12+
@class DCDDisplayLink;
1313

1414
/**
1515
* Interface containing the information about the last screen refresh.
@@ -26,7 +26,7 @@
2626
*/
2727
@property (nonatomic, readonly) NSTimeInterval deltaTime;
2828

29-
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink NS_DESIGNATED_INITIALIZER;
29+
- (instancetype)initWithDisplayLink:(DCDDisplayLink *)displayLink NS_DESIGNATED_INITIALIZER;
3030

3131
@end
3232

React/Base/RCTFrameUpdate.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*/
99

10-
#import <QuartzCore/CADisplayLink.h>
10+
#import "DCDDisplayLink.h"
1111

1212
#import "RCTFrameUpdate.h"
1313

@@ -17,7 +17,7 @@ @implementation RCTFrameUpdate
1717

1818
RCT_NOT_IMPLEMENTED(- (instancetype)init)
1919

20-
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink
20+
- (instancetype)initWithDisplayLink:(DCDDisplayLink *)displayLink
2121
{
2222
if ((self = [super init])) {
2323
_timestamp = displayLink.timestamp;

React/React.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,7 @@
10871087
C6827DF71EF17CCC00D66BEF /* RCTJSEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = C6827DF51EF17CCC00D66BEF /* RCTJSEnvironment.h */; };
10881088
C6827DFB1EF1800E00D66BEF /* RCTJSEnvironment.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = C6827DF51EF17CCC00D66BEF /* RCTJSEnvironment.h */; };
10891089
C6827DFC1EF1801B00D66BEF /* RCTJSEnvironment.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = C6827DF51EF17CCC00D66BEF /* RCTJSEnvironment.h */; };
1090+
CCE0428A1F72FC39006492D0 /* DCDDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = C19A08C91CEE3F6700DA4940 /* DCDDisplayLink.m */; };
10901091
CF2731C01E7B8DE40044CA4F /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */; };
10911092
CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */; };
10921093
CF2731C21E7B8DEF0044CA4F /* RCTDeviceInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = CF2731BE1E7B8DE40044CA4F /* RCTDeviceInfo.h */; };
@@ -2015,6 +2016,8 @@
20152016
B233E6E91D2D845D00BC68BA /* RCTI18nManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTI18nManager.m; sourceTree = "<group>"; };
20162017
B95154301D1B34B200FE7B80 /* RCTActivityIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTActivityIndicatorView.h; sourceTree = "<group>"; };
20172018
B95154311D1B34B200FE7B80 /* RCTActivityIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTActivityIndicatorView.m; sourceTree = "<group>"; };
2019+
C19A08C81CEE3F6700DA4940 /* DCDDisplayLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DCDDisplayLink.h; sourceTree = "<group>"; };
2020+
C19A08C91CEE3F6700DA4940 /* DCDDisplayLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DCDDisplayLink.m; sourceTree = "<group>"; };
20182021
C6194AA91EF156280034D062 /* RCTPackagerConnectionBridgeConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPackagerConnectionBridgeConfig.h; sourceTree = "<group>"; };
20192022
C6194AAA1EF156280034D062 /* RCTPackagerConnectionBridgeConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPackagerConnectionBridgeConfig.m; sourceTree = "<group>"; };
20202023
C6194AAB1EF156280034D062 /* RCTPackagerConnectionConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPackagerConnectionConfig.h; sourceTree = "<group>"; };
@@ -2634,6 +2637,8 @@
26342637
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
26352638
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
26362639
83CBBA501A601E3B00E9B192 /* RCTUtils.m */,
2640+
C19A08C81CEE3F6700DA4940 /* DCDDisplayLink.h */,
2641+
C19A08C91CEE3F6700DA4940 /* DCDDisplayLink.m */,
26372642
);
26382643
path = Base;
26392644
sourceTree = "<group>";
@@ -3832,6 +3837,7 @@
38323837
isa = PBXSourcesBuildPhase;
38333838
buildActionMask = 2147483647;
38343839
files = (
3840+
CCE0428A1F72FC39006492D0 /* DCDDisplayLink.m in Sources */,
38353841
13134C9A1E296B2A00B9F3CB /* RCTCxxMethod.mm in Sources */,
38363842
59FBEFB61E46D91C0095D885 /* RCTScrollContentViewManager.m in Sources */,
38373843
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */,

0 commit comments

Comments
 (0)