├── .gitignore ├── .travis.yml ├── AMKLaunchTimeProfiler.podspec ├── AMKLaunchTimeProfiler ├── Assets │ └── .gitkeep └── Classes │ ├── Core │ ├── AMKLaunchTimeProfiler.h │ ├── AMKLaunchTimeProfiler.m │ ├── AMKLaunchTimeProfilerConstants.h │ ├── AMKLaunchTimeProfilerConstants.m │ ├── AMKLaunchTimeProfilerLogModel.h │ ├── AMKLaunchTimeProfilerLogModel.m │ ├── AMKLaunchTimeProfilerLogsViewController.h │ ├── AMKLaunchTimeProfilerLogsViewController.m │ ├── UIResponder+AMKLaunchTimeProfiler.h │ └── UIResponder+AMKLaunchTimeProfiler.m │ └── Private │ ├── AMKLaunchTimeProfiler+Private.h │ ├── MFMailComposeViewController+AMKLaunchTimeProfiler.h │ ├── MFMailComposeViewController+AMKLaunchTimeProfiler.m │ ├── NSDate+AMKLaunchTimeProfiler.h │ ├── NSDate+AMKLaunchTimeProfiler.m │ ├── NSNumber+AMKLaunchTimeProfiler.h │ ├── NSNumber+AMKLaunchTimeProfiler.m │ ├── NSString+AMKLaunchTimeProfiler.h │ ├── NSString+AMKLaunchTimeProfiler.m │ ├── UIAlertController+AMKLaunchTimeProfiler.h │ ├── UIAlertController+AMKLaunchTimeProfiler.m │ ├── UIBarButtonItem+AMKLaunchTimeProfiler.h │ ├── UIBarButtonItem+AMKLaunchTimeProfiler.m │ ├── UIDevice+AMKLaunchTimeProfiler.h │ ├── UIDevice+AMKLaunchTimeProfiler.m │ ├── UIImage+AMKLaunchTimeProfiler.h │ ├── UIImage+AMKLaunchTimeProfiler.m │ ├── UINavigationController+AMKLaunchTimeProfiler.h │ ├── UINavigationController+AMKLaunchTimeProfiler.m │ ├── YYCache+AMKLaunchTimeProfiler.h │ └── YYCache+AMKLaunchTimeProfiler.m ├── Example ├── AMKLaunchTimeProfiler.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── AMKLaunchTimeProfiler-Example.xcscheme ├── AMKLaunchTimeProfiler.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── AMKLaunchTimeProfiler │ ├── AMKAppDelegate.h │ ├── AMKAppDelegate.m │ ├── AMKHomeViewController.h │ ├── AMKHomeViewController.m │ ├── AMKLaunchTimeProfiler-Info.plist │ ├── AMKLaunchTimeProfiler-Prefix.pch │ ├── AMKNavigationController.h │ ├── AMKNavigationController.m │ ├── AMKRootViewController.h │ ├── AMKRootViewController.m │ ├── AMKViewController.h │ ├── AMKViewController.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── Podfile └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.assets ├── 14448cdb434da423818f6b819ec96591.jpg ├── 31287f21b40610de7ca62a16bbcbe05f.jpg ├── 4f6414aa477ba5588ffe200f3cec6e7e.jpg ├── ea4fb8a570ac2cf9769654c1ad51da1e.jpg └── fd7727cbed7e363acd8dffdd01f2e860.jpg ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | project.xcworkspace/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | Pods/ 42 | !Podfile 43 | Podfile.lock 44 | 45 | # Carthage 46 | # 47 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 48 | # Carthage/Checkouts 49 | 50 | Carthage/Build 51 | 52 | # fastlane 53 | # 54 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 55 | # screenshots whenever they are needed. 56 | # For more information about the recommended setup visit: 57 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 58 | 59 | fastlane/report.xml 60 | fastlane/Preview.html 61 | fastlane/screenshots 62 | fastlane/test_output 63 | 64 | # Code Injection 65 | # 66 | # After new code Injection tools there's a generated folder /iOSInjectionProject 67 | # https://github.com/johnno1962/injectionforxcode 68 | 69 | iOSInjectionProject/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/AMKLaunchTimeProfiler.xcworkspace -scheme AMKLaunchTimeProfiler-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint AMKLaunchTimeProfiler.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'AMKLaunchTimeProfiler' 11 | s.version = '1.0.0' 12 | s.summary = 'iOS application cold launch time consumption analysis tool.' 13 | s.description = 'iOS application cold launch time consumption analysis tool.' 14 | s.homepage = 'https://github.com/AndyM129/AMKLaunchTimeProfiler' 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { 'MengXinxin' => 'andy_m129@baidu.com' } 17 | s.source = { :git => 'https://github.com/AndyM129/AMKLaunchTimeProfiler.git', :tag => s.version.to_s } 18 | s.ios.deployment_target = '9.0' 19 | s.source_files = 'AMKLaunchTimeProfiler/Classes/**/*.{h,m}' 20 | s.public_header_files = 'AMKLaunchTimeProfiler/Classes/Core/*.h' 21 | s.frameworks = 'UIKit' 22 | s.dependency 'YYCache' 23 | s.dependency 'YYModel' 24 | s.dependency 'Aspects' 25 | s.dependency 'SSZipArchive' 26 | end 27 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/AMKLaunchTimeProfiler/Assets/.gitkeep -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/22. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | /// APP启动耗时分析 13 | /// 14 | /// Xcode13上统计启动时长的变量 DYLD_PRINT_STATISTICS 失效了,故需要自行计算 15 | /// 核心实现参考:https://juejin.cn/post/7070679654566199333/ 16 | @interface AMKLaunchTimeProfiler : NSObject 17 | 18 | #pragma mark - 全局设置 19 | 20 | /// 当前的 启动耗时分析的 调试开关,默认NO 21 | @property(nonatomic, assign, readwrite, class) BOOL debugEnable; 22 | 23 | /// 当前的 启动耗时分析的 日志是否被停止记录,默认NO,当执行 [AMKLaunchTimeProfiler clearAllLogs] 时会修改为 YES 24 | @property(nonatomic, assign, readonly, class) BOOL logDisabled; 25 | 26 | /// 当前的 启动耗时分析的 日志最多保存多少组,默认30 27 | @property(nonatomic, assign, readwrite, class) NSUInteger maxCountOfProfilers; 28 | 29 | /// 邮件发送的收件人 30 | @property(nonatomic, strong, readwrite, nullable, class) NSArray *mailRecipients; 31 | 32 | #pragma mark - 当前实例 33 | 34 | /// 单例 35 | @property(nonatomic, strong, readonly, nonnull, class) AMKLaunchTimeProfiler *sharedInstance; 36 | 37 | /// 当前的 启动耗时分析的 Id 38 | @property(nonatomic, strong, readonly, nonnull) NSString *identifier; 39 | 40 | /// 当前的 启动耗时分析的 版本号 41 | @property(nonatomic, strong, readonly, nonnull) NSString *version; 42 | 43 | #pragma mark - APP信息 44 | 45 | /// 当前工程的 bundleName 46 | @property(nonatomic, strong, readonly, nonnull) NSString *bundleName; 47 | 48 | /// 当前工程的 BundleId 49 | @property(nonatomic, strong, readonly, nonnull) NSString *bundleId; 50 | 51 | /// 当前工程的 ShortVersion 52 | @property(nonatomic, strong, readonly, nonnull) NSString *bundleShortVersion; 53 | 54 | /// 当前工程的 BundleVersion 55 | @property(nonatomic, strong, readonly, nonnull) NSString *bundleVersion; 56 | 57 | /// 当前工程的 ClientVersion 58 | @property(nonatomic, strong, readonly, nonnull) NSString *clientVersion; 59 | 60 | /// 当前的 DeviceVersion 61 | @property(nonatomic, strong, readonly, nonnull) NSString *deviceVersion; 62 | 63 | /// 当前的 DeviceName 64 | @property(nonatomic, strong, readonly, nonnull) NSString *deviceName; 65 | 66 | #pragma mark - 启动耗时信息 67 | 68 | /// 创建进程的时间点 69 | @property(nonatomic, assign, readonly) NSTimeInterval processStartTime; 70 | 71 | /// 执行 main() 函数的时间点 72 | @property(nonatomic, assign, readonly) NSTimeInterval mainTime; 73 | 74 | /// 执行 [AppDelegate -application: didFinishLaunchingWithOptions:] 方法的时间点 75 | @property(nonatomic, assign, readonly) NSTimeInterval didFinishLaunchingTime; 76 | 77 | /// 执行 [AppDelegate.window.rootViewController -viewDidAppear:] 方法的时间点 78 | @property(nonatomic, assign, readonly) NSTimeInterval firstScreenTime; 79 | 80 | /// 阶段一 耗时:pre-main,指的是从用户唤起 App 到 main 函数执行之前的过程,即 mainTime - processStartTime 的差值 81 | /// 82 | /// - 装载APP的可执行文件 83 | /// - 递归加载所有依赖的动态库 84 | /// - 调用map_images进行可执行文件内容的解析和处理 85 | /// - 在load_images中调用call_load_methods,调用所有的Class和Category的 + load方法 86 | /// - 进行各种objc结构初始化(注册Objec类、初始化类对象等等) 87 | /// - 调用C++ 静态初始化器和 _attribute_((constructor))修饰函数 88 | @property(nonatomic, assign, readonly) NSTimeInterval preMainTimeConsuming; 89 | 90 | /// 阶段二 耗时:main,指的是从 Application 初始化到 applicationDidFinishLaunchingWithOptions 执行完,即 didFinishLaunchingTime - mainTime 的差值 91 | /// 92 | /// - main() 93 | /// - UIApplicationMain() 94 | /// - AppDelegate的application: didFinishLaunchingWithOptions:方法 95 | @property(nonatomic, assign, readonly) NSTimeInterval mainTimeConsuming; 96 | 97 | /// 阶段三 耗时:首页渲染,指的是 初始化帧渲染到 viewDidAppear 执行完,即 firstScreenTime - didFinishLaunchingTime 的耗时 98 | /// 99 | /// - 相关业务组件初始化 100 | /// - 基础信息数据同步,首页数据请求 101 | /// - 广告,弹层,一些第三方业务,等 102 | @property(nonatomic, assign, readonly) NSTimeInterval firstScreenTimeConsuming; 103 | 104 | /// 总耗时,即 firstScreenTime - processStartTime 的差值 105 | @property(nonatomic, assign, readonly) NSTimeInterval totalTimeConsuming; 106 | 107 | /// 清空所有logs 108 | + (void)clearAllLogs; 109 | 110 | /// 清空本次启动的所有logs 111 | - (void)clearAllLogs; 112 | 113 | /// 自定义log,推荐使用 AMKLaunchTimeProfilerLog(FORMAT, ...) 114 | - (void)logWithFunction:(const char *_Nullable)function line:(NSInteger)line string:(NSString *_Nullable)format, ...; 115 | 116 | /// APP启动耗时分析描述 117 | - (NSString *_Nullable)description; 118 | 119 | /// APP启动耗时分析描述 - 会高亮显示 timeDelta>=50ms 的日志 120 | - (NSMutableAttributedString *_Nullable)attributedDescription; 121 | 122 | @end 123 | 124 | 125 | #pragma mark - 启动耗时统计信息 126 | 127 | @interface AMKLaunchTimeProfiler (Statistics) 128 | @property(nonatomic, assign, readonly, class) NSInteger profilerCount; 129 | @property(nonatomic, assign, readonly, class) NSTimeInterval preMainTimeConsuming; 130 | @property(nonatomic, assign, readonly, class) NSTimeInterval mainTimeConsuming; 131 | @property(nonatomic, assign, readonly, class) NSTimeInterval firstScreenTimeConsuming; 132 | @property(nonatomic, assign, readonly, class) NSTimeInterval totalTimeConsuming; 133 | @end 134 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/22. 6 | // 7 | 8 | #import "AMKLaunchTimeProfiler.h" 9 | #import "AMKLaunchTimeProfiler+Private.h" 10 | #import "AMKLaunchTimeProfilerLogModel.h" 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | @interface AMKLaunchTimeProfiler () 17 | 18 | // 全局设置 19 | @property(nonatomic, assign, readwrite, class) BOOL logDisabled; 20 | @property(atomic, strong, readonly, nonnull, class) dispatch_queue_t logsCacheQueue; //!< log的持久化队列(串行) 21 | @property(nonatomic, strong, readonly, nonnull, class) UISwipeGestureRecognizer *gestureRecognizer; //!< 添加 轻扫手势,以显示「启动耗时日志」页面,默认 不可用 22 | 23 | // 组件信息 24 | @property(nonatomic, strong, readwrite, nonnull) NSString *identifier; 25 | @property(nonatomic, strong, readwrite, nonnull) NSString *version; 26 | @property(nonatomic, strong, readwrite, nonnull) NSMutableArray *logs; 27 | 28 | // APP信息 29 | @property(nonatomic, strong, readwrite, nonnull) NSString *bundleName; 30 | @property(nonatomic, strong, readwrite, nonnull) NSString *bundleId; 31 | @property(nonatomic, strong, readwrite, nonnull) NSString *bundleShortVersion; 32 | @property(nonatomic, strong, readwrite, nonnull) NSString *bundleVersion; 33 | @property(nonatomic, strong, readwrite, nonnull) NSString *clientVersion; 34 | @property(nonatomic, strong, readwrite, nonnull) NSString *deviceVersion; 35 | @property(nonatomic, strong, readwrite, nonnull) NSString *deviceName; 36 | 37 | // 关键时间点 38 | @property(nonatomic, assign, readwrite) NSTimeInterval processStartTime; 39 | @property(nonatomic, assign, readwrite) NSTimeInterval mainTime; 40 | @property(nonatomic, assign, readwrite) NSTimeInterval didFinishLaunchingTime; 41 | @property(nonatomic, assign, readwrite) NSTimeInterval firstScreenTime; 42 | 43 | // 各阶段耗时 44 | @property(nonatomic, assign, readwrite) NSTimeInterval preMainTimeConsuming; 45 | @property(nonatomic, assign, readwrite) NSTimeInterval mainTimeConsuming; 46 | @property(nonatomic, assign, readwrite) NSTimeInterval firstScreenTimeConsuming; 47 | @property(nonatomic, assign, readwrite) NSTimeInterval totalTimeConsuming; 48 | 49 | @end 50 | 51 | @implementation AMKLaunchTimeProfiler 52 | 53 | void static __attribute__((constructor)) before_main() { 54 | if (AMKLaunchTimeProfiler.sharedInstance.mainTime == 0) { 55 | AMKLaunchTimeProfiler.sharedInstance.mainTime = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; 56 | AMKLaunchTimeProfilerInternalLog(@"main time: %@", @(AMKLaunchTimeProfiler.sharedInstance.mainTime).amkltp_formattedDateStringForSystemTimeZone); 57 | } 58 | } 59 | 60 | NSTimeInterval AMKLaunchTimeProfilerGetProcessStartTime(void) { 61 | if (AMKLaunchTimeProfiler.sharedInstance.processStartTime == 0) { 62 | struct kinfo_proc proc; 63 | int pid = NSProcessInfo.processInfo.processIdentifier; 64 | int cmd[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 65 | size_t size = sizeof(proc); 66 | if (sysctl(cmd, sizeof(cmd)/sizeof(*cmd), &proc, &size, NULL, 0) == 0) { 67 | struct timeval starttime = proc.kp_proc.p_un.__p_starttime; 68 | AMKLaunchTimeProfiler.sharedInstance.processStartTime = [NSString stringWithFormat:@"%ld.%d", starttime.tv_sec /*(s)*/, starttime.tv_usec /*(ms)*/].doubleValue; 69 | AMKLaunchTimeProfilerInternalLog(@"process-start time: %@", @(AMKLaunchTimeProfiler.sharedInstance.processStartTime).amkltp_formattedDateStringForSystemTimeZone); 70 | } 71 | } 72 | return AMKLaunchTimeProfiler.sharedInstance.processStartTime; 73 | } 74 | 75 | #pragma mark - Init Methods 76 | 77 | + (void)initialize { 78 | static dispatch_once_t onceToken; 79 | dispatch_once(&onceToken, ^{ 80 | AMKLaunchTimeProfilerGetProcessStartTime(); 81 | }); 82 | } 83 | 84 | #pragma mark - Getters & Setters 85 | 86 | static BOOL _debugEnable; 87 | 88 | + (BOOL)debugEnable { 89 | return _debugEnable; 90 | } 91 | 92 | + (void)setDebugEnable:(BOOL)debugEnable { 93 | _debugEnable = debugEnable; 94 | self.gestureRecognizer.enabled = _debugEnable; 95 | } 96 | 97 | static BOOL _logDisabled; 98 | 99 | + (BOOL)logDisabled { 100 | return _logDisabled; 101 | } 102 | 103 | + (void)setLogDisabled:(BOOL)logDisabled { 104 | _logDisabled = logDisabled; 105 | } 106 | 107 | + (dispatch_queue_t)logsCacheQueue { 108 | static dispatch_queue_t logsCacheQueue; 109 | static dispatch_once_t onceToken; 110 | dispatch_once(&onceToken, ^{ 111 | NSString *label = [AMKLaunchTimeProfilerBundleId stringByAppendingString:@".logs-cache"]; 112 | logsCacheQueue = dispatch_queue_create(label.UTF8String, DISPATCH_QUEUE_SERIAL); 113 | }); 114 | return logsCacheQueue; 115 | } 116 | 117 | + (UISwipeGestureRecognizer *)gestureRecognizer { 118 | static UISwipeGestureRecognizer *gestureRecognizer; 119 | if (!gestureRecognizer && UIApplication.sharedApplication.delegate.window) { 120 | gestureRecognizer = [UISwipeGestureRecognizer.alloc initWithTarget:self action:@selector(gotoLaunchTimeProfilerLogsViewController:)]; 121 | gestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft; 122 | gestureRecognizer.numberOfTouchesRequired = 2; 123 | gestureRecognizer.enabled = NO; 124 | [UIApplication.sharedApplication.delegate.window addGestureRecognizer:gestureRecognizer]; 125 | } 126 | return gestureRecognizer; 127 | } 128 | 129 | static NSUInteger _maxCountOfProfilers = 30; 130 | 131 | + (NSUInteger)maxCountOfProfilers { 132 | return _maxCountOfProfilers; 133 | } 134 | 135 | + (void)setMaxCountOfProfilers:(NSUInteger)maxCountOfProfilers { 136 | _maxCountOfProfilers = MAX(1, maxCountOfProfilers); 137 | 138 | dispatch_async(self.logsCacheQueue, ^{ 139 | NSMutableArray *profilers = [NSMutableArray arrayWithArray:(NSArray *)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey] ?: @[]]; 140 | if (profilers.count > _maxCountOfProfilers) { 141 | [profilers removeObjectsInRange:NSMakeRange(0, profilers.count-_maxCountOfProfilers)]; 142 | [YYCache.amkltp_sharedInstance setObject:profilers forKey:AMKLaunchTimeProfilersCacheKey]; 143 | } 144 | }); 145 | } 146 | 147 | static NSArray *_mailRecipients; 148 | 149 | + (NSArray *)mailRecipients { 150 | return _mailRecipients; 151 | } 152 | 153 | + (void)setMailRecipients:(NSArray *)mailRecipients { 154 | _mailRecipients = mailRecipients.copy; 155 | } 156 | 157 | + (AMKLaunchTimeProfiler *)sharedInstance { 158 | static AMKLaunchTimeProfiler *sharedInstance; 159 | static dispatch_once_t onceToken; 160 | dispatch_once(&onceToken, ^{ 161 | sharedInstance = [[self alloc] init]; 162 | sharedInstance.identifier = NSProcessInfo.processInfo.globallyUniqueString.amkltp_md5String; 163 | sharedInstance.version = AMKLaunchTimeProfilerVersion; 164 | sharedInstance.bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"]; 165 | sharedInstance.bundleId = NSBundle.mainBundle.bundleIdentifier; 166 | sharedInstance.bundleShortVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"]; 167 | sharedInstance.bundleVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"]; 168 | sharedInstance.clientVersion = NSBundle.mainBundle.infoDictionary[@"sys-clientVersion"]; 169 | sharedInstance.deviceVersion = [NSString stringWithFormat:@"%@ with %@ %@", UIDevice.amkltp_platformString, UIDevice.currentDevice.systemName, UIDevice.currentDevice.systemVersion]; 170 | sharedInstance.deviceName = UIDevice.currentDevice.name; 171 | 172 | dispatch_async(self.logsCacheQueue, ^{ 173 | NSMutableArray *profilers = [NSMutableArray arrayWithArray:(NSArray *)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey] ?: @[]]; 174 | [profilers removeObject:sharedInstance]; 175 | [profilers addObject:sharedInstance]; 176 | [YYCache.amkltp_sharedInstance setObject:profilers forKey:AMKLaunchTimeProfilersCacheKey]; 177 | }); 178 | }); 179 | return sharedInstance; 180 | } 181 | 182 | - (NSTimeInterval)preMainTimeConsuming { 183 | if (!_preMainTimeConsuming && _mainTime && _processStartTime) { 184 | _preMainTimeConsuming = _mainTime - _processStartTime; 185 | } 186 | return _preMainTimeConsuming; 187 | } 188 | 189 | - (NSTimeInterval)mainTimeConsuming { 190 | if (!_mainTimeConsuming && _didFinishLaunchingTime && _mainTime) { 191 | _mainTimeConsuming = _didFinishLaunchingTime - _mainTime; 192 | } 193 | return _mainTimeConsuming; 194 | } 195 | 196 | - (NSTimeInterval)firstScreenTimeConsuming { 197 | if (!_firstScreenTimeConsuming && _firstScreenTime && _didFinishLaunchingTime) { 198 | _firstScreenTimeConsuming = _firstScreenTime - _didFinishLaunchingTime; 199 | } 200 | return _firstScreenTimeConsuming; 201 | } 202 | 203 | - (NSTimeInterval)totalTimeConsuming { 204 | if (!_totalTimeConsuming && _firstScreenTime && _processStartTime) { 205 | _totalTimeConsuming = _firstScreenTime - _processStartTime; 206 | } 207 | return _totalTimeConsuming; 208 | } 209 | 210 | - (NSMutableArray *)logs { 211 | if (!_logs) { 212 | _logs = @[].mutableCopy; 213 | } 214 | return _logs; 215 | } 216 | 217 | #pragma mark - Data & Networking 218 | 219 | #pragma mark - Public Methods 220 | 221 | + (void)clearAllLogs { 222 | AMKLaunchTimeProfiler.logDisabled = YES; 223 | # pragma clang diagnostic push 224 | # pragma clang diagnostic ignored "-Wundeclared-selector" 225 | [AMKLaunchTimeProfiler performSelector:@selector(clearStatistics)]; 226 | # pragma clang diagnostic pop 227 | [YYCache.amkltp_sharedInstance removeObjectForKey:AMKLaunchTimeProfilersCacheKey]; 228 | } 229 | 230 | - (void)clearAllLogs { 231 | [self.logs removeAllObjects]; 232 | 233 | dispatch_async(self.class.logsCacheQueue, ^{ 234 | NSMutableArray *profilers = [NSMutableArray arrayWithArray:(NSArray *)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey] ?: @[]]; 235 | [profilers removeObject:self]; 236 | [profilers addObject:self]; 237 | [YYCache.amkltp_sharedInstance setObject:profilers forKey:AMKLaunchTimeProfilersCacheKey]; 238 | }); 239 | } 240 | 241 | - (void)logWithFunction:(const char *)function line:(NSInteger)line string:(NSString *)format, ... { 242 | static NSTimeInterval lastLogTimeInterval = 0; 243 | if (AMKLaunchTimeProfiler.logDisabled) return; 244 | 245 | va_list arguments; 246 | va_start(arguments, format); 247 | NSString *string = [NSString.alloc initWithFormat:format arguments:arguments]; 248 | va_end(arguments); 249 | 250 | // 构建日志 251 | AMKLaunchTimeProfilerLogModel *logModel = [AMKLaunchTimeProfilerLogModel.alloc init]; 252 | logModel.timeInterval = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 - self.processStartTime; 253 | logModel.timeDelta = logModel.timeInterval - lastLogTimeInterval; 254 | logModel.function = [NSString stringWithCString:function ?: "__INTERNAL__" encoding:NSUTF8StringEncoding]; 255 | logModel.line = line; 256 | logModel.string = string; 257 | lastLogTimeInterval = logModel.timeInterval; 258 | 259 | // 打印日志 260 | [self debugLog:@"%@", logModel.description]; 261 | 262 | // 缓存日志 263 | [self.logs addObject:logModel]; 264 | dispatch_async(self.class.logsCacheQueue, ^{ 265 | NSMutableArray *profilers = [NSMutableArray arrayWithArray:(NSArray *)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey] ?: @[]]; 266 | [profilers removeObject:self]; 267 | [profilers addObject:self]; 268 | if (!profilers) printf("❌❌❌❌❌❌❌❌❌❌"); 269 | [YYCache.amkltp_sharedInstance setObject:profilers?:@[] forKey:AMKLaunchTimeProfilersCacheKey]; 270 | }); 271 | } 272 | 273 | #pragma mark - Private Methods 274 | 275 | + (void)gotoLaunchTimeProfilerLogsViewController:(id)sender { 276 | [AMKLaunchTimeProfilerLogsViewController.new presentingWithAnimated:YES completion:nil]; 277 | } 278 | 279 | - (void)printAnalysis { 280 | AMKLaunchTimeProfilerInternalLog(@"----------------------------------------------------------------------"); 281 | AMKLaunchTimeProfilerInternalLog(@"total time consuming: %.3f s", self.totalTimeConsuming); 282 | AMKLaunchTimeProfilerInternalLog(@"pre-main time consuming: %.3f s (%.2f%%)", self.preMainTimeConsuming, self.preMainTimeConsuming/self.totalTimeConsuming*100); 283 | AMKLaunchTimeProfilerInternalLog(@"main time consuming: %.3f s (%.2f%%)", self.mainTimeConsuming, self.mainTimeConsuming/self.totalTimeConsuming*100); 284 | AMKLaunchTimeProfilerInternalLog(@"first screen time consuming: %.3f s (%.2f%%)", self.firstScreenTimeConsuming, self.firstScreenTimeConsuming/self.totalTimeConsuming*100); 285 | AMKLaunchTimeProfilerInternalLog(@"----------------------------------------------------------------------"); 286 | } 287 | 288 | #pragma mark - Notifications 289 | 290 | #pragma mark - KVO 291 | 292 | #pragma mark - Protocol 293 | 294 | #pragma mark NSCoding 295 | 296 | - (void)encodeWithCoder:(NSCoder *)aCoder { 297 | [self yy_modelEncodeWithCoder:aCoder]; 298 | } 299 | 300 | - (id)initWithCoder:(NSCoder *)aDecoder { 301 | self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; 302 | } 303 | 304 | #pragma mark NSCopying 305 | 306 | - (id)copyWithZone:(NSZone *)zone { 307 | return [self yy_modelCopy]; 308 | } 309 | 310 | #pragma mark - Overrides 311 | 312 | - (NSUInteger)hash { 313 | return [self yy_modelHash]; 314 | } 315 | 316 | - (BOOL)isEqual:(id)object { 317 | return [object isKindOfClass:AMKLaunchTimeProfiler.class] && [self.identifier isEqualToString:[object identifier]]; 318 | } 319 | 320 | - (NSString *)description { 321 | return [self attributedDescription:NO].string; 322 | } 323 | 324 | - (NSMutableAttributedString *)attributedDescription { 325 | return [self attributedDescription:YES]; 326 | } 327 | 328 | - (NSMutableAttributedString *)attributedDescription:(BOOL)attributed { 329 | NSMutableAttributedString *attributedDescription = [NSMutableAttributedString.alloc init]; 330 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"======================================================================"]]]; 331 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nAMKLaunchTimeProfiler id: %@", self.identifier]]]; 332 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nAMKLaunchTimeProfiler version: %@", self.version]]]; 333 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nBundle id: %@", self.bundleId]]]; 334 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nBundle short Version: %@", self.bundleShortVersion]]]; 335 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nBundle version: %@", self.bundleVersion]]]; 336 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nClient Version: %@", self.clientVersion]]]; 337 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nDevice Version: %@", self.deviceVersion]]]; 338 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\nDevice Name: %@", self.deviceName]]]; 339 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n"]]]; 340 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n----------------------------------------------------------------------"]]]; 341 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n%30s: %@", "process-start time", @(self.processStartTime).amkltp_formattedDateStringForSystemTimeZone]]]; 342 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n%30s: %.3f s ", "total time consuming", self.totalTimeConsuming]]]; 343 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n%30s: %.3f s (%.2f%%)", "pre-main time consuming", self.preMainTimeConsuming, self.preMainTimeConsuming/self.totalTimeConsuming*100]]]; 344 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n%30s: %.3f s (%.2f%%)", "main time consuming", self.mainTimeConsuming, self.mainTimeConsuming/self.totalTimeConsuming*100]]]; 345 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n%30s: %.3f s (%.2f%%)", "first screen time consuming", self.firstScreenTimeConsuming, self.firstScreenTimeConsuming/self.totalTimeConsuming*100]]]; 346 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n"]]]; 347 | [attributedDescription appendAttributedString:[NSAttributedString.alloc initWithString:[NSString stringWithFormat:@"\n----------------------------------------------------------------------"]]]; 348 | [self.logs enumerateObjectsUsingBlock:^(AMKLaunchTimeProfilerLogModel * _Nonnull log, NSUInteger idx, BOOL * _Nonnull stop) { 349 | NSString *string = [NSString stringWithFormat:@"\n%@", log.description]; 350 | NSDictionary *attributes = (log.timeInterval=0.05 && ![log.function isEqualToString:@"__INTERNAL__"]) ? @{NSForegroundColorAttributeName: UIColor.redColor} : nil; 351 | NSAttributedString *attributedString = [NSAttributedString.alloc initWithString:string attributes:attributes]; 352 | [attributedDescription appendAttributedString:attributedString]; 353 | }]; 354 | return attributedDescription; 355 | } 356 | 357 | - (NSString *)debugDescription { 358 | return [self yy_modelDescription]; 359 | } 360 | 361 | #pragma mark - Helper Methods 362 | 363 | - (void)debugLog:(NSString *)format, ... { 364 | # if defined(DEBUG) 365 | va_list arguments; 366 | va_start(arguments, format); 367 | NSString *string = [NSString.alloc initWithFormat:format arguments:arguments]; 368 | va_end(arguments); 369 | fprintf(stderr, "%s\n", string.UTF8String); 370 | # endif 371 | } 372 | 373 | @end 374 | 375 | 376 | #pragma mark - 启动耗时统计信息 377 | 378 | @implementation AMKLaunchTimeProfiler (Statistics) 379 | static NSInteger profilerCount = 0; 380 | static NSTimeInterval preMainTimeConsuming = 0; 381 | static NSTimeInterval mainTimeConsuming = 0; 382 | static NSTimeInterval firstScreenTimeConsuming = 0; 383 | static NSTimeInterval totalTimeConsuming = 0; 384 | 385 | + (void)statistics { 386 | static dispatch_once_t onceToken; 387 | dispatch_once(&onceToken, ^{ 388 | NSArray *profilers = [NSArray arrayWithArray:(NSArray *)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey] ?: @[]]; 389 | profilers = [profilers objectsAtIndexes:[profilers indexesOfObjectsPassingTest:^BOOL(AMKLaunchTimeProfiler * _Nonnull profiler, NSUInteger idx, BOOL * _Nonnull stop) { 390 | if (profiler.preMainTimeConsuming <= 0) return NO; 391 | if (profiler.mainTimeConsuming <= 0) return NO; 392 | if (profiler.firstScreenTimeConsuming <= 0) return NO; 393 | if (profiler.totalTimeConsuming <= 0) return NO; 394 | return YES; 395 | }]]; 396 | if (!profilers.count) return; 397 | 398 | [profilers enumerateObjectsUsingBlock:^(AMKLaunchTimeProfiler * _Nonnull profiler, NSUInteger idx, BOOL * _Nonnull stop) { 399 | preMainTimeConsuming += profiler.preMainTimeConsuming; 400 | mainTimeConsuming += profiler.mainTimeConsuming; 401 | firstScreenTimeConsuming += profiler.firstScreenTimeConsuming; 402 | totalTimeConsuming += profiler.totalTimeConsuming; 403 | }]; 404 | profilerCount = profilers.count; 405 | preMainTimeConsuming = preMainTimeConsuming / profilerCount; 406 | mainTimeConsuming = mainTimeConsuming / profilerCount; 407 | firstScreenTimeConsuming = firstScreenTimeConsuming / profilerCount; 408 | totalTimeConsuming = totalTimeConsuming / profilerCount; 409 | }); 410 | } 411 | 412 | + (void)clearStatistics { 413 | profilerCount = 0; 414 | preMainTimeConsuming = 0; 415 | mainTimeConsuming = 0; 416 | firstScreenTimeConsuming = 0; 417 | totalTimeConsuming = 0; 418 | } 419 | 420 | + (NSInteger)profilerCount { 421 | [self statistics]; 422 | return profilerCount; 423 | } 424 | 425 | + (NSTimeInterval)preMainTimeConsuming { 426 | [self statistics]; 427 | return preMainTimeConsuming; 428 | } 429 | 430 | + (NSTimeInterval)mainTimeConsuming { 431 | [self statistics]; 432 | return mainTimeConsuming; 433 | } 434 | 435 | + (NSTimeInterval)firstScreenTimeConsuming { 436 | [self statistics]; 437 | return firstScreenTimeConsuming; 438 | } 439 | 440 | + (NSTimeInterval)totalTimeConsuming { 441 | [self statistics]; 442 | return totalTimeConsuming; 443 | } 444 | 445 | + (NSString *)description { 446 | if (!self.profilerCount) return @"暂无有效数据"; 447 | 448 | NSMutableString *description = [NSMutableString.alloc initWithFormat:@"%ld 次有效数据的平均数:", (long)self.profilerCount]; 449 | [description appendFormat:@"\n--------------------------------------------------"]; 450 | [description appendFormat:@"\n%30s: %.3f s ", "total time consuming", self.totalTimeConsuming]; 451 | [description appendFormat:@"\n%30s: %.3f s (%.2f%%)", "pre-main time consuming", self.preMainTimeConsuming, self.preMainTimeConsuming/self.totalTimeConsuming*100]; 452 | [description appendFormat:@"\n%30s: %.3f s (%.2f%%)", "main time consuming", self.mainTimeConsuming, self.mainTimeConsuming/self.totalTimeConsuming*100]; 453 | [description appendFormat:@"\n%30s: %.3f s (%.2f%%)", "first screen time consuming", self.firstScreenTimeConsuming, self.firstScreenTimeConsuming/self.totalTimeConsuming*100]; 454 | return description; 455 | } 456 | 457 | @end 458 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerConstants.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/22. 6 | // 7 | 8 | #import 9 | @class AMKLaunchTimeProfiler; 10 | 11 | #pragma mark - 相关变量定义 12 | 13 | /// 组件包名 14 | UIKIT_EXTERN NSString * _Nonnull const AMKLaunchTimeProfilerBundleId; 15 | 16 | /// 组件 ErrorDomain 17 | UIKIT_EXTERN NSString * _Nonnull const AMKLaunchTimeProfilerErrorDomain; 18 | 19 | /// 组件版本号 20 | UIKIT_EXTERN NSString * _Nonnull const AMKLaunchTimeProfilerVersion; 21 | 22 | /// 弹窗标题 23 | UIKIT_EXTERN NSString * _Nonnull const AMKLaunchTimeProfilerAlertControllerTitle; 24 | 25 | /// 缓存Key 26 | UIKIT_EXTERN NSString * _Nonnull const AMKLaunchTimeProfilersCacheKey; 27 | 28 | /// 主题色 29 | #define AMKLaunchTimeProfilerTintColor [UIColor colorWithRed:70/255.0 green:74/255.0 blue:250/255.0 alpha:1.0] 30 | 31 | #pragma mark - 相关通知定义 32 | 33 | /// 冷启动耗时分析通知 34 | typedef NSString *_Nonnull AMKLaunchTimeProfilerNotificationName NS_EXTENSIBLE_STRING_ENUM; 35 | 36 | /// 冷启动耗时分析通知:APP完成加载 37 | FOUNDATION_EXPORT AMKLaunchTimeProfilerNotificationName const AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification; 38 | 39 | /// 冷启动耗时分析通知:首屏完成显示 40 | FOUNDATION_EXPORT AMKLaunchTimeProfilerNotificationName const AMKLaunchTimeProfilerFirstScreenDidDisplayNotification; 41 | 42 | #pragma mark - 相关函数定义 43 | 44 | /// 打印日志 45 | #define AMKLaunchTimeProfilerLog(FORMAT, ...) \ 46 | [AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:FORMAT, ##__VA_ARGS__] 47 | 48 | /// 打印日志 - @"开始..." 49 | #define AMKLaunchTimeProfilerLogBegin(FORMAT, ...) \ 50 | [AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:@"开始..." FORMAT, ##__VA_ARGS__] 51 | 52 | /// 打印日志 - @"结束" 53 | #define AMKLaunchTimeProfilerLogEnd(FORMAT, ...) \ 54 | [AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:@"结束" FORMAT, ##__VA_ARGS__] 55 | 56 | /// 打印日志 - 内部日志 57 | #define AMKLaunchTimeProfilerInternalLog(FORMAT, ...) \ 58 | [AMKLaunchTimeProfiler.sharedInstance logWithFunction:0 line:0 string:FORMAT, ##__VA_ARGS__] 59 | 60 | /// 打印日志 - 相同位置 只打一次 61 | #define AMKLaunchTimeProfilerOnceLog(FORMAT, ...) \ 62 | { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{[AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:FORMAT, ##__VA_ARGS__];}); } 63 | 64 | /// 打印日志 - 相同位置 只打一次 - @"开始..." 65 | #define AMKLaunchTimeProfilerOnceLogBegin(FORMAT, ...) \ 66 | { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{[AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:@"开始..." FORMAT, ##__VA_ARGS__];}); } 67 | 68 | /// 打印日志 - 相同位置 只打一次 - @"结束" 69 | #define AMKLaunchTimeProfilerOnceLogEnd(FORMAT, ...) \ 70 | { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{[AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:@"结束" FORMAT, ##__VA_ARGS__];}); } 71 | 72 | #pragma mark - 其他 73 | 74 | #define AMKSharedLaunchTimeProfiler AMKLaunchTimeProfiler.sharedInstance 75 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerConstants.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/22. 6 | // 7 | 8 | #import "AMKLaunchTimeProfilerConstants.h" 9 | #import "AMKLaunchTimeProfiler.h" 10 | #import "AMKLaunchTimeProfiler+Private.h" 11 | 12 | #pragma mark - 相关变量定义 13 | 14 | Class AMKLaunchTimeProfilerApplicationDelegateClass; 15 | 16 | NSString * _Nonnull const AMKLaunchTimeProfilerBundleId = @"io.github.andym129.amk-launch-time-profiler"; 17 | NSString * _Nonnull const AMKLaunchTimeProfilerErrorDomain = @"io.github.andym129.amk-launch-time-profiler.error"; 18 | NSString * _Nonnull const AMKLaunchTimeProfilerVersion = @"1.0.0"; 19 | NSString * _Nonnull const AMKLaunchTimeProfilerAlertControllerTitle = @"AMKLaunchTimeProfiler\n—— APP冷启动耗时分析 ——"; 20 | NSString * _Nonnull const AMKLaunchTimeProfilersCacheKey = @"profilers"; 21 | 22 | #pragma mark - 相关通知定义 23 | 24 | AMKLaunchTimeProfilerNotificationName const AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification = @"AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification"; 25 | AMKLaunchTimeProfilerNotificationName const AMKLaunchTimeProfilerFirstScreenDidDisplayNotification = @"AMKLaunchTimeProfilerFirstScreenDidDisplayNotification"; 26 | 27 | #pragma mark - 相关函数定义 28 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerLogModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerLogModel.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import 9 | 10 | /// Log 的相关信息 11 | @interface AMKLaunchTimeProfilerLogModel : NSObject 12 | @property (nonatomic, assign, readwrite) NSTimeInterval timeDelta; //!< 与前一条log的时间差,便于查看具体耗时 13 | @property (nonatomic, assign, readwrite) NSTimeInterval timeInterval; //!< 相对时间 14 | @property (nonatomic, strong, readwrite) NSString *function; //!< 所在方法 15 | @property (nonatomic, assign, readwrite) NSInteger line; //!< 对应行数 16 | @property (nonatomic, strong, readwrite) NSString *string; //!< 内容 17 | @end 18 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerLogModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerLogModel.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import "AMKLaunchTimeProfilerLogModel.h" 9 | #import 10 | 11 | @implementation AMKLaunchTimeProfilerLogModel 12 | 13 | #pragma mark - Init Methods 14 | 15 | - (void)dealloc { 16 | 17 | } 18 | 19 | - (instancetype)init { 20 | if (self = [super init]) { 21 | 22 | } 23 | return self; 24 | } 25 | 26 | #pragma mark - Getters & Setters 27 | 28 | - (NSString *)description { 29 | NSMutableString *description = [NSMutableString stringWithFormat:@"⏱ +%.3f s ~Δ %4.0f ms >>", self.timeInterval, self.timeDelta*1000]; 30 | if (self.function) [description appendFormat:@" %@", self.function]; 31 | if (self.line) [description appendFormat:@" Line %ld", (long)self.line]; 32 | if (self.function || self.line) [description appendFormat:@":"]; 33 | [description appendFormat:@" %@", self.string?:@""]; 34 | return description; 35 | } 36 | 37 | #pragma mark - Data & Networking 38 | 39 | #pragma mark - Public Methods 40 | 41 | #pragma mark - Private Methods 42 | 43 | #pragma mark - Notifications 44 | 45 | #pragma mark - KVO 46 | 47 | #pragma mark - Protocol 48 | 49 | #pragma mark NSCoding 50 | 51 | - (void)encodeWithCoder:(NSCoder *)aCoder { 52 | [self yy_modelEncodeWithCoder:aCoder]; 53 | } 54 | 55 | - (id)initWithCoder:(NSCoder *)aDecoder { 56 | self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; 57 | } 58 | 59 | #pragma mark NSCopying 60 | 61 | - (id)copyWithZone:(NSZone *)zone { 62 | return [self yy_modelCopy]; 63 | } 64 | 65 | #pragma mark - Overrides 66 | 67 | - (NSUInteger)hash { 68 | return [self yy_modelHash]; 69 | } 70 | 71 | - (BOOL)isEqual:(id)object { 72 | return [self yy_modelIsEqual:object]; 73 | } 74 | 75 | - (NSString *)debugDescription { 76 | return [self yy_modelDescription]; 77 | } 78 | 79 | #pragma mark - Helper Methods 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerLogsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerLogsViewController.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import 9 | 10 | /// APP启动耗时分析 - 日志列表 11 | @interface AMKLaunchTimeProfilerLogsViewController : UIViewController 12 | 13 | - (void)presentingWithAnimated:(BOOL)flag completion:(void (^_Nullable)(void))completion; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/AMKLaunchTimeProfilerLogsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerLogsViewController.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import "AMKLaunchTimeProfilerLogsViewController.h" 9 | #import "AMKLaunchTimeProfiler.h" 10 | #import "AMKLaunchTimeProfiler+Private.h" 11 | #import 12 | #import 13 | #import 14 | #import 15 | #if defined(WK_TARGET_DEV) || defined(WK_TARGET_QA) || defined(WK_TARGET_ONLINE_QA) || defined(WK_TARGET_BETA) || defined(WK_TARGET_ONLINE) 16 | #import 17 | #else 18 | #import 19 | #endif 20 | 21 | @interface AMKLaunchTimeProfilerLogsViewController () 22 | @property(nonatomic, strong, readwrite, nullable) UIBarButtonItem *closeBarButtonItem; 23 | @property(nonatomic, strong, readwrite, nullable) UIBarButtonItem *filterBarButtonItem; 24 | @property(nonatomic, strong, readwrite, nullable) UIBarButtonItem *moreBarButtonItem; 25 | @property(nonatomic, strong, readwrite, nullable) UITextView *textView; 26 | @property(nonatomic, strong, readwrite, nullable) UITextView *statisticsTextView; 27 | @end 28 | 29 | @implementation AMKLaunchTimeProfilerLogsViewController 30 | 31 | #pragma mark - Dealloc 32 | 33 | - (void)dealloc { 34 | 35 | } 36 | 37 | #pragma mark - Init Methods 38 | 39 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 40 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 41 | self.title = @"冷启动耗时统计"; 42 | } 43 | return self; 44 | } 45 | 46 | #pragma mark - Life Circle 47 | 48 | - (void)viewDidLoad { 49 | [super viewDidLoad]; 50 | self.navigationItem.leftBarButtonItems = @[self.closeBarButtonItem]; 51 | self.navigationItem.rightBarButtonItems = @[self.moreBarButtonItem/*, self.filterBarButtonItem*/]; 52 | self.view.backgroundColor = self.view.backgroundColor?:[UIColor whiteColor]; 53 | [self reloadLogs:nil]; 54 | } 55 | 56 | - (void)viewWillAppear:(BOOL)animated { 57 | [super viewWillAppear:animated]; 58 | } 59 | 60 | - (void)viewDidAppear:(BOOL)animated { 61 | [super viewDidAppear:animated]; 62 | } 63 | 64 | - (void)viewWillDisappear:(BOOL)animated { 65 | [super viewWillDisappear:animated]; 66 | } 67 | 68 | - (void)viewDidDisappear:(BOOL)animated { 69 | [super viewDidDisappear:animated]; 70 | } 71 | 72 | #pragma mark - Getters & Setters 73 | 74 | - (UIBarButtonItem *)closeBarButtonItem { 75 | if (!_closeBarButtonItem) { 76 | _closeBarButtonItem = [UIBarButtonItem.alloc amkltp_initWithImageType:AMKLaunchTimeProfilerImageTypeNaviBarClose target:self action:@selector(closeBarButtonItemClicked:)]; 77 | } 78 | return _closeBarButtonItem; 79 | } 80 | 81 | - (UIBarButtonItem *)filterBarButtonItem { 82 | if (!_filterBarButtonItem) { 83 | _filterBarButtonItem = [UIBarButtonItem.alloc amkltp_initWithImageType:AMKLaunchTimeProfilerImageTypeNaviBarFilter target:self action:@selector(filterBarButtonItemClicked:)]; 84 | [(UIButton *)_filterBarButtonItem.customView setImage:[[UIImage amkltp_imageWithType:AMKLaunchTimeProfilerImageTypeNaviBarFilterSelected] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateSelected]; 85 | } 86 | return _filterBarButtonItem; 87 | } 88 | 89 | - (UIBarButtonItem *)moreBarButtonItem { 90 | if (!_moreBarButtonItem) { 91 | _moreBarButtonItem = [UIBarButtonItem.alloc amkltp_initWithImageType:AMKLaunchTimeProfilerImageTypeNaviBarMore target:self action:@selector(moreBarButtonItemClicked:)]; 92 | } 93 | return _moreBarButtonItem; 94 | } 95 | 96 | - (UITextView *)textView { 97 | if (!_textView) { 98 | _textView = [UITextView.alloc init]; 99 | _textView.font = [UIFont systemFontOfSize:6]; 100 | _textView.minimumZoomScale = 4 / _textView.font.pointSize; 101 | _textView.maximumZoomScale = 25 / _textView.font.pointSize; 102 | _textView.zoomScale = 1; 103 | _textView.editable = NO; 104 | _textView.selectable = YES; 105 | [self.view addSubview:_textView]; 106 | } 107 | return _textView; 108 | } 109 | 110 | - (UITextView *)statisticsTextView { 111 | if (!_statisticsTextView) { 112 | _statisticsTextView = [UITextView.alloc init]; 113 | _statisticsTextView.font = [UIFont systemFontOfSize:13]; 114 | _statisticsTextView.minimumZoomScale = 4 / _textView.font.pointSize; 115 | _statisticsTextView.maximumZoomScale = 25 / _textView.font.pointSize; 116 | _statisticsTextView.zoomScale = 1; 117 | _statisticsTextView.editable = NO; 118 | _statisticsTextView.selectable = YES; 119 | _statisticsTextView.backgroundColor = [UIColor colorWithRed:243/255.0 green:243/255.0 blue:243/255.0 alpha:1.0]; 120 | [self.view addSubview:_statisticsTextView]; 121 | } 122 | return _statisticsTextView; 123 | } 124 | 125 | #pragma mark - Data & Networking 126 | 127 | - (void)reloadLogs:(id)sender { 128 | NSArray *profilers = (id)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey]; 129 | if (![profilers isKindOfClass:NSArray.class]) { 130 | self.textView.text = AMKLaunchTimeProfiler.logDisabled ? @"⚠️ 已禁用日志" : [NSString stringWithFormat:@"❌ 日志加载异常:%@", profilers]; 131 | self.statisticsTextView.text = AMKLaunchTimeProfiler.description; 132 | return; 133 | } 134 | 135 | __block NSMutableAttributedString *attributedText = [NSMutableAttributedString.alloc init]; 136 | [profilers enumerateObjectsUsingBlock:^(AMKLaunchTimeProfiler * _Nonnull profiler, NSUInteger idx, BOOL * _Nonnull stop) { 137 | [attributedText appendAttributedString:profiler.attributedDescription]; 138 | [attributedText appendAttributedString:[NSAttributedString.alloc initWithString:@"\n\n\n\n\n\n\n\n\n\n"]]; 139 | }]; 140 | [attributedText addAttributes:@{NSFontAttributeName: self.textView.font} range:NSMakeRange(0, attributedText.length)]; 141 | 142 | self.textView.attributedText = attributedText.length ? attributedText : [NSAttributedString.alloc initWithString:@"暂无"]; 143 | self.statisticsTextView.text = AMKLaunchTimeProfiler.description; 144 | [self scrollToBottomAfterDelay:0.1 animated:YES]; 145 | } 146 | 147 | - (void)copyLogs:(id)sender { 148 | UIPasteboard.generalPasteboard.string = [NSString stringWithFormat:@"%@\n\n\n\n\n%@", self.textView.text, self.statisticsTextView.text]; 149 | } 150 | 151 | - (void)clearLogs:(id)sender { 152 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:@"确认清空所有日志吗?\n(此次启动时的相关日志也将被丢弃,并停止记录)" preferredStyle:UIAlertControllerStyleAlert]; 153 | [alertController addAction:[UIAlertAction actionWithTitle:@"清空" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { 154 | [AMKLaunchTimeProfiler clearAllLogs]; 155 | [self reloadLogs:sender]; 156 | }]]; 157 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 158 | [self presentViewController:alertController animated:YES completion:nil]; 159 | } 160 | 161 | - (void)sendLogs:(id)sender { 162 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:@"请选择发送的方式" preferredStyle:UIAlertControllerStyleActionSheet]; 163 | [alertController addAction:[UIAlertAction actionWithTitle:@"邮件" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 164 | [self sendLogsWithMail:sender]; 165 | }]]; 166 | [alertController addAction:[UIAlertAction actionWithTitle:@"其他" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 167 | [self sendLogsWithThirdApp:sender]; 168 | }]]; 169 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 170 | [self presentViewController:alertController animated:YES completion:nil]; 171 | } 172 | 173 | - (void)sendLogsWithThirdApp:(id)sender { 174 | NSError *error = nil; 175 | 176 | // 创建临时目录 177 | NSMutableString *tempDirPath = NSTemporaryDirectory().mutableCopy; 178 | [tempDirPath appendFormat:@"/%@", NSProcessInfo.processInfo.globallyUniqueString]; 179 | [tempDirPath appendFormat:@"/%@", AMKLaunchTimeProfiler.sharedInstance.bundleName]; 180 | [tempDirPath appendFormat:@" v%@", AMKLaunchTimeProfiler.sharedInstance.bundleShortVersion]; 181 | [tempDirPath appendFormat:@" (%@)", AMKLaunchTimeProfiler.sharedInstance.bundleVersion]; 182 | [tempDirPath appendFormat:@" && %@", AMKLaunchTimeProfiler.sharedInstance.deviceVersion]; 183 | [tempDirPath appendFormat:@" && C %ld", (long)AMKLaunchTimeProfiler.profilerCount]; 184 | [tempDirPath appendFormat:@" - T %.3f s", AMKLaunchTimeProfiler.sharedInstance.totalTimeConsuming]; 185 | [tempDirPath appendFormat:@" - P %.3f s", AMKLaunchTimeProfiler.sharedInstance.preMainTimeConsuming]; 186 | [tempDirPath appendFormat:@" - M %.3f s", AMKLaunchTimeProfiler.sharedInstance.mainTimeConsuming]; 187 | [tempDirPath appendFormat:@" - F %.3f s", AMKLaunchTimeProfiler.sharedInstance.firstScreenTimeConsuming]; 188 | [tempDirPath appendFormat:@" && %@", AMKLaunchTimeProfiler.sharedInstance.identifier]; 189 | NSURL *tempDirURL = [NSURL fileURLWithPath:tempDirPath isDirectory:YES]; 190 | [NSFileManager.defaultManager createDirectoryAtURL:tempDirURL withIntermediateDirectories:YES attributes:nil error:&error]; 191 | 192 | // 导出到文件 193 | if (!error) { 194 | NSString *txtFile = [tempDirPath stringByAppendingFormat:@"/AMKLaunchTimeProfiler_%@.txt", AMKLaunchTimeProfiler.sharedInstance.identifier]; 195 | [self.statisticsTextView.text writeToFile:txtFile atomically:YES encoding:NSUTF8StringEncoding error:&error]; 196 | } 197 | if (!error) { 198 | NSString *logsFile = [tempDirPath stringByAppendingFormat:@"/AMKLaunchTimeProfiler_%@.log", AMKLaunchTimeProfiler.sharedInstance.identifier]; 199 | [self.textView.text writeToFile:logsFile atomically:YES encoding:NSUTF8StringEncoding error:&error]; 200 | } 201 | if (!error) { 202 | NSArray *profilers = (id)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey]; 203 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[profilers yy_modelToJSONObject]?:@[] options:NSJSONWritingPrettyPrinted error:nil]; 204 | NSString *jsonString = [NSString.alloc initWithData:jsonData encoding:NSUTF8StringEncoding]; 205 | NSString *jsonFile = [tempDirPath stringByAppendingFormat:@"/AMKLaunchTimeProfiler_%@.json", AMKLaunchTimeProfiler.sharedInstance.identifier]; 206 | [jsonString?:@"" writeToFile:jsonFile atomically:YES encoding:NSUTF8StringEncoding error:&error]; 207 | } 208 | 209 | // 压缩文件 210 | NSString *zipFilePath = [tempDirPath stringByAppendingFormat:@".zip"]; 211 | BOOL zipSuccess = [SSZipArchive createZipFileAtPath:zipFilePath withContentsOfDirectory:tempDirPath]; 212 | if (!zipSuccess) error = [NSError errorWithDomain:AMKLaunchTimeProfilerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey:@"文件压缩失败"}]; 213 | 214 | // 异常处理 215 | if (error) { 216 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:[NSString stringWithFormat:@"日志导出失败\n%@", error.localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; 217 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 218 | [self presentViewController:alertController animated:YES completion:nil]; 219 | return; 220 | } 221 | 222 | // 发送 223 | NSURL *URL = [NSURL fileURLWithPath:zipFilePath]; 224 | UIActivityViewController *activityViewController = [UIActivityViewController.alloc initWithActivityItems:@[URL] applicationActivities:nil]; 225 | [self presentViewController:activityViewController animated:YES completion:nil]; 226 | } 227 | 228 | - (void)sendLogsWithMail:(id)sender { 229 | // 功能验证 230 | if (!MFMailComposeViewController.canSendMail) { 231 | NSString *message = @"无法发送邮件,请在系统邮件应用中设置邮箱账号"; 232 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:message preferredStyle:UIAlertControllerStyleAlert]; 233 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 234 | [self presentViewController:alertController animated:YES completion:nil]; 235 | return; 236 | } 237 | 238 | // 邮件主题 239 | NSMutableString *subject = [NSMutableString stringWithFormat:@"【APP冷启动耗时统计反馈】"]; 240 | [subject appendFormat:@"来自:%@", AMKLaunchTimeProfiler.sharedInstance.bundleName]; 241 | [subject appendFormat:@" v%@", AMKLaunchTimeProfiler.sharedInstance.bundleShortVersion]; 242 | [subject appendFormat:@" (%@)", AMKLaunchTimeProfiler.sharedInstance.bundleVersion]; 243 | [subject appendFormat:@" && %@", AMKLaunchTimeProfiler.sharedInstance.deviceVersion]; 244 | 245 | // 邮件内容 246 | NSMutableString *messageBody = @"Hi:".mutableCopy; 247 | [messageBody appendFormat:@"\n\n我正在使用「AMKLaunchTimeProfiler」来统计APP冷启动耗时情况,数据信息如下,详见附件。"]; 248 | [messageBody appendFormat:@"\n\n"]; 249 | [messageBody appendFormat:@"\nAMKLaunchTimeProfiler 信息:"]; 250 | [messageBody appendFormat:@"\n--------------------------------------------------"]; 251 | [messageBody appendFormat:@"\n• AMKLaunchTimeProfiler Version: %@", AMKLaunchTimeProfiler.sharedInstance.version]; 252 | [messageBody appendFormat:@"\n• AMKLaunchTimeProfiler Identifier: %@", AMKLaunchTimeProfiler.sharedInstance.identifier]; 253 | [messageBody appendFormat:@"\n\n"]; 254 | [messageBody appendFormat:@"\nAPP 信息:"]; 255 | [messageBody appendFormat:@"\n--------------------------------------------------"]; 256 | [messageBody appendFormat:@"\n• BundleId: %@", AMKLaunchTimeProfiler.sharedInstance.bundleId]; 257 | [messageBody appendFormat:@"\n• ShortVersion: %@", AMKLaunchTimeProfiler.sharedInstance.bundleShortVersion]; 258 | [messageBody appendFormat:@"\n• BundleVersion: %@", AMKLaunchTimeProfiler.sharedInstance.bundleVersion]; 259 | [messageBody appendFormat:@"\n• ClientVersion: %@", AMKLaunchTimeProfiler.sharedInstance.clientVersion]; 260 | [messageBody appendFormat:@"\n• DeviceVersion: %@", AMKLaunchTimeProfiler.sharedInstance.deviceVersion]; 261 | [messageBody appendFormat:@"\n• DeviceName: %@", AMKLaunchTimeProfiler.sharedInstance.deviceName]; 262 | # if defined(WK_TARGET_ONLINE) 263 | [messageBody appendFormat:@"\n• WK_TARGET_ONLINE: %@", @(WK_TARGET_ONLINE)]; 264 | # endif 265 | # if defined(WK_TARGET_ONLINE_QA) 266 | [messageBody appendFormat:@"\n• WK_TARGET_ONLINE_QA: %@", @(WK_TARGET_ONLINE_QA)]; 267 | # endif 268 | # if defined(WK_TARGET_BETA) 269 | [messageBody appendFormat:@"\n• WK_TARGET_BETA: %@", @(WK_TARGET_BETA)]; 270 | # endif 271 | # if defined(WK_TARGET_QA) 272 | [messageBody appendFormat:@"\n• WK_TARGET_QA: %@", @(WK_TARGET_QA)]; 273 | # endif 274 | # if defined(WK_TARGET_DEV) 275 | [messageBody appendFormat:@"\n• WK_TARGET_DEV: %@", @(WK_TARGET_DEV)]; 276 | # endif 277 | # if defined(DEBUG) 278 | [messageBody appendFormat:@"\n• DEBUG: %@", @(DEBUG)]; 279 | # else 280 | [messageBody appendFormat:@"\n• DEBUG: %@", @(0)]; 281 | # endif 282 | [messageBody appendFormat:@"\n\n\n"]; 283 | [messageBody appendFormat:@"%@", AMKLaunchTimeProfiler.description]; 284 | 285 | 286 | // 邮件附件 287 | NSString *txtFileName = [NSString stringWithFormat:@"AMKLaunchTimeProfiler_%@.txt", AMKLaunchTimeProfiler.sharedInstance.identifier]; 288 | NSData *txtFileData = [self.statisticsTextView.text dataUsingEncoding:NSUTF8StringEncoding]; 289 | 290 | NSString *logFileName = [NSString stringWithFormat:@"AMKLaunchTimeProfiler_%@.log", AMKLaunchTimeProfiler.sharedInstance.identifier]; 291 | NSData *logFileData = [self.textView.text dataUsingEncoding:NSUTF8StringEncoding]; 292 | 293 | NSString *jsonFileName = [NSString stringWithFormat:@"AMKLaunchTimeProfiler_%@.json", AMKLaunchTimeProfiler.sharedInstance.identifier]; 294 | NSArray *profilers = (id)[YYCache.amkltp_sharedInstance objectForKey:AMKLaunchTimeProfilersCacheKey]; 295 | if (![profilers isKindOfClass:NSArray.class]) profilers = nil; 296 | NSData *jsonFileData = [NSJSONSerialization dataWithJSONObject:[profilers yy_modelToJSONObject]?:@[] options:NSJSONWritingPrettyPrinted error:nil]; 297 | 298 | // 新建邮件 299 | MFMailComposeViewController *viewController = [MFMailComposeViewController.alloc init]; 300 | [viewController.navigationBar setTintColor:AMKLaunchTimeProfilerTintColor]; 301 | [viewController setMailComposeDelegate:(id)self]; 302 | [viewController setToRecipients:AMKLaunchTimeProfiler.mailRecipients]; 303 | [viewController setSubject:subject]; 304 | [viewController setMessageBody:messageBody isHTML:NO]; 305 | [viewController addAttachmentData:txtFileData?:NSData.new mimeType:@"text/plain" fileName:txtFileName]; 306 | [viewController addAttachmentData:logFileData?:NSData.new mimeType:@"text/plain" fileName:logFileName]; 307 | [viewController addAttachmentData:jsonFileData?:NSData.new mimeType:@"text/plain" fileName:jsonFileName]; 308 | [self presentViewController:viewController animated:YES completion:nil]; 309 | } 310 | 311 | #pragma mark - Layout Subviews 312 | 313 | #pragma mark - Public Methods 314 | 315 | - (void)presentingWithAnimated:(BOOL)flag completion:(void (^)(void))completion { 316 | UINavigationController *navigationController = [AMKLaunchTimeProfilerNavigationController.alloc initWithRootViewController:self]; 317 | navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; 318 | navigationController.modalPresentationStyle = UIModalPresentationFullScreen; 319 | [UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:navigationController animated:flag completion:completion]; 320 | } 321 | 322 | #pragma mark - Private Methods 323 | 324 | - (void)closeBarButtonItemClicked:(id)sender { 325 | [self dismissViewControllerAnimated:YES completion:nil]; 326 | } 327 | 328 | - (void)filterBarButtonItemClicked:(id)sender { 329 | NSLog(@""); 330 | 331 | UIButton *filterBarButton = (UIButton *)self.filterBarButtonItem.customView; 332 | filterBarButton.selected = !filterBarButton.selected; 333 | } 334 | 335 | - (void)moreBarButtonItemClicked:(id)sender { 336 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:@"更多操作" preferredStyle:UIAlertControllerStyleActionSheet]; 337 | [alertController addAction:[UIAlertAction actionWithTitle:@"刷新" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 338 | [self reloadLogs:sender]; 339 | }]]; 340 | [alertController addAction:[UIAlertAction actionWithTitle:@"复制" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 341 | [self copyLogs:sender]; 342 | }]]; 343 | [alertController addAction:[UIAlertAction actionWithTitle:@"发送" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 344 | [self sendLogs:sender]; 345 | }]]; 346 | [alertController addAction:[UIAlertAction actionWithTitle:@"清空" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { 347 | [self clearLogs:sender]; 348 | }]]; 349 | [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; 350 | [self presentViewController:alertController animated:YES completion:nil]; 351 | } 352 | 353 | - (void)scrollToBottomAfterDelay:(NSTimeInterval)afterDelay animated:(BOOL)animated { 354 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(afterDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 355 | if (self.textView.contentOffset.y + self.textView.frame.size.height < self.textView.contentSize.height) { 356 | [self.textView scrollRectToVisible:CGRectMake(0, self.textView.contentSize.height-1, self.textView.frame.size.width, 1) animated:animated]; 357 | [self scrollToBottomAfterDelay:afterDelay animated:animated]; 358 | } 359 | }); 360 | } 361 | 362 | #pragma mark - Notifications 363 | 364 | #pragma mark - KVO 365 | 366 | #pragma mark - Protocol 367 | 368 | #pragma mark MFMailComposeViewControllerDelegate 369 | 370 | - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(nullable NSError *)error { 371 | [controller dismissViewControllerAnimated:YES completion:^{ 372 | if (result != MFMailComposeResultFailed) return; 373 | 374 | NSString *message = AMKLaunchTimeProfilerStringFromMFMailComposeResult(result); 375 | message = [NSString stringWithFormat:@"%@\n%@", message?:@"", error.localizedDescription]; 376 | 377 | UIAlertController *alertController = [AMKLaunchTimeProfilerAlertController alertControllerWithTitle:AMKLaunchTimeProfilerAlertControllerTitle message:message preferredStyle:UIAlertControllerStyleAlert]; 378 | [alertController addAction:[UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil]]; 379 | [self presentViewController:alertController animated:YES completion:nil]; 380 | }]; 381 | } 382 | 383 | #pragma mark - Overrides 384 | 385 | static CGFloat statisticsTextViewHeight = 130; 386 | - (void)viewDidLayoutSubviews { 387 | [super viewDidLayoutSubviews]; 388 | CGRect textViewFrame = self.view.frame; 389 | textViewFrame.size.height -= statisticsTextViewHeight; 390 | self.textView.frame = textViewFrame; 391 | 392 | CGRect statisticsTextViewFrame = self.view.frame; 393 | statisticsTextViewFrame.origin.y = textViewFrame.size.height; 394 | statisticsTextViewFrame.size.height = statisticsTextViewHeight; 395 | self.statisticsTextView.frame = statisticsTextViewFrame; 396 | } 397 | 398 | #pragma mark - Helper Methods 399 | 400 | @end 401 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/UIResponder+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIResponder+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/5. 6 | // 7 | 8 | #import 9 | 10 | @interface UIResponder (AMKLaunchTimeProfiler) 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Core/UIResponder+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIResponder+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/5. 6 | // 7 | 8 | #import "UIResponder+AMKLaunchTimeProfiler.h" 9 | #import "AMKLaunchTimeProfiler.h" 10 | #import "AMKLaunchTimeProfiler+Private.h" 11 | #import 12 | 13 | @interface AMKLaunchTimeProfiler () 14 | @property(nonatomic, assign, readwrite) NSTimeInterval processStartTime; 15 | @property(nonatomic, assign, readwrite) NSTimeInterval mainTime; 16 | @property(nonatomic, assign, readwrite) NSTimeInterval didFinishLaunchingTime; 17 | @property(nonatomic, assign, readwrite) NSTimeInterval firstScreenTime; 18 | @property(nonatomic, strong, readonly, nonnull, class) UISwipeGestureRecognizer *gestureRecognizer; 19 | - (void)printAnalysis; 20 | @end 21 | 22 | @implementation UIResponder (AMKLaunchTimeProfiler) 23 | 24 | + (void)load { 25 | [self AMKLaunchTimeProfiler_UIResponder_hookInit]; 26 | } 27 | 28 | // 通过 hook [UIResponder -init] 方法,找到 UIApplication.sharedApplication.delegate 实例 29 | // 并 hook 它的 -application:didFinishLaunchingWithOptions: 方法 30 | + (void)AMKLaunchTimeProfiler_UIResponder_hookInit { 31 | static dispatch_once_t onceToken; 32 | dispatch_once(&onceToken, ^{ 33 | static id aspectToken; 34 | aspectToken = [UIResponder aspect_hookSelector:@selector(init) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 35 | if ([aspectInfo.instance conformsToProtocol:@protocol(UIApplicationDelegate)]) { 36 | if ([aspectInfo.instance respondsToSelector:@selector(application:didFinishLaunchingWithOptions:)]) { 37 | [aspectInfo.instance AMKLaunchTimeProfiler_UIResponder_hookApplication$didFinishLaunchingWithOptions$]; 38 | [aspectInfo.instance AMKLaunchTimeProfiler_UIResponder_hookSetWindow$]; 39 | [aspectToken remove]; 40 | aspectToken = nil; 41 | } 42 | } 43 | } error:NULL]; 44 | }); 45 | } 46 | 47 | // 通过 hook UIApplication.sharedApplication.delegate 的 -application:didFinishLaunchingWithOptions: 方法 48 | // 在其执行完之后,记录 didFinishLaunchingTime 49 | - (void)AMKLaunchTimeProfiler_UIResponder_hookApplication$didFinishLaunchingWithOptions$ { 50 | static dispatch_once_t onceToken; 51 | dispatch_once(&onceToken, ^{ 52 | static id aspectToken; 53 | aspectToken = [self aspect_hookSelector:@selector(application:didFinishLaunchingWithOptions:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo, UIApplication *application, NSDictionary *launchOptions) { 54 | if (aspectInfo.instance == UIApplication.sharedApplication.delegate) { 55 | AMKLaunchTimeProfiler.gestureRecognizer.enabled = AMKLaunchTimeProfiler.debugEnable; 56 | AMKLaunchTimeProfiler.sharedInstance.didFinishLaunchingTime = (CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970); 57 | AMKLaunchTimeProfilerInternalLog(@"did finish launching time: %@", @(AMKLaunchTimeProfiler.sharedInstance.didFinishLaunchingTime).amkltp_formattedDateStringForSystemTimeZone); 58 | [NSNotificationCenter.defaultCenter postNotificationName:AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification object:AMKLaunchTimeProfiler.sharedInstance]; 59 | [aspectToken remove]; 60 | aspectToken = nil; 61 | } 62 | } error:NULL]; 63 | }); 64 | } 65 | 66 | - (void)AMKLaunchTimeProfiler_UIResponder_hookSetWindow$ { 67 | static dispatch_once_t onceToken; 68 | dispatch_once(&onceToken, ^{ 69 | 70 | // 通过 hook UIApplication.sharedApplication.delegate 的 -setWindow: 方法,以便获取 keyWindoew 71 | [self aspect_hookSelector:@selector(setWindow:) withOptions:AspectPositionAfter|AspectOptionAutomaticRemoval usingBlock:^(id aspectInfo, UIWindow *window) { 72 | UIResponder *appDelegate = aspectInfo.instance; 73 | UIWindow *keyWindow = appDelegate.window; 74 | if (keyWindow) { 75 | 76 | // 通过 hook keyWindoew 的 -setRootViewController: 方法,以便获取 rootViewController 77 | [keyWindow aspect_hookSelector:@selector(setRootViewController:) withOptions:AspectPositionAfter|AspectOptionAutomaticRemoval usingBlock:^(id aspectInfo, UIViewController *viewController) { 78 | UIWindow *keyWindow = aspectInfo.instance; 79 | UIViewController *rootViewController = keyWindow.rootViewController; 80 | if (rootViewController) { 81 | 82 | // 通过 hook rootViewController 的 -viewDidAppear: 方法,以便在其执行完该方法之后,记录 firstScreenTime 83 | [rootViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter|AspectOptionAutomaticRemoval usingBlock:^(id aspectInfo, BOOL animated) { 84 | AMKLaunchTimeProfiler.sharedInstance.firstScreenTime = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; 85 | AMKLaunchTimeProfilerInternalLog(@"first screen time: %@", @(AMKLaunchTimeProfiler.sharedInstance.firstScreenTime).amkltp_formattedDateStringForSystemTimeZone); 86 | [AMKLaunchTimeProfiler.sharedInstance printAnalysis]; 87 | [NSNotificationCenter.defaultCenter postNotificationName:AMKLaunchTimeProfilerFirstScreenDidDisplayNotification object:AMKLaunchTimeProfiler.sharedInstance]; 88 | } error:NULL]; 89 | } 90 | } error:NULL]; 91 | } 92 | } error:NULL]; 93 | }); 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/AMKLaunchTimeProfiler+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfiler+Private.h 3 | // Pods 4 | // 5 | // Created by mengxinxin on 2022/5/5. 6 | // 7 | 8 | #ifndef AMKLaunchTimeProfiler_Private_h 9 | #define AMKLaunchTimeProfiler_Private_h 10 | 11 | #import "MFMailComposeViewController+AMKLaunchTimeProfiler.h" 12 | #import "NSDate+AMKLaunchTimeProfiler.h" 13 | #import "NSNumber+AMKLaunchTimeProfiler.h" 14 | #import "NSString+AMKLaunchTimeProfiler.h" 15 | #import "UIAlertController+AMKLaunchTimeProfiler.h" 16 | #import "UIBarButtonItem+AMKLaunchTimeProfiler.h" 17 | #import "UIDevice+AMKLaunchTimeProfiler.h" 18 | #import "UIImage+AMKLaunchTimeProfiler.h" 19 | #import "UINavigationController+AMKLaunchTimeProfiler.h" 20 | #import "YYCache+AMKLaunchTimeProfiler.h" 21 | 22 | #endif /* AMKLaunchTimeProfiler_Private_h */ 23 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/MFMailComposeViewController+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFMailComposeViewController+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import 9 | 10 | UIKIT_EXTERN NSString *AMKLaunchTimeProfilerStringFromMFMailComposeResult(MFMailComposeResult result); 11 | 12 | @interface MFMailComposeViewController (AMKLaunchTimeProfiler) 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/MFMailComposeViewController+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFMailComposeViewController+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import "MFMailComposeViewController+AMKLaunchTimeProfiler.h" 9 | 10 | NSString *AMKLaunchTimeProfilerStringFromMFMailComposeResult(MFMailComposeResult result) { 11 | switch (result) { 12 | case MFMailComposeResultCancelled: return @"邮件已取消发送"; 13 | case MFMailComposeResultSaved: return @"邮件已保存至草稿箱"; 14 | case MFMailComposeResultSent: return @"邮件已发送"; 15 | case MFMailComposeResultFailed: return @"邮件发送失败"; 16 | default: return nil; 17 | } 18 | } 19 | 20 | @implementation MFMailComposeViewController (AMKLaunchTimeProfiler) 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSDate+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import 9 | 10 | @interface NSDate (AMKLaunchTimeProfiler) 11 | 12 | - (NSDate *_Nullable)amkltp_dateForSystemTimeZone; 13 | 14 | - (NSString *_Nullable)amkltp_formattedStringForSystemTimeZone; 15 | 16 | - (NSString *_Nullable)amkltp_stringForTimestamp; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSDate+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import "NSDate+AMKLaunchTimeProfiler.h" 9 | 10 | @implementation NSDate (AMKLaunchTimeProfiler) 11 | 12 | #pragma mark - Init Methods 13 | 14 | #pragma mark - Getters & Setters 15 | 16 | #pragma mark - Data & Networking 17 | 18 | #pragma mark - Public Methods 19 | 20 | - (NSDate *)amkltp_dateForSystemTimeZone { 21 | NSInteger interval = [NSTimeZone.systemTimeZone secondsFromGMTForDate:self]; 22 | NSDate *localeDate = [self dateByAddingTimeInterval:interval]; 23 | return localeDate; 24 | } 25 | 26 | - (NSString *)amkltp_formattedStringForSystemTimeZone { 27 | static NSDateFormatter *dateFormatter; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | dateFormatter = [NSDateFormatter.alloc init]; 31 | dateFormatter.timeZone = NSTimeZone.systemTimeZone; 32 | dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; 33 | }); 34 | return [dateFormatter stringFromDate:self]; 35 | } 36 | 37 | - (NSString *_Nullable)amkltp_stringForTimestamp { 38 | static NSDateFormatter *dateFormatter; 39 | static dispatch_once_t onceToken; 40 | dispatch_once(&onceToken, ^{ 41 | dateFormatter = [NSDateFormatter.alloc init]; 42 | dateFormatter.timeZone = NSTimeZone.systemTimeZone; 43 | dateFormatter.dateFormat = @"yyyyMMddHHmmss"; 44 | }); 45 | return [dateFormatter stringFromDate:self]; 46 | } 47 | 48 | #pragma mark - Private Methods 49 | 50 | #pragma mark - Notifications 51 | 52 | #pragma mark - KVO 53 | 54 | #pragma mark - Protocol 55 | 56 | #pragma mark - Overrides 57 | 58 | #pragma mark - Helper Methods 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSNumber+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import 9 | 10 | @interface NSNumber (AMKLaunchTimeProfiler) 11 | 12 | - (NSDate *_Nullable)amkltp_dateWithTimeIntervalSince1970; 13 | 14 | - (NSString *_Nullable)amkltp_formattedDateStringForSystemTimeZone; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSNumber+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNumber+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import "NSNumber+AMKLaunchTimeProfiler.h" 9 | #import "NSDate+AMKLaunchTimeProfiler.h" 10 | 11 | @implementation NSNumber (AMKLaunchTimeProfiler) 12 | 13 | #pragma mark - Init Methods 14 | 15 | #pragma mark - Getters & Setters 16 | 17 | - (NSDate *_Nullable)amkltp_dateWithTimeIntervalSince1970 { 18 | return [NSDate dateWithTimeIntervalSince1970:self.doubleValue]; 19 | } 20 | 21 | - (NSString *_Nullable)amkltp_formattedDateStringForSystemTimeZone { 22 | return self.amkltp_dateWithTimeIntervalSince1970.amkltp_formattedStringForSystemTimeZone; 23 | } 24 | 25 | #pragma mark - Data & Networking 26 | 27 | #pragma mark - Public Methods 28 | 29 | #pragma mark - Private Methods 30 | 31 | #pragma mark - Notifications 32 | 33 | #pragma mark - KVO 34 | 35 | #pragma mark - Protocol 36 | 37 | #pragma mark - Overrides 38 | 39 | #pragma mark - Helper Methods 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSString+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/12. 6 | // 7 | 8 | #import 9 | 10 | @interface NSString (AMKLaunchTimeProfiler) 11 | 12 | - (NSString *_Nonnull)amkltp_md5String; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/NSString+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/12. 6 | // 7 | 8 | #import "NSString+AMKLaunchTimeProfiler.h" 9 | #include 10 | 11 | @implementation NSString (AMKLaunchTimeProfiler) 12 | 13 | #pragma mark - Init Methods 14 | 15 | #pragma mark - Getters & Setters 16 | 17 | #pragma mark - Data & Networking 18 | 19 | #pragma mark - Public Methods 20 | 21 | - (NSString *)amkltp_md5String { 22 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; 23 | 24 | unsigned char result[CC_MD5_DIGEST_LENGTH]; 25 | CC_MD5(data.bytes, (CC_LONG)data.length, result); 26 | return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 27 | result[0], result[1], result[2], result[3], 28 | result[4], result[5], result[6], result[7], 29 | result[8], result[9], result[10], result[11], 30 | result[12], result[13], result[14], result[15] 31 | ]; 32 | } 33 | 34 | #pragma mark - Private Methods 35 | 36 | #pragma mark - Notifications 37 | 38 | #pragma mark - KVO 39 | 40 | #pragma mark - Protocol 41 | 42 | #pragma mark - Overrides 43 | 44 | #pragma mark - Helper Methods 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIAlertController+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import 9 | 10 | @interface AMKLaunchTimeProfilerAlertController : UIAlertController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIAlertController+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertController+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import "UIAlertController+AMKLaunchTimeProfiler.h" 9 | #import 10 | 11 | @interface AMKLaunchTimeProfilerAlertController () 12 | 13 | @end 14 | 15 | @implementation AMKLaunchTimeProfilerAlertController 16 | 17 | #pragma mark - Dealloc 18 | 19 | - (void)dealloc { 20 | 21 | } 22 | 23 | #pragma mark - Init Methods 24 | 25 | #pragma mark - Life Circle 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | self.view.tintColor = AMKLaunchTimeProfilerTintColor; 30 | } 31 | 32 | - (void)viewWillAppear:(BOOL)animated { 33 | [super viewWillAppear:animated]; 34 | } 35 | 36 | - (void)viewDidAppear:(BOOL)animated { 37 | [super viewDidAppear:animated]; 38 | } 39 | 40 | - (void)viewWillDisappear:(BOOL)animated { 41 | [super viewWillDisappear:animated]; 42 | } 43 | 44 | - (void)viewDidDisappear:(BOOL)animated { 45 | [super viewDidDisappear:animated]; 46 | } 47 | 48 | #pragma mark - Getters & Setters 49 | 50 | #pragma mark - Data & Networking 51 | 52 | #pragma mark - Layout Subviews 53 | 54 | #pragma mark - Public Methods 55 | 56 | #pragma mark - Private Methods 57 | 58 | #pragma mark - Notifications 59 | 60 | #pragma mark - KVO 61 | 62 | #pragma mark - Protocol 63 | 64 | #pragma mark - Overrides 65 | 66 | #pragma mark - Helper Methods 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIBarButtonItem+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @interface UIBarButtonItem (AMKLaunchTimeProfiler) 12 | 13 | - (instancetype _Nullable)amkltp_initWithImageType:(AMKLaunchTimeProfilerImageType)type target:(id _Nullable)target action:(SEL _Nullable)action; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIBarButtonItem+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIBarButtonItem+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import "UIBarButtonItem+AMKLaunchTimeProfiler.h" 9 | 10 | @implementation UIBarButtonItem (AMKLaunchTimeProfiler) 11 | 12 | #pragma mark - Init Methods 13 | 14 | - (instancetype _Nullable)amkltp_initWithImageType:(AMKLaunchTimeProfilerImageType)type target:(id _Nullable)target action:(SEL _Nullable)action { 15 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 16 | [button setFrame:CGRectMake(0, 0, 30, 30)]; 17 | [button setImage:[[UIImage amkltp_imageWithType:type] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; 18 | [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; 19 | UIBarButtonItem *barButtonItem = [UIBarButtonItem.alloc initWithCustomView:button]; 20 | return barButtonItem; 21 | } 22 | 23 | #pragma mark - Getters & Setters 24 | 25 | #pragma mark - Data & Networking 26 | 27 | #pragma mark - Public Methods 28 | 29 | #pragma mark - Private Methods 30 | 31 | #pragma mark - Notifications 32 | 33 | #pragma mark - KVO 34 | 35 | #pragma mark - Protocol 36 | 37 | #pragma mark - Overrides 38 | 39 | #pragma mark - Helper Methods 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIDevice+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import 9 | 10 | /// fetching model descriptions from an iOS device 11 | /// @see https://github.com/squarefrog/UIDeviceIdentifier/blob/master/UIDeviceIdentifier 12 | @interface UIDevice (AMKLaunchTimeProfiler) 13 | 14 | /// The model name of the device. For example, `iPhone5,3`, `iPad3,1`, `iPod5,1`. 15 | /// @return The current devices model name as a string. 16 | + (NSString *)amkltp_platform; 17 | 18 | /// The full human readable platform string. For example, `iPhone 5C (GSM)`, `iPad 3 (WiFi)`, `iPod Touch 5G`. 19 | /// @return The current devices platform string in a human readable format. 20 | + (NSString *)amkltp_platformString; 21 | 22 | /// The simplified human readable platform string. For example, `iPhone 5C`, `iPad 3`, `iPod Touch 5G`. 23 | /// @return The current devices platform string in a simplified human readable format. 24 | + (NSString *)amkltp_platformStringSimple; 25 | 26 | /// Get a platform string for a specified type. For example: `[UIDeviceHardware platformStringForType:@"iPhone5,3"]; Returns "iPhone 5C (GSM)" 27 | /// @return The platform string for the specified device type. 28 | + (NSString *)amkltp_platformStringForType:(NSString *)type; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIDevice+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/23. 6 | // 7 | 8 | #import "UIDevice+AMKLaunchTimeProfiler.h" 9 | #include 10 | 11 | @implementation UIDevice (AMKLaunchTimeProfiler) 12 | 13 | + (NSString *)amkltp_platform { 14 | size_t size; 15 | sysctlbyname("hw.machine", NULL, &size, NULL, 0); 16 | char *machine = malloc(size); 17 | sysctlbyname("hw.machine", machine, &size, NULL, 0); 18 | NSString *platform = [NSString stringWithUTF8String:machine]; 19 | free(machine); 20 | 21 | return platform; 22 | } 23 | 24 | + (NSDictionary *)amkltp_platformDefinitions { 25 | NSDictionary *definitions = @{ 26 | #if !defined(TARGET_OS_IOS) || TARGET_OS_IOS 27 | @"iPhone1,1": @"iPhone 1G", 28 | @"iPhone1,2": @"iPhone 3G", 29 | @"iPhone2,1": @"iPhone 3GS", 30 | @"iPhone3,1": @"iPhone 4 (GSM)", 31 | @"iPhone3,2": @"iPhone 4 (GSM Rev A)", 32 | @"iPhone3,3": @"iPhone 4 (CDMA)", 33 | @"iPhone4,1": @"iPhone 4S", 34 | @"iPhone5,1": @"iPhone 5 (GSM)", 35 | @"iPhone5,2": @"iPhone 5 (GSM+CDMA)", 36 | @"iPhone5,3": @"iPhone 5C (GSM)", 37 | @"iPhone5,4": @"iPhone 5C (GSM+CDMA)", 38 | @"iPhone6,1": @"iPhone 5S (GSM)", 39 | @"iPhone6,2": @"iPhone 5S (GSM+CDMA)", 40 | @"iPhone7,1": @"iPhone 6 Plus", 41 | @"iPhone7,2": @"iPhone 6", 42 | @"iPhone8,1": @"iPhone 6s", 43 | @"iPhone8,2": @"iPhone 6s Plus", 44 | @"iPhone8,4": @"iPhone SE 1st-gen", 45 | @"iPhone9,4": @"iPhone 7 Plus", 46 | @"iPhone9,2": @"iPhone 7 Plus", 47 | @"iPhone9,3": @"iPhone 7", 48 | @"iPhone9,1": @"iPhone 7", 49 | @"iPhone10,1" : @"iPhone 8", 50 | @"iPhone10,4" : @"iPhone 8", 51 | @"iPhone10,2" : @"iPhone 8 Plus", 52 | @"iPhone10,5" : @"iPhone 8 Plus", 53 | @"iPhone10,3" : @"iPhone X", 54 | @"iPhone10,6" : @"iPhone X", 55 | @"iPhone11,2" : @"iPhone XS", 56 | @"iPhone11,4" : @"iPhone XS Max", 57 | @"iPhone11,6" : @"iPhone XS Max", 58 | @"iPhone11,8" : @"iPhone XR", 59 | @"iPhone12,1" : @"iPhone 11", 60 | @"iPhone12,3" : @"iPhone 11 Pro", 61 | @"iPhone12,5" : @"iPhone 11 Pro Max", 62 | @"iPhone12,8" : @"iPhone SE 2nd-gen", 63 | @"iPhone13,1" : @"iPhone 12 mini", 64 | @"iPhone13,2" : @"iPhone 12", 65 | @"iPhone13,3" : @"iPhone 12 Pro", 66 | @"iPhone13,4" : @"iPhone 12 Pro Max", 67 | @"iPhone14,2" : @"iPhone 13 Pro", 68 | @"iPhone14,3" : @"iPhone 13 Pro Max", 69 | @"iPhone14,4" : @"iPhone 13 mini", 70 | @"iPhone14,5" : @"iPhone 13", 71 | @"iPod1,1": @"iPod Touch 1G", 72 | @"iPod2,1": @"iPod Touch 2G", 73 | @"iPod3,1": @"iPod Touch 3G", 74 | @"iPod4,1": @"iPod Touch 4G", 75 | @"iPod5,1": @"iPod Touch 5G", 76 | @"iPod7,1": @"iPod Touch 6G", 77 | @"iPod9,1": @"iPod Touch 7G", 78 | @"iPad1,1": @"iPad 1", 79 | @"iPad2,1": @"iPad 2 (WiFi)", 80 | @"iPad2,2": @"iPad 2 (GSM)", 81 | @"iPad2,3": @"iPad 2 (CDMA)", 82 | @"iPad2,4": @"iPad 2", 83 | @"iPad2,5": @"iPad Mini (WiFi)", 84 | @"iPad2,6": @"iPad Mini (GSM)", 85 | @"iPad2,7": @"iPad Mini (GSM+CDMA)", 86 | @"iPad3,1": @"iPad 3 (WiFi)", 87 | @"iPad3,2": @"iPad 3 (GSM+CDMA)", 88 | @"iPad3,3": @"iPad 3 (GSM)", 89 | @"iPad3,4": @"iPad 4 (WiFi)", 90 | @"iPad3,5": @"iPad 4 (GSM)", 91 | @"iPad3,6": @"iPad 4 (GSM+CDMA)", 92 | @"iPad4,1": @"iPad Air (WiFi)", 93 | @"iPad4,2": @"iPad Air (WiFi/Cellular)", 94 | @"iPad4,3": @"iPad Air (China)", 95 | @"iPad4,4": @"iPad Mini Retina (WiFi)", 96 | @"iPad4,5": @"iPad Mini Retina (WiFi/Cellular)", 97 | @"iPad4,6": @"iPad Mini Retina (China)", 98 | @"iPad4,7": @"iPad Mini 3 (WiFi)", 99 | @"iPad4,8": @"iPad Mini 3 (WiFi/Cellular)", 100 | @"iPad4,9": @"iPad Mini 3 (China)", 101 | @"iPad5,1": @"iPad Mini 4 (WiFi)", 102 | @"iPad5,2": @"iPad Mini 4 (WiFi/Cellular)", 103 | @"iPad5,3": @"iPad Air 2 (WiFi)", 104 | @"iPad5,4": @"iPad Air 2 (WiFi/Cellular)", 105 | @"iPad6,3": @"iPad Pro 9.7-inch (WiFi)", 106 | @"iPad6,4": @"iPad Pro 9.7-inch (WiFi/Cellular)", 107 | @"iPad6,7": @"iPad Pro 12.9-inch (WiFi)", 108 | @"iPad6,8": @"iPad Pro 12.9-inch (WiFi/Cellular)", 109 | @"iPad6,11": @"iPad 5th-gen (WiFi)", 110 | @"iPad6,12": @"iPad 5th-gen (WiFi/Cellular)", 111 | @"iPad7,1": @"iPad Pro 12.9-inch 2nd-gen (WiFi)", 112 | @"iPad7,2": @"iPad Pro 12.9-inch 2nd-gen (WiFi/Cellular)", 113 | @"iPad7,3": @"iPad Pro 10.5-inch (WiFi)", 114 | @"iPad7,4": @"iPad Pro 10.5-inch (WiFi/Cellular)", 115 | @"iPad7,5": @"iPad 6th-gen (WiFi)", 116 | @"iPad7,6": @"iPad 6th-gen (WiFi/Cellular)", 117 | @"iPad7,11": @"iPad 7th-gen (WiFi)", 118 | @"iPad7,12": @"iPad 7th-gen (WiFi/Cellular)", 119 | @"iPad8,1": @"iPad Pro 11-inch 1st-gen (WiFi)", 120 | @"iPad8,2": @"iPad Pro 11-inch 1st-gen (WiFi)", 121 | @"iPad8,3": @"iPad Pro 11-inch 1st-gen (WiFi/Cellular)", 122 | @"iPad8,4": @"iPad Pro 11-inch 1st-gen (WiFi/Cellular)", 123 | @"iPad8,5": @"iPad Pro 12.9-inch 3rd-gen (WiFi)", 124 | @"iPad8,6": @"iPad Pro 12.9-inch 3rd-gen (WiFi)", 125 | @"iPad8,7": @"iPad Pro 12.9-inch 3rd-gen (WiFi/Cellular)", 126 | @"iPad8,8": @"iPad Pro 12.9-inch 3rd-gen (WiFi/Cellular)", 127 | @"iPad8,9": @"iPad Pro 11-inch 2nd-gen (WiFi)", 128 | @"iPad8,10": @"iPad Pro 11-inch 2nd-gen (WiFi/Cellular)", 129 | @"iPad8,11": @"iPad Pro 12.9-inch 4th-gen (WiFi)", 130 | @"iPad8,12": @"iPad Pro 12.9-inch 4th-gen (WiFi/Cellular)", 131 | @"iPad11,1": @"iPad Mini 5th-gen (WiFi)", 132 | @"iPad11,2": @"iPad Mini 5th-gen (WiFi/Cellular)", 133 | @"iPad11,3": @"iPad Air 3rd-gen (WiFi)", 134 | @"iPad11,4": @"iPad Air 3rd-gen (WiFi/Cellular)", 135 | @"iPad11,6": @"iPad 8th-gen (WiFi)", 136 | @"iPad11,7": @"iPad 8th-gen (WiFi/Cellular)", 137 | @"iPad12,1": @"iPad 9th-gen (Wifi)", 138 | @"iPad12,2": @"iPad 9th-gen (Wifi/Cellular)", 139 | @"iPad13,1": @"iPad Air 4th-gen (WiFi)", 140 | @"iPad13,2": @"iPad Air 4th-gen (WiFi/Cellular)", 141 | @"iPad13,4": @"iPad Pro 11-inch 3rd-gen (WiFi)", 142 | @"iPad13,5": @"iPad Pro 11-inch 3rd-gen (WiFi)", 143 | @"iPad13,6": @"iPad Pro 11-inch 3rd-gen (WiFi/Cellular)", 144 | @"iPad13,7": @"iPad Pro 11-inch 3rd-gen (WiFi/Cellular)", 145 | @"iPad13,8": @"iPad Pro 12.9-inch 5th-gen (WiFi)", 146 | @"iPad13,9": @"iPad Pro 12.9-inch 5th-gen (WiFi)", 147 | @"iPad13,10": @"iPad Pro 12.9-inch 5th-gen (WiFi/Cellular)", 148 | @"iPad13,11": @"iPad Pro 12.9-inch 5th-gen (WiFi/Cellular)", 149 | @"iPad14,1": @"iPad mini 6th-gen (WiFi)", 150 | @"iPad14,2": @"iPad mini 6th-gen (WiFi/Cellular)", 151 | #endif 152 | #if TARGET_OS_TV 153 | @"AppleTV5,3": @"Apple TV 4G", 154 | @"AppleTV6,2": @"Apple TV 4K", 155 | #endif 156 | #if !defined(TARGET_OS_SIMULATOR) || TARGET_OS_SIMULATOR 157 | @"i386": @"Simulator", 158 | @"x86_64": @"Simulator", 159 | #endif 160 | }; 161 | return definitions; 162 | } 163 | 164 | + (NSString *)amkltp_platformString { 165 | return [self amkltp_platformStringForType: [self amkltp_platform]]; 166 | } 167 | 168 | + (NSString *)amkltp_platformStringSimple { 169 | NSString *platformString = [self amkltp_platformString]; 170 | 171 | NSRange range = [platformString rangeOfString:@"("]; 172 | if (range.length) return [platformString substringToIndex:range.location - 1]; 173 | 174 | return platformString; 175 | } 176 | 177 | + (NSString *)amkltp_platformStringForType:(NSString *)type { 178 | NSDictionary *platformStrings = [self amkltp_platformDefinitions]; 179 | return platformStrings[type] ?: type; 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIImage+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_ENUM(NSInteger, AMKLaunchTimeProfilerImageType) { 11 | AMKLaunchTimeProfilerImageTypeNone = 0, 12 | AMKLaunchTimeProfilerImageTypeNaviBarClose, //!< 导航栏 - 关闭 13 | AMKLaunchTimeProfilerImageTypeNaviBarFilter, //!< 导航栏 - 过滤 14 | AMKLaunchTimeProfilerImageTypeNaviBarFilterSelected, //!< 导航栏 - 过滤 - 选中 15 | AMKLaunchTimeProfilerImageTypeNaviBarMore, //!< 导航栏 - 更多 16 | }; 17 | 18 | @interface UIImage (AMKLaunchTimeProfiler) 19 | 20 | + (UIImage *)amkltp_imageWithType:(AMKLaunchTimeProfilerImageType)type; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UIImage+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/3/24. 6 | // 7 | 8 | #import "UIImage+AMKLaunchTimeProfiler.h" 9 | 10 | @implementation UIImage (AMKLaunchTimeProfiler) 11 | 12 | #pragma mark - Init Methods 13 | 14 | // 转换工具:https://c.runoob.com/front-end/59/ 15 | + (UIImage *)amkltp_imageWithType:(AMKLaunchTimeProfilerImageType)type { 16 | NSString *encodedImageString = nil; 17 | switch (type) { 18 | case AMKLaunchTimeProfilerImageTypeNaviBarClose: encodedImageString = @"data:image/png;base64,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"; break; 19 | case AMKLaunchTimeProfilerImageTypeNaviBarMore: encodedImageString = @"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMKADAAQAAAABAAAAMAAAAAAoDQEPAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MjAwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjIwMDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4Kbs6npwAAAbdJREFUaAXtV0tugzAQxabfVQvKqvdC4hjsOEQlVt2zhz1bLsANeoL0AN1V1M/ySDglAcuKHKuDNNJ4Mh6/eW9wEjHPc5pE/MiIsWvo3EBoBVkBVsCTAR4hTwK9t7MC3hR6FmAFPAn03s4KeFPoWYAV8CTQe/v/UqDv+xS2l7Zr52sc+Eu5YXKapvu2bZ8IOHzE1D65slfnN03zTPnwt/Id6lt4kxUAywTZdd0DAanr+gVGa3x20oQGr2ICOVVVvcLgI7bShGv9JTbtX2pADsPwiMPBYJ7njXK/YPCJYeQQCQagGMfxLsuyD5V7hMFHbNGEPtyh/prSlxsw7GomD4fDuwIyL83ENLOkBDVlwFv5JqbJUI1o5tGQqpnsrP+HfRB3TgFJM1mW5ZuU8hPghRDfMPiI4TMAoHcCvhkZMI8GfozBP9I4QSnX+mgagE8t+mv0nAJp7CMEqeJ+ic2suV5zN3WN0gtz019kAkyrG2LXQz8jiqLA7bL5XDsfAJwa2EQcICH6a5QbCDA11pGsgEVHgAUrEIB060hWwKIjwIIVCEC6dSQrYNERYMEKBCDdOpIVsOgIsPgF3pV5z/+1eXMAAAAASUVORK5CYII="; break; 20 | case AMKLaunchTimeProfilerImageTypeNaviBarFilter: encodedImageString = @"data:image/png;base64,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"; break; 21 | case AMKLaunchTimeProfilerImageTypeNaviBarFilterSelected: encodedImageString = @"data:image/png;base64,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"; break; 22 | default: break; 23 | } 24 | 25 | if (!encodedImageString.length) return nil; 26 | 27 | NSRange range = [encodedImageString rangeOfString:@";base64,"]; 28 | if (range.location != NSNotFound) encodedImageString = [encodedImageString substringFromIndex:range.location+range.length]; 29 | 30 | NSData *decodedImageData = [NSData.alloc initWithBase64EncodedString:encodedImageString options:0]; 31 | UIImage *decodedImage = [UIImage imageWithData:decodedImageData scale:2]; 32 | return decodedImage; 33 | } 34 | 35 | #pragma mark - Getters & Setters 36 | 37 | #pragma mark - Data & Networking 38 | 39 | #pragma mark - Public Methods 40 | 41 | #pragma mark - Private Methods 42 | 43 | #pragma mark - Notifications 44 | 45 | #pragma mark - KVO 46 | 47 | #pragma mark - Protocol 48 | 49 | #pragma mark - Overrides 50 | 51 | #pragma mark - Helper Methods 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UINavigationController+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import 9 | 10 | @interface AMKLaunchTimeProfilerNavigationController : UINavigationController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/UINavigationController+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // UINavigationController+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import "UINavigationController+AMKLaunchTimeProfiler.h" 9 | #import 10 | 11 | @implementation AMKLaunchTimeProfilerNavigationController 12 | 13 | #pragma mark - Dealloc 14 | 15 | - (void)dealloc { 16 | 17 | } 18 | 19 | #pragma mark - Init Methods 20 | 21 | - (instancetype)initWithRootViewController:(UIViewController *)rootViewController { 22 | if (self = [super initWithRootViewController:rootViewController]) { 23 | self.navigationBar.tintColor = AMKLaunchTimeProfilerTintColor; 24 | } 25 | return self; 26 | } 27 | 28 | #pragma mark - Life Circle 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | self.view.tintColor = AMKLaunchTimeProfilerTintColor; 33 | } 34 | 35 | - (void)viewWillAppear:(BOOL)animated { 36 | [super viewWillAppear:animated]; 37 | } 38 | 39 | - (void)viewDidAppear:(BOOL)animated { 40 | [super viewDidAppear:animated]; 41 | } 42 | 43 | - (void)viewWillDisappear:(BOOL)animated { 44 | [super viewWillDisappear:animated]; 45 | } 46 | 47 | - (void)viewDidDisappear:(BOOL)animated { 48 | [super viewDidDisappear:animated]; 49 | } 50 | 51 | #pragma mark - Getters & Setters 52 | 53 | #pragma mark - Data & Networking 54 | 55 | #pragma mark - Layout Subviews 56 | 57 | #pragma mark - Public Methods 58 | 59 | #pragma mark - Private Methods 60 | 61 | #pragma mark - Notifications 62 | 63 | #pragma mark - KVO 64 | 65 | #pragma mark - Protocol 66 | 67 | #pragma mark - Overrides 68 | 69 | #pragma mark - Helper Methods 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/YYCache+AMKLaunchTimeProfiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYCache+AMKLaunchTimeProfiler.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import 9 | 10 | @interface YYCache (AMKLaunchTimeProfiler) 11 | 12 | /// 单例 13 | + (YYCache *_Nonnull)amkltp_sharedInstance; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AMKLaunchTimeProfiler/Classes/Private/YYCache+AMKLaunchTimeProfiler.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYCache+AMKLaunchTimeProfiler.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 2022/5/9. 6 | // 7 | 8 | #import "YYCache+AMKLaunchTimeProfiler.h" 9 | #import "AMKLaunchTimeProfilerConstants.h" 10 | 11 | @implementation YYCache (AMKLaunchTimeProfiler) 12 | 13 | #pragma mark - Init Methods 14 | 15 | #pragma mark - Getters & Setters 16 | 17 | + (YYCache *)amkltp_sharedInstance { 18 | static YYCache *sharedInstance; 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^{ 21 | sharedInstance = [YYCache cacheWithName:@"AMKLaunchTimeProfiler"]; 22 | //sharedInstance.diskCache.countLimit = sharedInstance.memoryCache.countLimit = 10000; // 最大缓存数据个数 23 | //sharedInstance.diskCache.costLimit = sharedInstance.memoryCache.costLimit = 10*1024; // 最大缓存开销 24 | }); 25 | return sharedInstance; 26 | } 27 | 28 | #pragma mark - Data & Networking 29 | 30 | #pragma mark - Public Methods 31 | 32 | #pragma mark - Private Methods 33 | 34 | #pragma mark - Notifications 35 | 36 | #pragma mark - KVO 37 | 38 | #pragma mark - Protocol 39 | 40 | #pragma mark - Overrides 41 | 42 | #pragma mark - Helper Methods 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 11 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 12 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 13 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 14 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 15 | 6003F59E195388D20070C39A /* AMKAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* AMKAppDelegate.m */; }; 16 | 6003F5A7195388D20070C39A /* AMKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* AMKViewController.m */; }; 17 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 18 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 19 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 20 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 21 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 22 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 23 | 657432C0800F7A90718357AC /* Pods_AMKLaunchTimeProfiler_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7833C075B6EFE90BC0F6FE4B /* Pods_AMKLaunchTimeProfiler_Example.framework */; }; 24 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 25 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 26 | 88E5128B2863056D0056813A /* AMKHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E512832863056D0056813A /* AMKHomeViewController.m */; }; 27 | 88E5128C2863056D0056813A /* AMKRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E512842863056D0056813A /* AMKRootViewController.m */; }; 28 | 88E5128D2863056D0056813A /* AMKNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88E512872863056D0056813A /* AMKNavigationController.m */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 6003F582195388D10070C39A /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 6003F589195388D20070C39A; 37 | remoteInfo = AMKLaunchTimeProfiler; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 00B28ABE667EDE6E976753A9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 43 | 2EAAFFA186506A0E421BF926 /* Pods-AMKLaunchTimeProfiler_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AMKLaunchTimeProfiler_Example.debug.xcconfig"; path = "Target Support Files/Pods-AMKLaunchTimeProfiler_Example/Pods-AMKLaunchTimeProfiler_Example.debug.xcconfig"; sourceTree = ""; }; 44 | 3599189879CC77EC5E853E41 /* AMKLaunchTimeProfiler.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = AMKLaunchTimeProfiler.podspec; path = ../AMKLaunchTimeProfiler.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 45 | 6003F58A195388D20070C39A /* AMKLaunchTimeProfiler_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AMKLaunchTimeProfiler_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 47 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 48 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 49 | 6003F595195388D20070C39A /* AMKLaunchTimeProfiler-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "AMKLaunchTimeProfiler-Info.plist"; sourceTree = ""; }; 50 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 51 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 52 | 6003F59B195388D20070C39A /* AMKLaunchTimeProfiler-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AMKLaunchTimeProfiler-Prefix.pch"; sourceTree = ""; }; 53 | 6003F59C195388D20070C39A /* AMKAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AMKAppDelegate.h; sourceTree = ""; }; 54 | 6003F59D195388D20070C39A /* AMKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AMKAppDelegate.m; sourceTree = ""; }; 55 | 6003F5A5195388D20070C39A /* AMKViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AMKViewController.h; sourceTree = ""; }; 56 | 6003F5A6195388D20070C39A /* AMKViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AMKViewController.m; sourceTree = ""; }; 57 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 58 | 6003F5AE195388D20070C39A /* AMKLaunchTimeProfiler_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AMKLaunchTimeProfiler_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 60 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 61 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 62 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 63 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 64 | 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 65 | 7833C075B6EFE90BC0F6FE4B /* Pods_AMKLaunchTimeProfiler_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AMKLaunchTimeProfiler_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 871E037A79E4E4DDDF72A602 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 67 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 68 | 88E512832863056D0056813A /* AMKHomeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMKHomeViewController.m; sourceTree = ""; }; 69 | 88E512842863056D0056813A /* AMKRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMKRootViewController.m; sourceTree = ""; }; 70 | 88E512852863056D0056813A /* AMKRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMKRootViewController.h; sourceTree = ""; }; 71 | 88E512862863056D0056813A /* AMKHomeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMKHomeViewController.h; sourceTree = ""; }; 72 | 88E512872863056D0056813A /* AMKNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMKNavigationController.m; sourceTree = ""; }; 73 | 88E512892863056D0056813A /* AMKNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMKNavigationController.h; sourceTree = ""; }; 74 | 9C40ECAF7E8DB543F92D9AB7 /* Pods-AMKLaunchTimeProfiler_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AMKLaunchTimeProfiler_Example.release.xcconfig"; path = "Target Support Files/Pods-AMKLaunchTimeProfiler_Example/Pods-AMKLaunchTimeProfiler_Example.release.xcconfig"; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 6003F587195388D20070C39A /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 83 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 84 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 85 | 657432C0800F7A90718357AC /* Pods_AMKLaunchTimeProfiler_Example.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | 6003F5AB195388D20070C39A /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 94 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 95 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 3631F0F2DF1A31CF8E736AC0 /* Pods */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 2EAAFFA186506A0E421BF926 /* Pods-AMKLaunchTimeProfiler_Example.debug.xcconfig */, 106 | 9C40ECAF7E8DB543F92D9AB7 /* Pods-AMKLaunchTimeProfiler_Example.release.xcconfig */, 107 | ); 108 | path = Pods; 109 | sourceTree = ""; 110 | }; 111 | 6003F581195388D10070C39A = { 112 | isa = PBXGroup; 113 | children = ( 114 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 115 | 6003F593195388D20070C39A /* Example for AMKLaunchTimeProfiler */, 116 | 6003F5B5195388D20070C39A /* Tests */, 117 | 6003F58C195388D20070C39A /* Frameworks */, 118 | 6003F58B195388D20070C39A /* Products */, 119 | 3631F0F2DF1A31CF8E736AC0 /* Pods */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | 6003F58B195388D20070C39A /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 6003F58A195388D20070C39A /* AMKLaunchTimeProfiler_Example.app */, 127 | 6003F5AE195388D20070C39A /* AMKLaunchTimeProfiler_Tests.xctest */, 128 | ); 129 | name = Products; 130 | sourceTree = ""; 131 | }; 132 | 6003F58C195388D20070C39A /* Frameworks */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 6003F58D195388D20070C39A /* Foundation.framework */, 136 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 137 | 6003F591195388D20070C39A /* UIKit.framework */, 138 | 6003F5AF195388D20070C39A /* XCTest.framework */, 139 | 7833C075B6EFE90BC0F6FE4B /* Pods_AMKLaunchTimeProfiler_Example.framework */, 140 | ); 141 | name = Frameworks; 142 | sourceTree = ""; 143 | }; 144 | 6003F593195388D20070C39A /* Example for AMKLaunchTimeProfiler */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 6003F59C195388D20070C39A /* AMKAppDelegate.h */, 148 | 6003F59D195388D20070C39A /* AMKAppDelegate.m */, 149 | 88E512852863056D0056813A /* AMKRootViewController.h */, 150 | 88E512842863056D0056813A /* AMKRootViewController.m */, 151 | 88E512862863056D0056813A /* AMKHomeViewController.h */, 152 | 88E512832863056D0056813A /* AMKHomeViewController.m */, 153 | 6003F5A5195388D20070C39A /* AMKViewController.h */, 154 | 6003F5A6195388D20070C39A /* AMKViewController.m */, 155 | 88E512892863056D0056813A /* AMKNavigationController.h */, 156 | 88E512872863056D0056813A /* AMKNavigationController.m */, 157 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 158 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, 159 | 6003F5A8195388D20070C39A /* Images.xcassets */, 160 | 6003F594195388D20070C39A /* Supporting Files */, 161 | ); 162 | name = "Example for AMKLaunchTimeProfiler"; 163 | path = AMKLaunchTimeProfiler; 164 | sourceTree = ""; 165 | }; 166 | 6003F594195388D20070C39A /* Supporting Files */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 6003F595195388D20070C39A /* AMKLaunchTimeProfiler-Info.plist */, 170 | 6003F596195388D20070C39A /* InfoPlist.strings */, 171 | 6003F599195388D20070C39A /* main.m */, 172 | 6003F59B195388D20070C39A /* AMKLaunchTimeProfiler-Prefix.pch */, 173 | ); 174 | name = "Supporting Files"; 175 | sourceTree = ""; 176 | }; 177 | 6003F5B5195388D20070C39A /* Tests */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 6003F5BB195388D20070C39A /* Tests.m */, 181 | 6003F5B6195388D20070C39A /* Supporting Files */, 182 | ); 183 | path = Tests; 184 | sourceTree = ""; 185 | }; 186 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 190 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 191 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 192 | ); 193 | name = "Supporting Files"; 194 | sourceTree = ""; 195 | }; 196 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 3599189879CC77EC5E853E41 /* AMKLaunchTimeProfiler.podspec */, 200 | 871E037A79E4E4DDDF72A602 /* README.md */, 201 | 00B28ABE667EDE6E976753A9 /* LICENSE */, 202 | ); 203 | name = "Podspec Metadata"; 204 | sourceTree = ""; 205 | }; 206 | /* End PBXGroup section */ 207 | 208 | /* Begin PBXNativeTarget section */ 209 | 6003F589195388D20070C39A /* AMKLaunchTimeProfiler_Example */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "AMKLaunchTimeProfiler_Example" */; 212 | buildPhases = ( 213 | A048603BEE8A1EDC076E8F23 /* [CP] Check Pods Manifest.lock */, 214 | 6003F586195388D20070C39A /* Sources */, 215 | 6003F587195388D20070C39A /* Frameworks */, 216 | 6003F588195388D20070C39A /* Resources */, 217 | B7FC7CC30DDC572A194DC8A8 /* [CP] Embed Pods Frameworks */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ); 223 | name = AMKLaunchTimeProfiler_Example; 224 | productName = AMKLaunchTimeProfiler; 225 | productReference = 6003F58A195388D20070C39A /* AMKLaunchTimeProfiler_Example.app */; 226 | productType = "com.apple.product-type.application"; 227 | }; 228 | 6003F5AD195388D20070C39A /* AMKLaunchTimeProfiler_Tests */ = { 229 | isa = PBXNativeTarget; 230 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "AMKLaunchTimeProfiler_Tests" */; 231 | buildPhases = ( 232 | 6003F5AA195388D20070C39A /* Sources */, 233 | 6003F5AB195388D20070C39A /* Frameworks */, 234 | 6003F5AC195388D20070C39A /* Resources */, 235 | ); 236 | buildRules = ( 237 | ); 238 | dependencies = ( 239 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 240 | ); 241 | name = AMKLaunchTimeProfiler_Tests; 242 | productName = AMKLaunchTimeProfilerTests; 243 | productReference = 6003F5AE195388D20070C39A /* AMKLaunchTimeProfiler_Tests.xctest */; 244 | productType = "com.apple.product-type.bundle.unit-test"; 245 | }; 246 | /* End PBXNativeTarget section */ 247 | 248 | /* Begin PBXProject section */ 249 | 6003F582195388D10070C39A /* Project object */ = { 250 | isa = PBXProject; 251 | attributes = { 252 | CLASSPREFIX = AMK; 253 | LastUpgradeCheck = 0720; 254 | ORGANIZATIONNAME = mengxinxin; 255 | TargetAttributes = { 256 | 6003F589195388D20070C39A = { 257 | DevelopmentTeam = 9X349G8KD4; 258 | }; 259 | 6003F5AD195388D20070C39A = { 260 | TestTargetID = 6003F589195388D20070C39A; 261 | }; 262 | }; 263 | }; 264 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "AMKLaunchTimeProfiler" */; 265 | compatibilityVersion = "Xcode 3.2"; 266 | developmentRegion = English; 267 | hasScannedForEncodings = 0; 268 | knownRegions = ( 269 | English, 270 | en, 271 | Base, 272 | ); 273 | mainGroup = 6003F581195388D10070C39A; 274 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 275 | projectDirPath = ""; 276 | projectRoot = ""; 277 | targets = ( 278 | 6003F589195388D20070C39A /* AMKLaunchTimeProfiler_Example */, 279 | 6003F5AD195388D20070C39A /* AMKLaunchTimeProfiler_Tests */, 280 | ); 281 | }; 282 | /* End PBXProject section */ 283 | 284 | /* Begin PBXResourcesBuildPhase section */ 285 | 6003F588195388D20070C39A /* Resources */ = { 286 | isa = PBXResourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 290 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, 291 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 292 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 6003F5AC195388D20070C39A /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | /* End PBXResourcesBuildPhase section */ 305 | 306 | /* Begin PBXShellScriptBuildPhase section */ 307 | A048603BEE8A1EDC076E8F23 /* [CP] Check Pods Manifest.lock */ = { 308 | isa = PBXShellScriptBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | ); 312 | inputFileListPaths = ( 313 | ); 314 | inputPaths = ( 315 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 316 | "${PODS_ROOT}/Manifest.lock", 317 | ); 318 | name = "[CP] Check Pods Manifest.lock"; 319 | outputFileListPaths = ( 320 | ); 321 | outputPaths = ( 322 | "$(DERIVED_FILE_DIR)/Pods-AMKLaunchTimeProfiler_Example-checkManifestLockResult.txt", 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | shellPath = /bin/sh; 326 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 327 | showEnvVarsInLog = 0; 328 | }; 329 | B7FC7CC30DDC572A194DC8A8 /* [CP] Embed Pods Frameworks */ = { 330 | isa = PBXShellScriptBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | ); 334 | inputPaths = ( 335 | "${PODS_ROOT}/Target Support Files/Pods-AMKLaunchTimeProfiler_Example/Pods-AMKLaunchTimeProfiler_Example-frameworks.sh", 336 | "${BUILT_PRODUCTS_DIR}/AMKLaunchTimeProfiler/AMKLaunchTimeProfiler.framework", 337 | "${BUILT_PRODUCTS_DIR}/Aspects/Aspects.framework", 338 | "${BUILT_PRODUCTS_DIR}/FLEX/FLEX.framework", 339 | "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework", 340 | "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework", 341 | "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework", 342 | "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework", 343 | ); 344 | name = "[CP] Embed Pods Frameworks"; 345 | outputPaths = ( 346 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AMKLaunchTimeProfiler.framework", 347 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Aspects.framework", 348 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLEX.framework", 349 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework", 350 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", 351 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework", 352 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework", 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | shellPath = /bin/sh; 356 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AMKLaunchTimeProfiler_Example/Pods-AMKLaunchTimeProfiler_Example-frameworks.sh\"\n"; 357 | showEnvVarsInLog = 0; 358 | }; 359 | /* End PBXShellScriptBuildPhase section */ 360 | 361 | /* Begin PBXSourcesBuildPhase section */ 362 | 6003F586195388D20070C39A /* Sources */ = { 363 | isa = PBXSourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | 6003F59E195388D20070C39A /* AMKAppDelegate.m in Sources */, 367 | 88E5128B2863056D0056813A /* AMKHomeViewController.m in Sources */, 368 | 6003F5A7195388D20070C39A /* AMKViewController.m in Sources */, 369 | 6003F59A195388D20070C39A /* main.m in Sources */, 370 | 88E5128C2863056D0056813A /* AMKRootViewController.m in Sources */, 371 | 88E5128D2863056D0056813A /* AMKNavigationController.m in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | 6003F5AA195388D20070C39A /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | /* End PBXSourcesBuildPhase section */ 384 | 385 | /* Begin PBXTargetDependency section */ 386 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 387 | isa = PBXTargetDependency; 388 | target = 6003F589195388D20070C39A /* AMKLaunchTimeProfiler_Example */; 389 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 390 | }; 391 | /* End PBXTargetDependency section */ 392 | 393 | /* Begin PBXVariantGroup section */ 394 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 395 | isa = PBXVariantGroup; 396 | children = ( 397 | 6003F597195388D20070C39A /* en */, 398 | ); 399 | name = InfoPlist.strings; 400 | sourceTree = ""; 401 | }; 402 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 403 | isa = PBXVariantGroup; 404 | children = ( 405 | 6003F5B9195388D20070C39A /* en */, 406 | ); 407 | name = InfoPlist.strings; 408 | sourceTree = ""; 409 | }; 410 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = { 411 | isa = PBXVariantGroup; 412 | children = ( 413 | 71719F9E1E33DC2100824A3D /* Base */, 414 | ); 415 | name = LaunchScreen.storyboard; 416 | sourceTree = ""; 417 | }; 418 | /* End PBXVariantGroup section */ 419 | 420 | /* Begin XCBuildConfiguration section */ 421 | 6003F5BD195388D20070C39A /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 426 | CLANG_CXX_LIBRARY = "libc++"; 427 | CLANG_ENABLE_MODULES = YES; 428 | CLANG_ENABLE_OBJC_ARC = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_CONSTANT_CONVERSION = YES; 431 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 432 | CLANG_WARN_EMPTY_BODY = YES; 433 | CLANG_WARN_ENUM_CONVERSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 436 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 437 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 438 | COPY_PHASE_STRIP = NO; 439 | ENABLE_TESTABILITY = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu99; 441 | GCC_DYNAMIC_NO_PIC = NO; 442 | GCC_OPTIMIZATION_LEVEL = 0; 443 | GCC_PREPROCESSOR_DEFINITIONS = ( 444 | "DEBUG=1", 445 | "$(inherited)", 446 | ); 447 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 455 | ONLY_ACTIVE_ARCH = YES; 456 | SDKROOT = iphoneos; 457 | TARGETED_DEVICE_FAMILY = "1,2"; 458 | }; 459 | name = Debug; 460 | }; 461 | 6003F5BE195388D20070C39A /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | ALWAYS_SEARCH_USER_PATHS = NO; 465 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 466 | CLANG_CXX_LIBRARY = "libc++"; 467 | CLANG_ENABLE_MODULES = YES; 468 | CLANG_ENABLE_OBJC_ARC = YES; 469 | CLANG_WARN_BOOL_CONVERSION = YES; 470 | CLANG_WARN_CONSTANT_CONVERSION = YES; 471 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 472 | CLANG_WARN_EMPTY_BODY = YES; 473 | CLANG_WARN_ENUM_CONVERSION = YES; 474 | CLANG_WARN_INT_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 477 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 478 | COPY_PHASE_STRIP = YES; 479 | ENABLE_NS_ASSERTIONS = NO; 480 | GCC_C_LANGUAGE_STANDARD = gnu99; 481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 483 | GCC_WARN_UNDECLARED_SELECTOR = YES; 484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 485 | GCC_WARN_UNUSED_FUNCTION = YES; 486 | GCC_WARN_UNUSED_VARIABLE = YES; 487 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 488 | SDKROOT = iphoneos; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | VALIDATE_PRODUCT = YES; 491 | }; 492 | name = Release; 493 | }; 494 | 6003F5C0195388D20070C39A /* Debug */ = { 495 | isa = XCBuildConfiguration; 496 | baseConfigurationReference = 2EAAFFA186506A0E421BF926 /* Pods-AMKLaunchTimeProfiler_Example.debug.xcconfig */; 497 | buildSettings = { 498 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 499 | DEVELOPMENT_TEAM = 9X349G8KD4; 500 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 501 | GCC_PREFIX_HEADER = "AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Prefix.pch"; 502 | INFOPLIST_FILE = "AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Info.plist"; 503 | MODULE_NAME = ExampleApp; 504 | PRODUCT_BUNDLE_IDENTIFIER = "io.github.andym129.amk-launch-time-profiler"; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SWIFT_VERSION = 4.0; 507 | WRAPPER_EXTENSION = app; 508 | }; 509 | name = Debug; 510 | }; 511 | 6003F5C1195388D20070C39A /* Release */ = { 512 | isa = XCBuildConfiguration; 513 | baseConfigurationReference = 9C40ECAF7E8DB543F92D9AB7 /* Pods-AMKLaunchTimeProfiler_Example.release.xcconfig */; 514 | buildSettings = { 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | DEVELOPMENT_TEAM = 9X349G8KD4; 517 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 518 | GCC_PREFIX_HEADER = "AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Prefix.pch"; 519 | INFOPLIST_FILE = "AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Info.plist"; 520 | MODULE_NAME = ExampleApp; 521 | PRODUCT_BUNDLE_IDENTIFIER = "io.github.andym129.amk-launch-time-profiler"; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_VERSION = 4.0; 524 | WRAPPER_EXTENSION = app; 525 | }; 526 | name = Release; 527 | }; 528 | 6003F5C3195388D20070C39A /* Debug */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | BUNDLE_LOADER = "$(TEST_HOST)"; 532 | FRAMEWORK_SEARCH_PATHS = ( 533 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 534 | "$(inherited)", 535 | "$(DEVELOPER_FRAMEWORKS_DIR)", 536 | ); 537 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 538 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 539 | GCC_PREPROCESSOR_DEFINITIONS = ( 540 | "DEBUG=1", 541 | "$(inherited)", 542 | ); 543 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 544 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | SWIFT_VERSION = 4.0; 547 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AMKLaunchTimeProfiler_Example.app/AMKLaunchTimeProfiler_Example"; 548 | WRAPPER_EXTENSION = xctest; 549 | }; 550 | name = Debug; 551 | }; 552 | 6003F5C4195388D20070C39A /* Release */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | BUNDLE_LOADER = "$(TEST_HOST)"; 556 | FRAMEWORK_SEARCH_PATHS = ( 557 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 558 | "$(inherited)", 559 | "$(DEVELOPER_FRAMEWORKS_DIR)", 560 | ); 561 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 562 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 563 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 564 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | SWIFT_VERSION = 4.0; 567 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AMKLaunchTimeProfiler_Example.app/AMKLaunchTimeProfiler_Example"; 568 | WRAPPER_EXTENSION = xctest; 569 | }; 570 | name = Release; 571 | }; 572 | /* End XCBuildConfiguration section */ 573 | 574 | /* Begin XCConfigurationList section */ 575 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "AMKLaunchTimeProfiler" */ = { 576 | isa = XCConfigurationList; 577 | buildConfigurations = ( 578 | 6003F5BD195388D20070C39A /* Debug */, 579 | 6003F5BE195388D20070C39A /* Release */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "AMKLaunchTimeProfiler_Example" */ = { 585 | isa = XCConfigurationList; 586 | buildConfigurations = ( 587 | 6003F5C0195388D20070C39A /* Debug */, 588 | 6003F5C1195388D20070C39A /* Release */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "AMKLaunchTimeProfiler_Tests" */ = { 594 | isa = XCConfigurationList; 595 | buildConfigurations = ( 596 | 6003F5C3195388D20070C39A /* Debug */, 597 | 6003F5C4195388D20070C39A /* Release */, 598 | ); 599 | defaultConfigurationIsVisible = 0; 600 | defaultConfigurationName = Release; 601 | }; 602 | /* End XCConfigurationList section */ 603 | }; 604 | rootObject = 6003F582195388D10070C39A /* Project object */; 605 | } 606 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler.xcodeproj/xcshareddata/xcschemes/AMKLaunchTimeProfiler-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKAppDelegate.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface AMKAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKAppDelegate.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import "AMKAppDelegate.h" 10 | #import "AMKRootViewController.h" 11 | #import 12 | 13 | @implementation AMKAppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 16 | AMKLaunchTimeProfiler.debugEnable = YES; 17 | AMKLaunchTimeProfiler.mailRecipients = @[@"example@email.com"]; 18 | AMKLaunchTimeProfilerOnceLogBegin(@""); 19 | 20 | self.window = [UIWindow.alloc initWithFrame:UIScreen.mainScreen.bounds]; 21 | self.window.rootViewController = [AMKRootViewController.alloc init]; 22 | [self.window makeKeyAndVisible]; 23 | 24 | // Log 测试 25 | [AMKLaunchTimeProfiler.sharedInstance logWithFunction:__FUNCTION__ line:__LINE__ string:@"hello"]; 26 | AMKLaunchTimeProfilerLog(@"world"); 27 | 28 | // 监听通知 29 | [NSNotificationCenter.defaultCenter addObserverForName:AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) { 30 | NSLog(@"AMKLaunchTimeProfilerApplicationDidFinishLaunchingNotification"); 31 | }]; 32 | [NSNotificationCenter.defaultCenter addObserverForName:AMKLaunchTimeProfilerFirstScreenDidDisplayNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) { 33 | NSLog(@"AMKLaunchTimeProfilerFirstScreenDidDisplayNotification"); 34 | 35 | // 此时已完成首屏的显示,可以参考如下代码,实现相关数据的上报 36 | // 37 | // [BaiduMobStat.defaultStat logEventWithDurationTime:@"TotalTimeConsuming" durationTime:BDESharedLaunchTimeProfiler.totalTimeConsuming*1000 attributes:@{@"TimeConsuming": @(BDESharedLaunchTimeProfiler.totalTimeConsuming*1000).stringValue}]; 38 | // [BaiduMobStat.defaultStat logEventWithDurationTime:@"PreMainTimeConsuming" durationTime:BDESharedLaunchTimeProfiler.preMainTimeConsuming*1000 attributes:@{@"TimeConsuming": @(BDESharedLaunchTimeProfiler.preMainTimeConsuming*1000).stringValue}]; 39 | // [BaiduMobStat.defaultStat logEventWithDurationTime:@"MainTimeConsuming" durationTime:BDESharedLaunchTimeProfiler.mainTimeConsuming*1000 attributes:@{@"TimeConsuming": @(BDESharedLaunchTimeProfiler.mainTimeConsuming*1000).stringValue}]; 40 | // [BaiduMobStat.defaultStat logEventWithDurationTime:@"FirstScreenTimeConsuming" durationTime:BDESharedLaunchTimeProfiler.firstScreenTimeConsuming*1000 attributes:@{@"TimeConsuming": @(BDESharedLaunchTimeProfiler.firstScreenTimeConsuming*1000).stringValue}]; 41 | }]; 42 | 43 | AMKLaunchTimeProfilerOnceLogEnd(@""); 44 | return YES; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKHomeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKHomeViewController.h 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AMKHomeViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKHomeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKHomeViewController.m 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import "AMKHomeViewController.h" 10 | 11 | @interface AMKHomeViewController () 12 | 13 | @end 14 | 15 | @implementation AMKHomeViewController 16 | 17 | #pragma mark - Dealloc 18 | 19 | - (void)dealloc { 20 | 21 | } 22 | 23 | #pragma mark - Init Methods 24 | 25 | - (instancetype)init { 26 | if (self = [super init]) { 27 | self.title = @"AMKLaunchTimeProfiler"; 28 | self.tabBarItem.title = @"Home"; 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark - Life Circle 34 | 35 | - (void)viewDidLoad { 36 | [super viewDidLoad]; 37 | AMKLaunchTimeProfilerOnceLogBegin(@""); 38 | self.view.backgroundColor = self.view.backgroundColor?:[UIColor whiteColor]; 39 | self.navigationItem.rightBarButtonItem = 40 | [UIBarButtonItem.alloc initWithTitle:@"日志" style:UIBarButtonItemStylePlain target:self action:@selector(logsBarButtonItemClicked:)]; 41 | AMKLaunchTimeProfilerOnceLogEnd(@""); 42 | } 43 | 44 | - (void)viewWillAppear:(BOOL)animated { 45 | [super viewWillAppear:animated]; 46 | } 47 | 48 | - (void)viewDidAppear:(BOOL)animated { 49 | [super viewDidAppear:animated]; 50 | AMKLaunchTimeProfilerOnceLogBegin(@""); 51 | AMKLaunchTimeProfilerOnceLogEnd(@""); 52 | } 53 | 54 | - (void)viewWillDisappear:(BOOL)animated { 55 | [super viewWillDisappear:animated]; 56 | } 57 | 58 | - (void)viewDidDisappear:(BOOL)animated { 59 | [super viewDidDisappear:animated]; 60 | } 61 | 62 | #pragma mark - Getters & Setters 63 | 64 | #pragma mark - Data & Networking 65 | 66 | #pragma mark - Layout Subviews 67 | 68 | #pragma mark - Public Methods 69 | 70 | #pragma mark - Private Methods 71 | 72 | - (void)logsBarButtonItemClicked:(id)sender { 73 | [AMKLaunchTimeProfilerLogsViewController.new presentingWithAnimated:YES completion:nil]; 74 | } 75 | 76 | #pragma mark - Notifications 77 | 78 | #pragma mark - KVO 79 | 80 | #pragma mark - Protocol 81 | 82 | #pragma mark - Overrides 83 | 84 | #pragma mark - Helper Methods 85 | 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIUserInterfaceStyle 47 | Light 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKLaunchTimeProfiler-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | 17 | # ifndef __OPTIMIZE__ 18 | # pragma clang diagnostic push 19 | # pragma clang diagnostic ignored "-Wunused-function" 20 | static NSString *_AMKLogTimeStringWithDate(NSDate *date) { 21 | static NSDateFormatter *dateFormatter = nil; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | dateFormatter = [NSDateFormatter.alloc init]; 25 | [dateFormatter setDateStyle:NSDateFormatterFullStyle]; 26 | [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; 27 | }); 28 | return [dateFormatter stringFromDate:date]; 29 | } 30 | # pragma clang diagnostic pop 31 | # define NSLog(FORMAT, ...) fprintf(stderr,"%s %s Line %d: %s\n", _AMKLogTimeStringWithDate(NSDate.date).UTF8String, __FUNCTION__, __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]) 32 | # else 33 | # define NSLog(...) {} 34 | # endif 35 | 36 | # import 37 | # import 38 | 39 | #endif 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKNavigationController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKNavigationController.h 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AMKNavigationController : UINavigationController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKNavigationController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKNavigationController.m 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import "AMKNavigationController.h" 10 | 11 | @interface AMKNavigationController () 12 | 13 | @end 14 | 15 | @implementation AMKNavigationController 16 | 17 | #pragma mark - Dealloc 18 | 19 | - (void)dealloc { 20 | 21 | } 22 | 23 | #pragma mark - Init Methods 24 | 25 | #pragma mark - Life Circle 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | if (@available(iOS 15.0, *)) { 30 | UINavigationBarAppearance *appperance = [UINavigationBarAppearance.alloc init]; 31 | appperance.backgroundColor = [UIColor colorWithRed:243/255.0 green:243/255.0 blue:243/255.0 alpha:1.0]; 32 | [appperance setTitleTextAttributes:@{NSForegroundColorAttributeName:UIColor.darkTextColor}]; 33 | self.navigationBar.standardAppearance = appperance; 34 | self.navigationBar.scrollEdgeAppearance = appperance; 35 | } 36 | } 37 | 38 | - (void)viewWillAppear:(BOOL)animated { 39 | [super viewWillAppear:animated]; 40 | } 41 | 42 | - (void)viewDidAppear:(BOOL)animated { 43 | [super viewDidAppear:animated]; 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated { 47 | [super viewWillDisappear:animated]; 48 | } 49 | 50 | - (void)viewDidDisappear:(BOOL)animated { 51 | [super viewDidDisappear:animated]; 52 | } 53 | 54 | #pragma mark - Getters & Setters 55 | 56 | #pragma mark - Data & Networking 57 | 58 | #pragma mark - Layout Subviews 59 | 60 | #pragma mark - Public Methods 61 | 62 | #pragma mark - Private Methods 63 | 64 | #pragma mark - Notifications 65 | 66 | #pragma mark - KVO 67 | 68 | #pragma mark - Protocol 69 | 70 | #pragma mark - Overrides 71 | 72 | #pragma mark - Helper Methods 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKRootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKRootViewController.h 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AMKRootViewController : UITabBarController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKRootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKRootViewController.m 3 | // AMKLaunchTimeProfiler_Example 4 | // 5 | // Created by mengxinxin on 2022/5/11. 6 | // Copyright © 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import "AMKRootViewController.h" 10 | #import "AMKNavigationController.h" 11 | #import "AMKHomeViewController.h" 12 | #import "AMKViewController.h" 13 | #import 14 | 15 | @interface AMKRootViewController () 16 | 17 | @end 18 | 19 | @implementation AMKRootViewController 20 | 21 | #pragma mark - Dealloc 22 | 23 | - (void)dealloc { 24 | 25 | } 26 | 27 | #pragma mark - Init Methods 28 | 29 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 30 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 31 | self.title = NSStringFromClass(self.class); 32 | } 33 | return self; 34 | } 35 | 36 | #pragma mark - Life Circle 37 | 38 | - (void)viewDidLoad { 39 | [super viewDidLoad]; 40 | AMKLaunchTimeProfilerOnceLogBegin(@""); 41 | self.viewControllers = @[ 42 | [AMKNavigationController.alloc initWithRootViewController:AMKHomeViewController.new], 43 | [AMKNavigationController.alloc initWithRootViewController:AMKViewController.new], 44 | ]; 45 | AMKLaunchTimeProfilerOnceLogEnd(@""); 46 | } 47 | 48 | - (void)viewWillAppear:(BOOL)animated { 49 | [super viewWillAppear:animated]; 50 | } 51 | 52 | - (void)viewDidAppear:(BOOL)animated { 53 | [super viewDidAppear:animated]; 54 | AMKLaunchTimeProfilerOnceLogBegin(@""); 55 | AMKLaunchTimeProfilerOnceLogEnd(@""); 56 | } 57 | 58 | - (void)viewWillDisappear:(BOOL)animated { 59 | [super viewWillDisappear:animated]; 60 | } 61 | 62 | - (void)viewDidDisappear:(BOOL)animated { 63 | [super viewDidDisappear:animated]; 64 | } 65 | 66 | #pragma mark - Getters & Setters 67 | 68 | #pragma mark - Data & Networking 69 | 70 | #pragma mark - Layout Subviews 71 | 72 | #pragma mark - Public Methods 73 | 74 | #pragma mark - Private Methods 75 | 76 | #pragma mark - Notifications 77 | 78 | #pragma mark - KVO 79 | 80 | #pragma mark - Protocol 81 | 82 | #pragma mark - Overrides 83 | 84 | #pragma mark - Helper Methods 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AMKViewController.h 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface AMKViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/AMKViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKViewController.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | #import "AMKViewController.h" 10 | #import 11 | 12 | @interface AMKViewController () 13 | 14 | @end 15 | 16 | @implementation AMKViewController 17 | 18 | #pragma mark - Dealloc 19 | 20 | - (void)dealloc { 21 | 22 | } 23 | 24 | #pragma mark - Init Methods 25 | 26 | - (instancetype)init { 27 | if (self = [super init]) { 28 | self.title = @"Other"; 29 | self.tabBarItem.title = self.title; 30 | } 31 | return self; 32 | } 33 | 34 | #pragma mark - Life Circle 35 | 36 | - (void)viewDidLoad { 37 | [super viewDidLoad]; 38 | AMKLaunchTimeProfilerOnceLogBegin(@""); 39 | self.title = @"Other"; 40 | self.view.backgroundColor = self.view.backgroundColor?:[UIColor whiteColor]; 41 | AMKLaunchTimeProfilerOnceLogEnd(@""); 42 | } 43 | 44 | - (void)viewWillAppear:(BOOL)animated { 45 | [super viewWillAppear:animated]; 46 | } 47 | 48 | - (void)viewDidAppear:(BOOL)animated { 49 | [super viewDidAppear:animated]; 50 | AMKLaunchTimeProfilerOnceLogBegin(@""); 51 | AMKLaunchTimeProfilerOnceLogEnd(@""); 52 | } 53 | 54 | - (void)viewWillDisappear:(BOOL)animated { 55 | [super viewWillDisappear:animated]; 56 | } 57 | 58 | - (void)viewDidDisappear:(BOOL)animated { 59 | [super viewDidDisappear:animated]; 60 | } 61 | 62 | #pragma mark - Getters & Setters 63 | 64 | #pragma mark - Data & Networking 65 | 66 | #pragma mark - Layout Subviews 67 | 68 | #pragma mark - Public Methods 69 | 70 | #pragma mark - Private Methods 71 | 72 | #pragma mark - Notifications 73 | 74 | #pragma mark - KVO 75 | 76 | #pragma mark - Protocol 77 | 78 | #pragma mark - Overrides 79 | 80 | #pragma mark - Helper Methods 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/AMKLaunchTimeProfiler/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AMKLaunchTimeProfiler 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "AMKAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AMKAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | platform :ios, '9.0' 4 | 5 | target 'AMKLaunchTimeProfiler_Example' do 6 | pod 'AMKLaunchTimeProfiler', :path => '../' 7 | pod 'Masonry' 8 | pod 'FLEX' 9 | end 10 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AMKLaunchTimeProfilerTests.m 3 | // AMKLaunchTimeProfilerTests 4 | // 5 | // Created by mengxinxin on 06/22/2022. 6 | // Copyright (c) 2022 mengxinxin. All rights reserved. 7 | // 8 | 9 | @import XCTest; 10 | 11 | @interface Tests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Tests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 MengXinxin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.assets/14448cdb434da423818f6b819ec96591.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/README.assets/14448cdb434da423818f6b819ec96591.jpg -------------------------------------------------------------------------------- /README.assets/31287f21b40610de7ca62a16bbcbe05f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/README.assets/31287f21b40610de7ca62a16bbcbe05f.jpg -------------------------------------------------------------------------------- /README.assets/4f6414aa477ba5588ffe200f3cec6e7e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/README.assets/4f6414aa477ba5588ffe200f3cec6e7e.jpg -------------------------------------------------------------------------------- /README.assets/ea4fb8a570ac2cf9769654c1ad51da1e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/README.assets/ea4fb8a570ac2cf9769654c1ad51da1e.jpg -------------------------------------------------------------------------------- /README.assets/fd7727cbed7e363acd8dffdd01f2e860.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyM129/AMKLaunchTimeProfiler/8934c6f1a6910271a32f1292bdd0cf28a5645b30/README.assets/fd7727cbed7e363acd8dffdd01f2e860.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AMKLaunchTimeProfiler
—— 简单、易用、强大的 iOS APP 冷启动耗时分析工具 2 | 3 | [![CI Status](https://img.shields.io/travis/mengxinxin/AMKLaunchTimeProfiler.svg?style=flat)](https://travis-ci.org/mengxinxin/AMKLaunchTimeProfiler) 4 | [![Version](https://img.shields.io/cocoapods/v/AMKLaunchTimeProfiler.svg?style=flat)](https://cocoapods.org/pods/AMKLaunchTimeProfiler) 5 | [![License](https://img.shields.io/cocoapods/l/AMKLaunchTimeProfiler.svg?style=flat)](https://cocoapods.org/pods/AMKLaunchTimeProfiler) 6 | [![Platform](https://img.shields.io/cocoapods/p/AMKLaunchTimeProfiler.svg?style=flat)](https://cocoapods.org/pods/AMKLaunchTimeProfiler) 7 | 8 | ## 1、Features 9 | 10 | * **零成本使用:** 通过 CocoaPods 一行代码接入,即可实现 APP 冷启动耗时统计,包括 `pre-main`、`main`、`首屏渲染`三个阶段的耗时,及总耗时 11 | 12 | * **可视化查看:** 内置日志查看页面,并自动高亮高耗时方法,各种信息一目了然 13 | 14 | * **源数据导出:** 可以方便的通过邮件、AirDrop 等方式 导出全部数据,具体包括 15 | 16 | * 日志列表的文本数据,方便在电脑上查看 17 | 18 | * 日志的JSON数据,方便基于日志的执行时间、调用方法、执行耗时 等信息 做自定义分析 19 | 20 | * 多次有效数据的平均数 数据 21 | 22 | | ![31287f21b40610de7ca62a16bbcbe05f](README.assets/31287f21b40610de7ca62a16bbcbe05f.jpg) | ![14448cdb434da423818f6b819ec96591](README.assets/14448cdb434da423818f6b819ec96591.jpg) | ![ea4fb8a570ac2cf9769654c1ad51da1e](README.assets/ea4fb8a570ac2cf9769654c1ad51da1e.jpg) | ![4f6414aa477ba5588ffe200f3cec6e7e](README.assets/4f6414aa477ba5588ffe200f3cec6e7e.jpg) | ![fd7727cbed7e363acd8dffdd01f2e860](README.assets/fd7727cbed7e363acd8dffdd01f2e860.jpg) | 23 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | 24 | | 可视化查看 | 操作选项 | 源数据导出 | 通过邮件导出 | 通过其他方式导出 | 25 | 26 | 27 | 28 | 29 | 30 | ## 2、Installation 31 | 32 | `AMKLaunchTimeProfiler` 可通过[CocoaPods](https://cocoapods.org)完成引入,仅需现在工程的`Podfile`文件中 添加如下代码 33 | 34 | ``` 35 | pod 'AMKLaunchTimeProfiler' 36 | ``` 37 | 38 | 然后在终端在`Podfile`文件所在路径下执行 `pod install`命令即可完成源码下载与引入。 39 | 40 | 41 | 42 | 43 | 44 | ## 3、Usage 45 | 46 | ### 3.1、查看相关日志 47 | 48 | 内置日志查看页面,并自动高亮高耗时方法,各种信息一目了然,具体可通过如下方式打开: 49 | 50 | 51 | 52 | #### 3.1.1、使用手势 53 | 54 | 通过 `AMKLaunchTimeProfiler.debugEnable = YES;` 启用调试模式(默认不启用),即可在 任意界面,通过「双指从右向左轻扫」的手势打开 55 | 56 | 57 | 58 | #### 3.1.2、使用编码 59 | 60 | 通过 `[AMKLaunchTimeProfilerLogsViewController.new presentingWithAnimated:YES completion:nil];` 使用代码打开 61 | 62 | 63 | 64 | 每一次启动,都会新创建 并保存一个 `AMKLaunchTimeProfiler` 实例,相关日志也都会存在该实例中,并实时持久化到本地,举例如下: 65 | 66 | ``` 67 | ====================================================================== 68 | AMKLaunchTimeProfiler id: 5c39904610203e20d26a22969df37bf9 69 | AMKLaunchTimeProfiler version: 1.0.0 70 | Bundle id: io.github.andym129.amk-launch-time-profiler 71 | Bundle short Version: 1.0 72 | Bundle version: 1.0 73 | Client Version: (null) 74 | Device Version: iPhone 11 Pro Max with iOS 15.5 75 | Device Name: Andy's iPhone 11 Pro Max 76 | 77 | ---------------------------------------------------------------------- 78 | process-start time: 2022-06-22 16:13:00 79 | total time consuming: 2.121 s 80 | pre-main time consuming: 1.716 s (80.88%) 81 | main time consuming: 0.345 s (16.24%) 82 | first screen time consuming: 0.061 s (2.87%) 83 | 84 | ---------------------------------------------------------------------- 85 | ⏱ +1.716 s ~Δ 1716 ms >> __INTERNAL__: process-start time: 2022-06-22 16:13:00 86 | ⏱ +1.716 s ~Δ 0 ms >> __INTERNAL__: main time: 2022-06-22 16:13:02 87 | ⏱ +2.023 s ~Δ 308 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke Line 18: 开始... 88 | ⏱ +2.026 s ~Δ 2 ms >> -[AMKRootViewController viewDidLoad]_block_invoke Line 40: 开始... 89 | ⏱ +2.044 s ~Δ 18 ms >> -[AMKRootViewController viewDidLoad]_block_invoke_2 Line 45: 结束 90 | ⏱ +2.060 s ~Δ 16 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 25: hello 91 | ⏱ +2.060 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 26: world 92 | ⏱ +2.060 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke_4 Line 36: 结束 93 | ⏱ +2.061 s ~Δ 1 ms >> __INTERNAL__: did finish launching time: 2022-06-22 16:13:02 94 | ⏱ +2.063 s ~Δ 3 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke Line 38: 开始... 95 | ⏱ +2.064 s ~Δ 1 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke_2 Line 42: 结束 96 | ⏱ +2.121 s ~Δ 57 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke Line 51: 开始... 97 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke_2 Line 52: 结束 98 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke Line 54: 开始... 99 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke_2 Line 55: 结束 100 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: first screen time: 2022-06-22 16:13:02 101 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 102 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: total time consuming: 2.121 s 103 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: pre-main time consuming: 1.716 s (80.88%) 104 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: main time consuming: 0.345 s (16.24%) 105 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: first screen time consuming: 0.061 s (2.87%) 106 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 107 | ⏱ +11.081 s ~Δ 8960 ms >> -[AMKViewController viewDidLoad]_block_invoke Line 38: 开始... 108 | ⏱ +11.082 s ~Δ 0 ms >> -[AMKViewController viewDidLoad]_block_invoke_2 Line 41: 结束 109 | ⏱ +11.099 s ~Δ 17 ms >> -[AMKViewController viewDidAppear:]_block_invoke Line 50: 开始... 110 | ⏱ +11.099 s ~Δ 0 ms >> -[AMKViewController viewDidAppear:]_block_invoke_2 Line 51: 结束 111 | ``` 112 | 113 | 114 | 115 | ### 3.2、导出日志 116 | 117 | 在日志查看页面,点击右上角的“···”,点击发送,即可选择“邮件”或“其他”方式导出,具体包括: 118 | 119 | #### 3.2.1、AMKLaunchTimeProfiler_554b38e7f43a36526d5b74484930d2ad.log 120 | 121 | ``` 122 | ====================================================================== 123 | AMKLaunchTimeProfiler id: 77949e4c840246c4ddedce31d8b9b54e 124 | AMKLaunchTimeProfiler version: 1.0.0 125 | Bundle id: io.github.andym129.amk-launch-time-profiler 126 | Bundle short Version: 1.0 127 | Bundle version: 1.0 128 | Client Version: (null) 129 | Device Version: iPhone 11 Pro Max with iOS 15.5 130 | Device Name: Andy's iPhone 11 Pro Max 131 | 132 | ---------------------------------------------------------------------- 133 | process-start time: 2022-06-22 16:04:53 134 | total time consuming: 3.081 s 135 | pre-main time consuming: 2.575 s (83.59%) 136 | main time consuming: 0.000 s (0.00%) 137 | first screen time consuming: 0.000 s (0.00%) 138 | 139 | ---------------------------------------------------------------------- 140 | ⏱ +2.575 s ~Δ 2575 ms >> __INTERNAL__: process-start time: 2022-06-22 16:04:53 141 | ⏱ +2.576 s ~Δ 0 ms >> __INTERNAL__: main time: 2022-06-22 16:04:56 142 | ⏱ +3.081 s ~Δ 506 ms >> __INTERNAL__: first screen time: 2022-06-22 16:04:56 143 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 144 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: total time consuming: 3.081 s 145 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: pre-main time consuming: 2.575 s (83.59%) 146 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: main time consuming: 0.000 s (0.00%) 147 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: first screen time consuming: 0.000 s (0.00%) 148 | ⏱ +3.081 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ====================================================================== 159 | AMKLaunchTimeProfiler id: 5c39904610203e20d26a22969df37bf9 160 | AMKLaunchTimeProfiler version: 1.0.0 161 | Bundle id: io.github.andym129.amk-launch-time-profiler 162 | Bundle short Version: 1.0 163 | Bundle version: 1.0 164 | Client Version: (null) 165 | Device Version: iPhone 11 Pro Max with iOS 15.5 166 | Device Name: Andy's iPhone 11 Pro Max 167 | 168 | ---------------------------------------------------------------------- 169 | process-start time: 2022-06-22 16:13:00 170 | total time consuming: 2.121 s 171 | pre-main time consuming: 1.716 s (80.88%) 172 | main time consuming: 0.345 s (16.24%) 173 | first screen time consuming: 0.061 s (2.87%) 174 | 175 | ---------------------------------------------------------------------- 176 | ⏱ +1.716 s ~Δ 1716 ms >> __INTERNAL__: process-start time: 2022-06-22 16:13:00 177 | ⏱ +1.716 s ~Δ 0 ms >> __INTERNAL__: main time: 2022-06-22 16:13:02 178 | ⏱ +2.023 s ~Δ 308 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke Line 18: 开始... 179 | ⏱ +2.026 s ~Δ 2 ms >> -[AMKRootViewController viewDidLoad]_block_invoke Line 40: 开始... 180 | ⏱ +2.044 s ~Δ 18 ms >> -[AMKRootViewController viewDidLoad]_block_invoke_2 Line 45: 结束 181 | ⏱ +2.060 s ~Δ 16 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 25: hello 182 | ⏱ +2.060 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 26: world 183 | ⏱ +2.060 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke_4 Line 36: 结束 184 | ⏱ +2.061 s ~Δ 1 ms >> __INTERNAL__: did finish launching time: 2022-06-22 16:13:02 185 | ⏱ +2.063 s ~Δ 3 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke Line 38: 开始... 186 | ⏱ +2.064 s ~Δ 1 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke_2 Line 42: 结束 187 | ⏱ +2.121 s ~Δ 57 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke Line 51: 开始... 188 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke_2 Line 52: 结束 189 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke Line 54: 开始... 190 | ⏱ +2.121 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke_2 Line 55: 结束 191 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: first screen time: 2022-06-22 16:13:02 192 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 193 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: total time consuming: 2.121 s 194 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: pre-main time consuming: 1.716 s (80.88%) 195 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: main time consuming: 0.345 s (16.24%) 196 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: first screen time consuming: 0.061 s (2.87%) 197 | ⏱ +2.121 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 198 | ⏱ +11.081 s ~Δ 8960 ms >> -[AMKViewController viewDidLoad]_block_invoke Line 38: 开始... 199 | ⏱ +11.082 s ~Δ 0 ms >> -[AMKViewController viewDidLoad]_block_invoke_2 Line 41: 结束 200 | ⏱ +11.099 s ~Δ 17 ms >> -[AMKViewController viewDidAppear:]_block_invoke Line 50: 开始... 201 | ⏱ +11.099 s ~Δ 0 ms >> -[AMKViewController viewDidAppear:]_block_invoke_2 Line 51: 结束 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | ====================================================================== 212 | AMKLaunchTimeProfiler id: 554b38e7f43a36526d5b74484930d2ad 213 | AMKLaunchTimeProfiler version: 1.0.0 214 | Bundle id: io.github.andym129.amk-launch-time-profiler 215 | Bundle short Version: 1.0 216 | Bundle version: 1.0 217 | Client Version: (null) 218 | Device Version: iPhone 11 Pro Max with iOS 15.5 219 | Device Name: Andy's iPhone 11 Pro Max 220 | 221 | ---------------------------------------------------------------------- 222 | process-start time: 2022-06-22 16:26:05 223 | total time consuming: 2.896 s 224 | pre-main time consuming: 2.263 s (78.14%) 225 | main time consuming: 0.563 s (19.43%) 226 | first screen time consuming: 0.070 s (2.43%) 227 | 228 | ---------------------------------------------------------------------- 229 | ⏱ +2.263 s ~Δ 2263 ms >> __INTERNAL__: process-start time: 2022-06-22 16:26:05 230 | ⏱ +2.263 s ~Δ 0 ms >> __INTERNAL__: main time: 2022-06-22 16:26:07 231 | ⏱ +2.791 s ~Δ 529 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke Line 18: 开始... 232 | ⏱ +2.795 s ~Δ 4 ms >> -[AMKRootViewController viewDidLoad]_block_invoke Line 40: 开始... 233 | ⏱ +2.812 s ~Δ 17 ms >> -[AMKRootViewController viewDidLoad]_block_invoke_2 Line 45: 结束 234 | ⏱ +2.825 s ~Δ 13 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 25: hello 235 | ⏱ +2.825 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:] Line 26: world 236 | ⏱ +2.825 s ~Δ 0 ms >> -[AMKAppDelegate application:didFinishLaunchingWithOptions:]_block_invoke_4 Line 43: 结束 237 | ⏱ +2.825 s ~Δ 1 ms >> __INTERNAL__: did finish launching time: 2022-06-22 16:26:08 238 | ⏱ +2.829 s ~Δ 3 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke Line 37: 开始... 239 | ⏱ +2.829 s ~Δ 1 ms >> -[AMKHomeViewController viewDidLoad]_block_invoke_2 Line 41: 结束 240 | ⏱ +2.896 s ~Δ 66 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke Line 50: 开始... 241 | ⏱ +2.896 s ~Δ 0 ms >> -[AMKHomeViewController viewDidAppear:]_block_invoke_2 Line 51: 结束 242 | ⏱ +2.896 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke Line 54: 开始... 243 | ⏱ +2.896 s ~Δ 0 ms >> -[AMKRootViewController viewDidAppear:]_block_invoke_2 Line 55: 结束 244 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: first screen time: 2022-06-22 16:26:08 245 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 246 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: total time consuming: 2.896 s 247 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: pre-main time consuming: 2.263 s (78.14%) 248 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: main time consuming: 0.563 s (19.43%) 249 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: first screen time consuming: 0.070 s (2.43%) 250 | ⏱ +2.896 s ~Δ 0 ms >> __INTERNAL__: ---------------------------------------------------------------------- 251 | ``` 252 | 253 | 254 | 255 | #### 3.2.2、AMKLaunchTimeProfiler_554b38e7f43a36526d5b74484930d2ad.txt 256 | 257 | ``` 258 | 2 次有效数据的平均数: 259 | -------------------------------------------------- 260 | total time consuming: 2.508 s 261 | pre-main time consuming: 1.989 s (79.30%) 262 | main time consuming: 0.454 s (18.08%) 263 | first screen time consuming: 0.066 s (2.61%) 264 | ``` 265 | 266 | 267 | 268 | #### 3.2.3、AMKLaunchTimeProfiler_554b38e7f43a36526d5b74484930d2ad.json 269 | 270 | ```json 271 | // 如下内容,已做精简 272 | [ 273 | { 274 | "mainTimeConsuming" : 0, 275 | "bundleId" : "io.github.andym129.amk-launch-time-profiler", 276 | "didFinishLaunchingTime" : 0, 277 | "identifier" : "77949e4c840246c4ddedce31d8b9b54e", 278 | "mainTime" : 1655885096.369308, 279 | "preMainTimeConsuming" : 2.5754809379577637, 280 | "deviceVersion" : "iPhone 11 Pro Max with iOS 15.5", 281 | "processStartTime" : 1655885093.7938271, 282 | "totalTimeConsuming" : 3.0812058448791504, 283 | "deviceName" : "Andy's iPhone 11 Pro Max", 284 | "logs" : [ 285 | { 286 | "timeDelta" : 2.5753798484802246, 287 | "function" : "__INTERNAL__", 288 | "line" : 0, 289 | "timeInterval" : 2.5753798484802246, 290 | "string" : "process-start time: 2022-06-22 16:04:53" 291 | }, 292 | ... 293 | ], 294 | "bundleName" : "AMKLaunchTimeProfiler_Example", 295 | "version" : "1.0.0", 296 | "firstScreenTimeConsuming" : 0, 297 | "bundleVersion" : "1.0", 298 | "firstScreenTime" : 1655885096.8750329, 299 | "bundleShortVersion" : "1.0" 300 | }, 301 | { 302 | "mainTimeConsuming" : 0.3446040153503418, 303 | "bundleId" : "io.github.andym129.amk-launch-time-profiler", 304 | "didFinishLaunchingTime" : 1655885582.4769759, 305 | "identifier" : "5c39904610203e20d26a22969df37bf9", 306 | "mainTime" : 1655885582.1323719, 307 | "preMainTimeConsuming" : 1.7158448696136475, 308 | "deviceVersion" : "iPhone 11 Pro Max with iOS 15.5", 309 | "processStartTime" : 1655885580.416527, 310 | "totalTimeConsuming" : 2.1213448047637939, 311 | "deviceName" : "Andy's iPhone 11 Pro Max", 312 | "logs" : [ 313 | { 314 | "timeDelta" : 1.7157728672027588, 315 | "function" : "__INTERNAL__", 316 | "line" : 0, 317 | "timeInterval" : 1.7157728672027588, 318 | "string" : "process-start time: 2022-06-22 16:13:00" 319 | }, 320 | ... 321 | ], 322 | "bundleName" : "AMKLaunchTimeProfiler_Example", 323 | "version" : "1.0.0", 324 | "firstScreenTimeConsuming" : 0.060895919799804688, 325 | "bundleVersion" : "1.0", 326 | "firstScreenTime" : 1655885582.5378718, 327 | "bundleShortVersion" : "1.0" 328 | }, 329 | { 330 | "mainTimeConsuming" : 0.56263923645019531, 331 | "bundleId" : "io.github.andym129.amk-launch-time-profiler", 332 | "didFinishLaunchingTime" : 1655886368.1614871, 333 | "identifier" : "554b38e7f43a36526d5b74484930d2ad", 334 | "mainTime" : 1655886367.5988479, 335 | "preMainTimeConsuming" : 2.2627308368682861, 336 | "deviceVersion" : "iPhone 11 Pro Max with iOS 15.5", 337 | "processStartTime" : 1655886365.336117, 338 | "totalTimeConsuming" : 2.8956289291381836, 339 | "deviceName" : "Andy's iPhone 11 Pro Max", 340 | "logs" : [ 341 | { 342 | "timeDelta" : 2.2626559734344482, 343 | "function" : "__INTERNAL__", 344 | "line" : 0, 345 | "timeInterval" : 2.2626559734344482, 346 | "string" : "process-start time: 2022-06-22 16:26:05" 347 | }, 348 | ... 349 | ], 350 | "bundleName" : "AMKLaunchTimeProfiler_Example", 351 | "version" : "1.0.0", 352 | "firstScreenTimeConsuming" : 0.070258855819702148, 353 | "bundleVersion" : "1.0", 354 | "firstScreenTime" : 1655886368.231746, 355 | "bundleShortVersion" : "1.0" 356 | } 357 | ] 358 | ``` 359 | 360 | 361 | 362 | 363 | 364 | ### 3.3、自定义日志 365 | 366 | 对于耗时较高的方法,可以通过补充自定义日志的方式,辅助找出具体的耗时的操作,进而通过优化 实现提速冷启动,相关方法如下: 367 | 368 | ```objective-c 369 | /// 打印日志 370 | #define AMKLaunchTimeProfilerLog(FORMAT, ...); 371 | 372 | /// 打印日志 - @"开始..." 373 | #define AMKLaunchTimeProfilerLogBegin(FORMAT, ...); 374 | 375 | /// 打印日志 - @"结束" 376 | #define AMKLaunchTimeProfilerLogEnd(FORMAT, ...); 377 | 378 | /// 打印日志 - 内部日志 379 | #define AMKLaunchTimeProfilerInternalLog(FORMAT, ...); 380 | 381 | /// 打印日志 - 相同位置 只打一次 382 | #define AMKLaunchTimeProfilerOnceLog(FORMAT, ...); 383 | 384 | /// 打印日志 - 相同位置 只打一次 - @"开始..." 385 | #define AMKLaunchTimeProfilerOnceLogBegin(FORMAT, ...); 386 | 387 | /// 打印日志 - 相同位置 只打一次 - @"结束" 388 | #define AMKLaunchTimeProfilerOnceLogEnd(FORMAT, ...); 389 | 390 | /// 完全自定义的日志 391 | - (void)logWithFunction:(const char *_Nullable)function line:(NSInteger)line string:(NSString *_Nullable)format, ...; 392 | 393 | ``` 394 | 395 | 396 | 397 | 398 | 399 | ## 4、Author 400 | 401 | * 邮箱:andy_m129@163.com 402 | * 掘金:https://juejin.cn/user/2875978147966855 403 | 404 | 405 | 406 | 407 | 408 | ## 5、License 409 | 410 | AMKLaunchTimeProfiler is available under the MIT license. See the LICENSE file for more info. 411 | 412 | 413 | 414 | 415 | 416 | ## 6、Thanks 417 | 418 | [![Stargazers repo roster for @AndyM129/AMKLaunchTimeProfiler](https://reporoster.com/stars/AndyM129/AMKLaunchTimeProfiler)](https://github.com/AndyM129/AMKLaunchTimeProfiler/stargazers) 419 | 420 | [![Forkers repo roster for @AndyM129/AMKLaunchTimeProfiler](https://reporoster.com/forks/AndyM129/AMKLaunchTimeProfiler)](https://github.com/AndyM129/AMKLaunchTimeProfiler/network/members) 421 | 422 | 423 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------