1111#import < QuartzCore/CADisplayLink.h>
1212
1313#import " RCTAssert.h"
14- #import " RCTBridgeModule.h"
1514#import " RCTFrameUpdate.h"
16- #import " RCTModuleData.h"
1715#import " RCTProfile.h"
1816
1917#define RCTAssertRunLoop () \
2018 RCTAssert (_runLoop == [NSRunLoop currentRunLoop ], @" This method must be called on the CADisplayLink run loop" )
2119
2220@implementation RCTDisplayLink {
2321 CADisplayLink *_jsDisplayLink;
24- NSMutableSet < id <RCTDisplayLinkModuleHolder>> *_frameUpdateObservers ;
22+ id <RCTFrameUpdateObserver> _frameUpdateObserver ;
2523 NSRunLoop *_runLoop;
2624}
2725
28- - (instancetype )init
26+ - (instancetype )initWithFrameUpdateObserver:( id <RCTFrameUpdateObserver>)observer
2927{
3028 if ((self = [super init ])) {
31- _frameUpdateObservers = [ NSMutableSet new ] ;
29+ _frameUpdateObserver = observer ;
3230 _jsDisplayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector (_jsThreadUpdate: )];
33- }
3431
35- return self;
36- }
32+ __weak typeof (self) weakSelf = self;
33+ observer.pauseCallback = ^{
34+ typeof (self) strongSelf = weakSelf;
35+ if (!strongSelf) {
36+ return ;
37+ }
3738
38- - (void )registerModuleForFrameUpdates:(id <RCTBridgeModule>)module
39- withModuleHolder:(id <RCTDisplayLinkModuleHolder>)moduleHolder
40- {
41- if (![moduleHolder.moduleClass conformsToProtocol: @protocol (RCTFrameUpdateObserver)] ||
42- [_frameUpdateObservers containsObject: moduleHolder]) {
43- return ;
44- }
39+ CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop ];
40+ if (!cfRunLoop) {
41+ return ;
42+ }
4543
46- [_frameUpdateObservers addObject: moduleHolder];
47-
48- // Don't access the module instance via moduleHolder, as this will cause deadlock
49- id <RCTFrameUpdateObserver> observer = (id <RCTFrameUpdateObserver>)module;
50- __weak typeof (self) weakSelf = self;
51- observer.pauseCallback = ^{
52- typeof (self) strongSelf = weakSelf;
53- if (!strongSelf) {
54- return ;
55- }
56-
57- CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop ];
58- if (!cfRunLoop) {
59- return ;
60- }
61-
62- if ([NSRunLoop currentRunLoop ] == strongSelf->_runLoop ) {
63- [weakSelf updateJSDisplayLinkState ];
64- } else {
65- CFRunLoopPerformBlock (cfRunLoop, kCFRunLoopDefaultMode , ^{
66- @autoreleasepool {
67- [weakSelf updateJSDisplayLinkState ];
68- }
69- });
70- CFRunLoopWakeUp (cfRunLoop);
71- }
72- };
73-
74- // Assuming we're paused right now, we only need to update the display link's state
75- // when the new observer is not paused. If it not paused, the observer will immediately
76- // start receiving updates anyway.
77- if (![observer isPaused ] && _runLoop) {
78- CFRunLoopPerformBlock ([_runLoop getCFRunLoop ], kCFRunLoopDefaultMode , ^{
79- @autoreleasepool {
80- [self updateJSDisplayLinkState ];
44+ if ([NSRunLoop currentRunLoop ] == strongSelf->_runLoop ) {
45+ [weakSelf updateJSDisplayLinkState ];
46+ } else {
47+ CFRunLoopPerformBlock (cfRunLoop, kCFRunLoopDefaultMode , ^{
48+ @autoreleasepool {
49+ [weakSelf updateJSDisplayLinkState ];
50+ }
51+ });
52+ CFRunLoopWakeUp (cfRunLoop);
8153 }
82- }) ;
54+ };
8355 }
56+
57+ return self;
8458}
8559
8660- (void )addToRunLoop:(NSRunLoop *)runLoop
@@ -96,47 +70,24 @@ - (void)dealloc
9670
9771- (void )invalidate
9872{
99- // ensure observer callbacks do not hold a reference to weak self via pauseCallback
100- for (id <RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
101- id <RCTFrameUpdateObserver> observer = (id <RCTFrameUpdateObserver>)moduleHolder.instance ;
102- [observer setPauseCallback: nil ];
103- }
104- [_frameUpdateObservers removeAllObjects ]; // just to be explicit
73+ // ensure the observer callback does not hold a reference to weak self via pauseCallback
74+ [_frameUpdateObserver setPauseCallback: nil ];
75+ _frameUpdateObserver = nil ;
10576
10677 [_jsDisplayLink invalidate ];
10778}
10879
109- - (void )dispatchBlock:(dispatch_block_t )block queue:(dispatch_queue_t )queue
110- {
111- if (queue == RCTJSThread) {
112- block ();
113- } else if (queue) {
114- dispatch_async (queue, block);
115- }
116- }
117-
11880- (void )_jsThreadUpdate:(CADisplayLink *)displayLink
11981{
12082 RCTAssertRunLoop ();
12183
12284 RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways, @" -[RCTDisplayLink _jsThreadUpdate:]" , nil );
12385
86+ // This always runs on the JS thread run loop, which is the queue the frame
87+ // update observer expects its callbacks on, so dispatch inline.
12488 RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc ] initWithDisplayLink: displayLink];
125- for (id <RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
126- id <RCTFrameUpdateObserver> observer = (id <RCTFrameUpdateObserver>)moduleHolder.instance ;
127- if (!observer.paused ) {
128- if (moduleHolder.methodQueue ) {
129- RCTProfileBeginFlowEvent ();
130- [self
131- dispatchBlock: ^{
132- RCTProfileEndFlowEvent ();
133- [observer didUpdateFrame: frameUpdate];
134- }
135- queue: moduleHolder.methodQueue];
136- } else {
137- [observer didUpdateFrame: frameUpdate];
138- }
139- }
89+ if (!_frameUpdateObserver.paused ) {
90+ [_frameUpdateObserver didUpdateFrame: frameUpdate];
14091 }
14192
14293 [self updateJSDisplayLinkState ];
@@ -150,16 +101,7 @@ - (void)updateJSDisplayLinkState
150101{
151102 RCTAssertRunLoop ();
152103
153- BOOL pauseDisplayLink = YES ;
154- for (id <RCTDisplayLinkModuleHolder> moduleHolder in _frameUpdateObservers) {
155- id <RCTFrameUpdateObserver> observer = (id <RCTFrameUpdateObserver>)moduleHolder.instance ;
156- if (!observer.paused ) {
157- pauseDisplayLink = NO ;
158- break ;
159- }
160- }
161-
162- _jsDisplayLink.paused = pauseDisplayLink;
104+ _jsDisplayLink.paused = _frameUpdateObserver == nil || _frameUpdateObserver.paused ;
163105}
164106
165107@end
0 commit comments