diff --git a/TYTextKitDemo.xcodeproj/project.pbxproj b/TYTextKitDemo.xcodeproj/project.pbxproj index ac7ce40..3948a72 100644 --- a/TYTextKitDemo.xcodeproj/project.pbxproj +++ b/TYTextKitDemo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0D4F220C24DC5350001EBC31 /* TYTaskScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D4F220B24DC5350001EBC31 /* TYTaskScheduler.m */; }; 2B0869F11FAFFB85008062CF /* TextAutolayoutController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B0869EF1FAFFB85008062CF /* TextAutolayoutController.m */; }; 2B0869F21FAFFB85008062CF /* TextAutolayoutController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2B0869F01FAFFB85008062CF /* TextAutolayoutController.xib */; }; 2B426F461F78F062009E70D9 /* TextDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B426F451F78F062009E70D9 /* TextDemoViewController.m */; }; @@ -38,6 +39,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 0D4F220A24DC5350001EBC31 /* TYTaskScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TYTaskScheduler.h; sourceTree = ""; }; + 0D4F220B24DC5350001EBC31 /* TYTaskScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TYTaskScheduler.m; sourceTree = ""; }; 2B0869EE1FAFFB85008062CF /* TextAutolayoutController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextAutolayoutController.h; sourceTree = ""; }; 2B0869EF1FAFFB85008062CF /* TextAutolayoutController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextAutolayoutController.m; sourceTree = ""; }; 2B0869F01FAFFB85008062CF /* TextAutolayoutController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextAutolayoutController.xib; sourceTree = ""; }; @@ -127,6 +130,8 @@ 2B4850FB1FBD7B230021BB28 /* TYTextView.m */, 2B4851191FBD7B510021BB28 /* TYAsyncLayer.h */, 2B4851181FBD7B510021BB28 /* TYAsyncLayer.m */, + 0D4F220A24DC5350001EBC31 /* TYTaskScheduler.h */, + 0D4F220B24DC5350001EBC31 /* TYTaskScheduler.m */, 2B4850FF1FBD7B230021BB28 /* TextKit */, ); path = TYText; @@ -304,6 +309,7 @@ 2B49A1221F973B19007E1935 /* ImageTextListViewController.m in Sources */, 2B48510F1FBD7B230021BB28 /* TYTextView.m in Sources */, 2B426F4F1F78F7AE009E70D9 /* TextViewCell.m in Sources */, + 0D4F220C24DC5350001EBC31 /* TYTaskScheduler.m in Sources */, 2B4851141FBD7B230021BB28 /* TYTextParse.m in Sources */, 2B4851131FBD7B230021BB28 /* TYTextRender.m in Sources */, 2B426F551F790A3C009E70D9 /* TYFPSLabel.m in Sources */, diff --git a/TYTextKitDemo/.DS_Store b/TYTextKitDemo/.DS_Store new file mode 100644 index 0000000..f8f00b4 Binary files /dev/null and b/TYTextKitDemo/.DS_Store differ diff --git a/TYTextKitDemo/TYText/TYAsyncLayer.h b/TYTextKitDemo/TYText/TYAsyncLayer.h index e36e6f9..b2ca22d 100644 --- a/TYTextKitDemo/TYText/TYAsyncLayer.h +++ b/TYTextKitDemo/TYText/TYAsyncLayer.h @@ -48,6 +48,7 @@ CGFloat ty_text_screen_scale(void); @interface TYAsyncLayerDisplayTask : NSObject +@property (nullable, nonatomic, weak) CALayer *layer; /** will display layer on main thread. diff --git a/TYTextKitDemo/TYText/TYAsyncLayer.m b/TYTextKitDemo/TYText/TYAsyncLayer.m index 401849e..45242e1 100644 --- a/TYTextKitDemo/TYText/TYAsyncLayer.m +++ b/TYTextKitDemo/TYText/TYAsyncLayer.m @@ -9,6 +9,8 @@ #import "TYAsyncLayer.h" #import +#import "TYTaskScheduler.h" + static dispatch_queue_t asyncDisplayQueue() { static dispatch_queue_t displayQueue = NULL; static dispatch_once_t onceToken; @@ -122,10 +124,18 @@ - (void)dealloc { } #pragma mark - private - - (void)displayAsync:(BOOL)asynchronously { __strong id delegate = _asyncDelegate; - TYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask]; + TYAsyncLayerDisplayTask *ltask = [delegate newAsyncDisplayTask]; + ltask.layer = self; + __weak TYAsyncLayer *weakSelf = self; + void (^block)(TYAsyncLayerDisplayTask *task) = ^(TYAsyncLayerDisplayTask *task) { + [weakSelf displayAsync:asynchronously task:task]; + }; + [[TYTaskScheduler sharedInstance] dispatchTask:ltask block:block]; +} + +- (void)displayAsync:(BOOL)asynchronously task:(TYAsyncLayerDisplayTask *)task { if (task.willDisplay) { task.willDisplay(self); } diff --git a/TYTextKitDemo/TYText/TYTaskScheduler.h b/TYTextKitDemo/TYText/TYTaskScheduler.h new file mode 100644 index 0000000..aa3fe60 --- /dev/null +++ b/TYTextKitDemo/TYText/TYTaskScheduler.h @@ -0,0 +1,22 @@ +// +// TYTaskScheduler.h +// TYTextKitDemo +// +// Created by jufan wang on 2020/8/6. +// Copyright © 2020 tany. All rights reserved. +// + +#import + +@class TYAsyncLayerDisplayTask; + +@interface TYTaskScheduler : NSObject + ++ (instancetype)sharedInstance; + +@property (nonatomic, assign) NSInteger maxScheduleCount; + +- (void)dispatchTask:(TYAsyncLayerDisplayTask *)task + block:(void (^)(TYAsyncLayerDisplayTask *task))block; + +@end diff --git a/TYTextKitDemo/TYText/TYTaskScheduler.m b/TYTextKitDemo/TYText/TYTaskScheduler.m new file mode 100644 index 0000000..9f86a71 --- /dev/null +++ b/TYTextKitDemo/TYText/TYTaskScheduler.m @@ -0,0 +1,100 @@ +// +// TYTaskScheduler.m +// TYTextKitDemo +// +// Created by jufan wang on 2020/8/6. +// Copyright © 2020 tany. All rights reserved. +// + +#import "TYTaskScheduler.h" +#import "TYAsyncLayer.h" + +@interface TYTaskScheduleParas : NSObject +@property (nonatomic, strong) TYAsyncLayerDisplayTask *task; +@property (nullable, nonatomic, copy) void (^block)(TYAsyncLayerDisplayTask *task); +@end +@implementation TYTaskScheduleParas +@end + +@interface TYTaskScheduler() { + CFRunLoopObserverRef _pendingListObserver; +} +@property (nonatomic, strong) NSMutableArray* tasksArray; +@end + +@implementation TYTaskScheduler + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static TYTaskScheduler *_taskScheduler = nil; + dispatch_once(&onceToken, ^{ + _taskScheduler = [[TYTaskScheduler alloc] init]; + }); + return _taskScheduler; +} + +- (instancetype)init { + if (self = [super init]) { + _tasksArray = [NSMutableArray array]; + _maxScheduleCount = 22; + [self schedulePendingList]; + } + return self; +} + +- (void)dispatchTask:(TYAsyncLayerDisplayTask *)task + block:(void (^)(TYAsyncLayerDisplayTask *task))block { + TYTaskScheduleParas *paras = nil; + for (TYTaskScheduleParas *pa in self.tasksArray) { + if (pa.task.layer == task.layer) { + paras = pa; + [self.tasksArray removeObject:paras]; + break; + } + } + if (!paras) { + paras = [[TYTaskScheduleParas alloc] init]; + } + paras.block = block; + paras.task = task; + [self.tasksArray addObject:paras]; + if (self.tasksArray.count > self.maxScheduleCount) { + [self.tasksArray removeObjectsInRange:NSMakeRange(0, self.tasksArray.count - self.maxScheduleCount)]; + } +} + +#pragma mark -- schedule + +- (void)clearPendingList { + if (_pendingListObserver) { + CFRunLoopRemoveObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes); + CFRelease(_pendingListObserver); + _pendingListObserver = NULL; + } +} + +- (void)schedulePendingList { + // see WebKit for magic numbers, eg http://trac.webkit.org/changeset/166540 + static const CFIndex TYTransactionCommitRunLoopOrder = 2000000; + static const CFIndex TYTaskSchedulerApplyRunLoopOrder = TYTransactionCommitRunLoopOrder - 1; + __weak TYTaskScheduler *weakSelf = self; + if (!_pendingListObserver) { + _pendingListObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, TYTaskSchedulerApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + TYTaskScheduleParas *paras = [weakSelf.tasksArray lastObject]; + if (!paras) return ; + [weakSelf.tasksArray removeObject:paras]; + if (paras.block) { + paras.block(paras.task); + } + }); + CFRunLoopAddObserver(CFRunLoopGetMain(), _pendingListObserver, kCFRunLoopCommonModes); + } +} + +- (void)dealloc { + [self clearPendingList]; +} + +@end + + diff --git a/TYTextKitDemo/TextListViewController.m b/TYTextKitDemo/TextListViewController.m index 8bebc25..35e02f4 100644 --- a/TYTextKitDemo/TextListViewController.m +++ b/TYTextKitDemo/TextListViewController.m @@ -50,7 +50,7 @@ - (void)loadData { NSMutableArray *itemArray = [NSMutableArray array]; NSMutableArray *textArray = [NSMutableArray array]; NSMutableArray *renderArray = [NSMutableArray array]; - for (int i = 0; i < 200; ++i) { + for (int i = 0; i < 2000; ++i) { NSString *str = [NSString stringWithFormat:@"%d Async Display Test ✺◟(❛ัᴗ❛∗)◞✺ ✺◟(∗❛ᴗ❛∗)◞✺ 😀😖😐😣😡🚖🚌🚋🎊💖💗💛💙🏨🏦🏫 Async Display Test ✺◟(∗❛ᴗ❛∗)◞✺ ✺◟(∗❛ᴗ❛∗)◞✺ 😀😖😐😣😡🚖🚌🚋🎊💖💗💛💙🏨🏦🏫",i%3 ? i:i*100]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:str];