RCTDisplayLink.m 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTDisplayLink.h"
  8. #import <Foundation/Foundation.h>
  9. #import <QuartzCore/CADisplayLink.h>
  10. #import "RCTAssert.h"
  11. #import "RCTBridgeModule.h"
  12. #import "RCTFrameUpdate.h"
  13. #import "RCTModuleData.h"
  14. #import "RCTProfile.h"
  15. #define RCTAssertRunLoop() \
  16. RCTAssert(_runLoop == [NSRunLoop currentRunLoop], @"This method must be called on the CADisplayLink run loop")
  17. @implementation RCTDisplayLink {
  18. CADisplayLink *_jsDisplayLink;
  19. NSMutableSet<RCTModuleData *> *_frameUpdateObservers;
  20. NSRunLoop *_runLoop;
  21. }
  22. - (instancetype)init
  23. {
  24. if ((self = [super init])) {
  25. _frameUpdateObservers = [NSMutableSet new];
  26. _jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
  27. }
  28. return self;
  29. }
  30. - (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module withModuleData:(RCTModuleData *)moduleData
  31. {
  32. if (![moduleData.moduleClass conformsToProtocol:@protocol(RCTFrameUpdateObserver)] ||
  33. [_frameUpdateObservers containsObject:moduleData]) {
  34. return;
  35. }
  36. [_frameUpdateObservers addObject:moduleData];
  37. // Don't access the module instance via moduleData, as this will cause deadlock
  38. id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)module;
  39. __weak typeof(self) weakSelf = self;
  40. observer.pauseCallback = ^{
  41. typeof(self) strongSelf = weakSelf;
  42. if (!strongSelf) {
  43. return;
  44. }
  45. CFRunLoopRef cfRunLoop = [strongSelf->_runLoop getCFRunLoop];
  46. if (!cfRunLoop) {
  47. return;
  48. }
  49. if ([NSRunLoop currentRunLoop] == strongSelf->_runLoop) {
  50. [weakSelf updateJSDisplayLinkState];
  51. } else {
  52. CFRunLoopPerformBlock(cfRunLoop, kCFRunLoopDefaultMode, ^{
  53. [weakSelf updateJSDisplayLinkState];
  54. });
  55. CFRunLoopWakeUp(cfRunLoop);
  56. }
  57. };
  58. // Assuming we're paused right now, we only need to update the display link's state
  59. // when the new observer is not paused. If it not paused, the observer will immediately
  60. // start receiving updates anyway.
  61. if (![observer isPaused] && _runLoop) {
  62. CFRunLoopPerformBlock([_runLoop getCFRunLoop], kCFRunLoopDefaultMode, ^{
  63. [self updateJSDisplayLinkState];
  64. });
  65. }
  66. }
  67. - (void)addToRunLoop:(NSRunLoop *)runLoop
  68. {
  69. _runLoop = runLoop;
  70. [_jsDisplayLink addToRunLoop:runLoop forMode:NSRunLoopCommonModes];
  71. }
  72. - (void)dealloc
  73. {
  74. [self invalidate];
  75. }
  76. - (void)invalidate
  77. {
  78. [_jsDisplayLink invalidate];
  79. }
  80. - (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue
  81. {
  82. if (queue == RCTJSThread) {
  83. block();
  84. } else if (queue) {
  85. dispatch_async(queue, block);
  86. }
  87. }
  88. - (void)_jsThreadUpdate:(CADisplayLink *)displayLink
  89. {
  90. RCTAssertRunLoop();
  91. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTDisplayLink _jsThreadUpdate:]", nil);
  92. RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
  93. for (RCTModuleData *moduleData in _frameUpdateObservers) {
  94. id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
  95. if (!observer.paused) {
  96. if (moduleData.methodQueue) {
  97. RCTProfileBeginFlowEvent();
  98. [self
  99. dispatchBlock:^{
  100. RCTProfileEndFlowEvent();
  101. [observer didUpdateFrame:frameUpdate];
  102. }
  103. queue:moduleData.methodQueue];
  104. } else {
  105. [observer didUpdateFrame:frameUpdate];
  106. }
  107. }
  108. }
  109. [self updateJSDisplayLinkState];
  110. RCTProfileImmediateEvent(RCTProfileTagAlways, @"JS Thread Tick", displayLink.timestamp, 'g');
  111. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"objc_call");
  112. }
  113. - (void)updateJSDisplayLinkState
  114. {
  115. RCTAssertRunLoop();
  116. BOOL pauseDisplayLink = YES;
  117. for (RCTModuleData *moduleData in _frameUpdateObservers) {
  118. id<RCTFrameUpdateObserver> observer = (id<RCTFrameUpdateObserver>)moduleData.instance;
  119. if (!observer.paused) {
  120. pauseDisplayLink = NO;
  121. break;
  122. }
  123. }
  124. _jsDisplayLink.paused = pauseDisplayLink;
  125. }
  126. @end