Authored by 刘金林

页面首次加载性能统计

@@ -707,6 +707,7 @@ @@ -707,6 +707,7 @@
707 TargetAttributes = { 707 TargetAttributes = {
708 529A00451F564E8500A83F63 = { 708 529A00451F564E8500A83F63 = {
709 CreatedOnToolsVersion = 8.3; 709 CreatedOnToolsVersion = 8.3;
  710 + DevelopmentTeam = LUM7XZ8Q2G;
710 ProvisioningStyle = Automatic; 711 ProvisioningStyle = Automatic;
711 }; 712 };
712 }; 713 };
@@ -947,6 +948,7 @@ @@ -947,6 +948,7 @@
947 isa = XCBuildConfiguration; 948 isa = XCBuildConfiguration;
948 buildSettings = { 949 buildSettings = {
949 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 950 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
  951 + DEVELOPMENT_TEAM = LUM7XZ8Q2G;
950 FRAMEWORK_SEARCH_PATHS = ( 952 FRAMEWORK_SEARCH_PATHS = (
951 "$(inherited)", 953 "$(inherited)",
952 "$(PROJECT_DIR)/YHEventReport/Utils/SDWebImage/Vendors", 954 "$(PROJECT_DIR)/YHEventReport/Utils/SDWebImage/Vendors",
@@ -965,6 +967,7 @@ @@ -965,6 +967,7 @@
965 isa = XCBuildConfiguration; 967 isa = XCBuildConfiguration;
966 buildSettings = { 968 buildSettings = {
967 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 969 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
  970 + DEVELOPMENT_TEAM = LUM7XZ8Q2G;
968 FRAMEWORK_SEARCH_PATHS = ( 971 FRAMEWORK_SEARCH_PATHS = (
969 "$(inherited)", 972 "$(inherited)",
970 "$(PROJECT_DIR)/YHEventReport/Utils/SDWebImage/Vendors", 973 "$(PROJECT_DIR)/YHEventReport/Utils/SDWebImage/Vendors",
@@ -8,6 +8,13 @@ @@ -8,6 +8,13 @@
8 8
9 #import <UIKit/UIKit.h> 9 #import <UIKit/UIKit.h>
10 10
  11 +@protocol UIViewControllerPerformanceTrack <NSObject>
  12 +
  13 +@optional
  14 +- (BOOL)shouldNotTrackFirstPageLoadPerformance;
  15 +
  16 +@end
  17 +
11 @interface UIViewController (AutoTrack) 18 @interface UIViewController (AutoTrack)
12 19
13 @property(nonatomic,copy)NSString *yh_viewId;//该页面唯一ID 20 @property(nonatomic,copy)NSString *yh_viewId;//该页面唯一ID
@@ -16,7 +23,7 @@ @@ -16,7 +23,7 @@
16 23
17 - (UITabBarController *)yher_tabBarController; 24 - (UITabBarController *)yher_tabBarController;
18 25
19 -@property(nonatomic,copy)NSString* yher_hasReported;//tab VC 已经统计过 @"0" @"1" 26 +@property(nonatomic,assign) BOOL yher_hasReported;
20 27
21 +(void)startTrack; 28 +(void)startTrack;
22 29
@@ -14,6 +14,44 @@ @@ -14,6 +14,44 @@
14 #import "YH_EventCollector.h" 14 #import "YH_EventCollector.h"
15 #import "YHEventReport.h" 15 #import "YHEventReport.h"
16 16
  17 +@interface UIViewController (AutoTrackPrivate)
  18 +
  19 +@property (nonatomic, assign, readonly) BOOL shouldTrack;
  20 +@property (nonatomic, assign) BOOL shouldResetStartLoadDate;
  21 +
  22 +@end
  23 +
  24 +@implementation UIViewController (AutoTrackPrivate)
  25 +
  26 +- (BOOL)shouldTrack {
  27 + return ![self isSystemContainerController] &&
  28 + ![self shouldNotTrackByProtocol] &&
  29 + [YHEventReport sharedInstance].isPerformanceTrackEnabled &&
  30 + [YHEventReport sharedInstance].isControllerPerformanceTrackEnable &&
  31 + ![[YHEventReport sharedInstance] isPerformanceViewControllerStringIgnored:NSStringFromClass([self class])];
  32 +}
  33 +
  34 +- (BOOL)shouldNotTrackByProtocol {
  35 + return [self conformsToProtocol:@protocol(UIViewControllerPerformanceTrack)] &&
  36 + [(id<UIViewControllerPerformanceTrack>)self shouldNotTrackFirstPageLoadPerformance];
  37 +}
  38 +
  39 +- (void)setShouldResetStartLoadDate:(BOOL)shouldResetStartLoadDate {
  40 + objc_setAssociatedObject(self, @selector(shouldResetStartLoadDate), @(shouldResetStartLoadDate), OBJC_ASSOCIATION_COPY_NONATOMIC);
  41 +}
  42 +
  43 +- (BOOL)shouldResetStartLoadDate {
  44 + return [objc_getAssociatedObject(self, _cmd) boolValue];
  45 +}
  46 +
  47 +- (BOOL)isSystemContainerController {
  48 + return [self isKindOfClass:[UINavigationController class]] ||
  49 + [self isKindOfClass:[UITabBarController class]] ||
  50 + [self isKindOfClass:[UISplitViewController class]] ||
  51 + [self isKindOfClass:[UIPageViewController class]];
  52 +}
  53 +
  54 +@end
17 55
18 @implementation UIViewController (AutoTrack) 56 @implementation UIViewController (AutoTrack)
19 57
@@ -39,6 +77,15 @@ @@ -39,6 +77,15 @@
39 viewdidappearError = NULL; 77 viewdidappearError = NULL;
40 } 78 }
41 79
  80 + NSError *viewwillappearError = NULL;
  81 + [[self class] yh_swizzleMethod:@selector(viewWillAppear:)
  82 + withMethod:@selector(yher_viewWillAppear:)
  83 + error:&viewwillappearError];
  84 + if (viewwillappearError) {
  85 + YHLog(@"Failed to swizzle viewWillAppear: on UIViewController. Details: %@", viewwillappearError);
  86 + viewdidappearError = NULL;
  87 + }
  88 +
42 } @catch (NSException *exception) { 89 } @catch (NSException *exception) {
43 YHLog(@"%@ error: %@", self, exception); 90 YHLog(@"%@ error: %@", self, exception);
44 } 91 }
@@ -48,19 +95,30 @@ @@ -48,19 +95,30 @@
48 95
49 - (void)yher_viewDidLoad{ 96 - (void)yher_viewDidLoad{
50 @try { 97 @try {
51 - if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isControllerPerformanceTrackEnable && ![[YHEventReport sharedInstance] isPerformanceViewControllerStringIgnored:NSStringFromClass([self class])]) { 98 + if (self.shouldTrack) {
52 [[YH_EventCollector sharedInstance] timeEventStartWithViewController:self]; 99 [[YH_EventCollector sharedInstance] timeEventStartWithViewController:self];
  100 + [self performSelector:@selector(yher_checkVisiable) withObject:nil afterDelay:0];
53 } 101 }
54 } @catch (NSException *exception) { 102 } @catch (NSException *exception) {
55 YHLog(@"%@ error: %@", self, exception); 103 YHLog(@"%@ error: %@", self, exception);
56 } 104 }
57 -  
58 [self yher_viewDidLoad]; 105 [self yher_viewDidLoad];
59 } 106 }
60 107
  108 +-(void)yher_viewWillAppear:(BOOL)animated {
  109 + @try {
  110 + if (self.shouldTrack && self.shouldResetStartLoadDate && !self.yher_hasReported) {
  111 + [[YH_EventCollector sharedInstance] timeEventStartWithViewController:self];
  112 + }
  113 + } @catch (NSException *exception) {
  114 + YHLog(@"%@ error: %@", self, exception);
  115 + }
  116 + [self yher_viewWillAppear:animated];
  117 +}
  118 +
61 -(void)yher_viewDidAppear:(BOOL)animated { 119 -(void)yher_viewDidAppear:(BOOL)animated {
62 @try { 120 @try {
63 - if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isControllerPerformanceTrackEnable && ![[YHEventReport sharedInstance] isPerformanceViewControllerStringIgnored:NSStringFromClass([self class])]) { 121 + if (self.shouldTrack && !self.yher_hasReported) {
64 [[YH_EventCollector sharedInstance] timeEventEndWithViewController:self]; 122 [[YH_EventCollector sharedInstance] timeEventEndWithViewController:self];
65 } 123 }
66 if ([YHEventReport sharedInstance].isAppCrashReportEnable && [YHEventReport sharedInstance].isAutoTrackEnabled) { 124 if ([YHEventReport sharedInstance].isAppCrashReportEnable && [YHEventReport sharedInstance].isAutoTrackEnabled) {
@@ -86,12 +144,18 @@ @@ -86,12 +144,18 @@
86 [self yher_viewDidAppear:animated]; 144 [self yher_viewDidAppear:animated];
87 } 145 }
88 146
  147 +- (void)yher_checkVisiable {
  148 + if (!self.view.window) {
  149 + self.shouldResetStartLoadDate = YES;
  150 + }
  151 +}
  152 +
89 - (void)setYh_viewId:(NSString *)yh_viewId{ 153 - (void)setYh_viewId:(NSString *)yh_viewId{
90 objc_setAssociatedObject(self,@selector(yh_viewId),yh_viewId,OBJC_ASSOCIATION_RETAIN); 154 objc_setAssociatedObject(self,@selector(yh_viewId),yh_viewId,OBJC_ASSOCIATION_RETAIN);
91 } 155 }
92 156
93 - (NSString *)yh_viewId{ 157 - (NSString *)yh_viewId{
94 - return objc_getAssociatedObject(self, @selector(yh_viewId)); 158 + return objc_getAssociatedObject(self, _cmd);
95 } 159 }
96 160
97 161
@@ -100,15 +164,15 @@ @@ -100,15 +164,15 @@
100 } 164 }
101 165
102 - (NSString *)yh_Alias{ 166 - (NSString *)yh_Alias{
103 - return objc_getAssociatedObject(self, @selector(yh_Alias)); 167 + return objc_getAssociatedObject(self, _cmd);
104 } 168 }
105 169
106 -- (void)setYher_hasReported:(NSString *)yher_hasReported{  
107 - objc_setAssociatedObject(self,@selector(yher_hasReported),yher_hasReported,OBJC_ASSOCIATION_RETAIN); 170 +- (void)setYher_hasReported:(BOOL)yher_hasReported{
  171 + objc_setAssociatedObject(self,@selector(yher_hasReported),@(yher_hasReported),OBJC_ASSOCIATION_COPY_NONATOMIC);
108 } 172 }
109 173
110 -- (NSString *)yher_hasReported{  
111 - return objc_getAssociatedObject(self, @selector(yher_hasReported)); 174 +- (BOOL)yher_hasReported{
  175 + return [objc_getAssociatedObject(self, _cmd) boolValue];
112 } 176 }
113 177
114 - (UITabBarController *)yher_tabBarController 178 - (UITabBarController *)yher_tabBarController
@@ -28,6 +28,16 @@ @@ -28,6 +28,16 @@
28 #define kYHEventReportQueue @"YHEventReportQueue" 28 #define kYHEventReportQueue @"YHEventReportQueue"
29 #define kYHEventReporrH5StartKey @"YHEventReporrH5StartKey" 29 #define kYHEventReporrH5StartKey @"YHEventReporrH5StartKey"
30 30
  31 +NS_INLINE NSString *YH_Objectkey(id anyObject) {
  32 + NSCParameterAssert(anyObject);
  33 + if (!anyObject) {
  34 + return NSStringFromClass(NSNull.class);
  35 + }
  36 + uintptr_t intPoint = (uintptr_t)anyObject;
  37 + NSString *keyPart1 = NSStringFromClass([anyObject class]);
  38 + NSString *keyPart2 = @(intPoint).description;
  39 + return [keyPart1 stringByAppendingString:keyPart2];
  40 +}
31 41
32 @interface YH_EventCollector() 42 @interface YH_EventCollector()
33 43
@@ -182,16 +192,12 @@ @@ -182,16 +192,12 @@
182 192
183 #pragma mark - viewcontroller performance 193 #pragma mark - viewcontroller performance
184 - (void)timeEventStartWithViewController:(UIViewController *)viewController{ 194 - (void)timeEventStartWithViewController:(UIViewController *)viewController{
185 - NSNumber *startTime = @([[NSDate date] timeIntervalSince1970]);  
186 - NSString *viewId = [NSString stringWithFormat:@"%@%@",NSStringFromClass([viewController class]),startTime];  
187 -  
188 - if (startTime==0||IsStrEmpty(viewId)) {  
189 - return;  
190 - }  
191 - 195 + NSParameterAssert(viewController);
192 if (!viewController) { 196 if (!viewController) {
193 return; 197 return;
194 } 198 }
  199 + NSNumber *startTime = @([[NSDate date] timeIntervalSince1970]);
  200 + NSString *viewId = YH_Objectkey(viewController);
195 201
196 if ([viewController isKindOfClass:NSClassFromString(@"YH_H5ViewController")]) { 202 if ([viewController isKindOfClass:NSClassFromString(@"YH_H5ViewController")]) {
197 dispatch_async(self.serialQueue, ^{ 203 dispatch_async(self.serialQueue, ^{
@@ -206,20 +212,22 @@ @@ -206,20 +212,22 @@
206 } 212 }
207 213
208 - (void)timeEventEndWithViewController:(UIViewController *)viewController{ 214 - (void)timeEventEndWithViewController:(UIViewController *)viewController{
209 - 215 + NSParameterAssert(viewController);
  216 + if (!viewController || !viewController.yh_viewId || viewController.yher_hasReported) {
  217 + return;
  218 + }
210 NSTimeInterval elapsedTime = 0; 219 NSTimeInterval elapsedTime = 0;
211 -  
212 if ([self isTabViewControllerString:NSStringFromClass([viewController class])]) { 220 if ([self isTabViewControllerString:NSStringFromClass([viewController class])]) {
213 UITabBarController *tabBarVC = [viewController yher_tabBarController]; 221 UITabBarController *tabBarVC = [viewController yher_tabBarController];
214 double didSelectTime = [tabBarVC.tabBar.selectedItem.yh_didSelectTime doubleValue]; 222 double didSelectTime = [tabBarVC.tabBar.selectedItem.yh_didSelectTime doubleValue];
215 - if ([viewController.yher_hasReported isEqualToString:@"1"]) { 223 + if (viewController.yher_hasReported) {
216 return; 224 return;
217 } 225 }
218 if (didSelectTime==0) { 226 if (didSelectTime==0) {
219 elapsedTime = [self eventElapsedTime:viewController.yh_viewId]; 227 elapsedTime = [self eventElapsedTime:viewController.yh_viewId];
220 }else{ 228 }else{
221 elapsedTime = [[NSDate date] timeIntervalSince1970] - didSelectTime; 229 elapsedTime = [[NSDate date] timeIntervalSince1970] - didSelectTime;
222 - viewController.yher_hasReported = @"1"; 230 + viewController.yher_hasReported = YES;
223 } 231 }
224 }else{ 232 }else{
225 elapsedTime = [self eventElapsedTime:viewController.yh_viewId]; 233 elapsedTime = [self eventElapsedTime:viewController.yh_viewId];
@@ -233,13 +241,22 @@ @@ -233,13 +241,22 @@
233 [self.timedEvents removeObjectForKey:viewController.yh_viewId]; 241 [self.timedEvents removeObjectForKey:viewController.yh_viewId];
234 }); 242 });
235 } 243 }
236 -  
237 NSDictionary *param = [YH_EventDataFactory factoryTimeEventDataWithViewController:viewController ElapsedTime:elapsedTime]; 244 NSDictionary *param = [YH_EventDataFactory factoryTimeEventDataWithViewController:viewController ElapsedTime:elapsedTime];
238 -  
239 if (param) { 245 if (param) {
240 [[YH_EventCacheManager sharedInstance] pushPerformanceData:param pointName:YHPN_VIEWCONTROLLER]; 246 [[YH_EventCacheManager sharedInstance] pushPerformanceData:param pointName:YHPN_VIEWCONTROLLER];
241 } 247 }
  248 + viewController.yher_hasReported = YES;
  249 + [self printDebugLoadElapsedTime:elapsedTime controller:viewController info:param];
  250 +}
242 251
  252 +- (void)printDebugLoadElapsedTime:(NSTimeInterval)elapsedTime controller:(UIViewController *)viewController info:(NSDictionary *)info {
  253 +#ifdef DEBUG
  254 + NSLog(@"🤬🤬🤬 \n %@:%@ \n 🤬🤬🤬",NSStringFromClass(viewController.class) ,info);
  255 + //优化目标:1秒内展现
  256 + if (elapsedTime >= 1.f) {
  257 + NSLog(@"🥀🥀🥀 FBI warning:加载速度过慢,急需优化 🥀🥀🥀");
  258 + }
  259 +#endif
243 } 260 }
244 261
245 #pragma mark - webview performance 262 #pragma mark - webview performance