iOS runloop 创建一个和App生命周期相同的线程

本文介绍了一个iOS应用中实现的网络请求线程,该线程与应用生命周期相同,并通过定时器持续发起网络请求。文章详细展示了如何创建线程、管理运行循环及定时任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


//
//  NetWorkRequestThread.m
//  test_nstherad_port_02
//  Created by jeffasd on 16/7/25.
//  Copyright © 2016年 jeffasd. All rights reserved.
//  创建一个和app生命周期相同的线程并一直请求发起网络请求

#import "NetWorkRequest.h"

@interface NetWorkRequest ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation NetWorkRequest

+ (instancetype)shareNetRequest{
    
    static NetWorkRequest *_networkRequest = nil;
    if (_networkRequest == nil) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _networkRequest = [[self alloc] init];
        });
    }
    return _networkRequest;
}

+ (NSThread *)shareNetworkRequestThread{
    
    static NSThread *_networkRequestThread = nil;
    if (_networkRequestThread == nil) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkrRequestThreadEntryPoint:) object:nil];
            [_networkRequestThread start];
        });
    }
    return _networkRequestThread;
}

+(void)networkrRequestThreadEntryPoint:(id)__unused object{
    
    [[NSThread currentThread] setName:@"networkRequestThread"];
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    //添加一个port监听让runloop一直处于运行状态 好让thread不被回收
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    
#if 0
    BOOL isRuning = YES;
    while (isRuning) {
        //此方法添加的runloop可以用CFRunLoopStop(runLoopRef)来停止RunLoop的运行
        //子线程中的runmode不能使用NSRunLoopCommonModes
        //    [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        //一直监听线程是否有消息输入(default模式),有当前线程就开始工作,没有就休眠。进行一次消息轮询,如果没有任务需要处理的消息源,则直接返回
        BOOL isRuning = [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        //此方法停止runloop要设置isRuning为NO 不是很方便 使用 CFRunLoopRun(); 方法来代替
        NSLog(@"isRuning is %@", isRuning ? @"YES" : @"NO");
    }
#endif
    
    //可以很方便暂停runloop循环
    CFRunLoopRun();
    
}

- (void)start{
    
    [self performSelector:@selector(startNetWorkRequest) onThread:[[self class] shareNetworkRequestThread] withObject:nil waitUntilDone:NO];
}

- (void)startNetWorkRequest{
    
//    NSThread *currentThread = [NSThread currentThread];
//    NSLog(@"currentThread is %@", currentThread);
//    NSLog(@"runLoop is %@", [NSRunLoop currentRunLoop]);
    NSLog(@"do Something");
    
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(getMessageFormRemoteService) userInfo:nil repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    
}

- (void)getMessageFormRemoteService{
    
    NSLog(@"currentThread is %@", [NSThread currentThread]);
    NSLog(@"service -- ");
}

- (void)cancel{
    
    NSThread *therad = [[self class] shareNetworkRequestThread];
    
    NSLog(@"the thread is %@", therad.executing ? @"YES" : @"NO");
    NSLog(@"the thread is %@", therad.finished ? @"YES" : @"NO");
    NSLog(@"the thread is %@", therad.cancelled ? @"YES" : @"NO");
    
    NSLog(@"the thread is %@", therad);
    
    [self performSelector:@selector(cancelNetWorkRequest) onThread:[[self class] shareNetworkRequestThread] withObject:nil waitUntilDone:NO];
}

- (void)cancelNetWorkRequest{
    
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"currentThread is %@", currentThread);
    
    CFRunLoopStop([NSRunLoop currentRunLoop].getCFRunLoop);
//    CFRunLoopStop(CFRunLoopGetCurrent());
}

@end


<think>嗯,用户问的是iOS APP的启动流程。首先我需要回想一下自己之前学过的相关知识,可能包括应用启动的不同阶段,比如pre-mainmain之后的部分。用户可能是个iOS开发者,或者是刚入门的,想了解应用的启动过程来优化启动时间或者解决问题。 首先,得确定用户的需求。他们可能想知道从点击APP图标到看到第一个界面整个过程中系统做了什么。可能需要分阶段解释,比如pre-main阶段包括哪些步骤,main之后又有哪些。同时,用户可能对如何优化启动时间感兴趣,所以在回答中加入优化建议会更好。 接下来要整理启动流程的各个阶段。比如dyld加载动态库,runtime初始化,然后是main函数执行,UIApplicationMain的设置,直到第一个视图控制器显示。每个阶段的关键点是什么,比如在pre-main阶段,动态链接器如何工作,runtime如何进行类注册、category处理等。 可能还需要提到启动时间的测量方法,比如设置环境变量DYLD_PRINT_STATISTICS来获取pre-main的时间。此外,冷启动热启动的区别也是需要注意的点,用户可能想知道这两者的不同处理流程。 还要考虑用户可能存在的误区,比如认为main函数是启动的全部,但实际上pre-main阶段同样重要。需要澄清这些概念,确保解释清楚每个阶段的作用顺序。 另外,优化部分需要具体,比如减少动态库数量,使用静态库,控制+load方法的使用,延迟初始化等。这些都是实际开发中常用的技巧,用户可能希望得到实用的建议。 最后,要检查回答的结构是否清晰,分点是否明确,是否覆盖了启动流程的所有关键步骤,以及是否提供了有用的优化方法。确保语言通俗易懂,避免过于专业的术语,或者在必要时进行解释,让不同层次的读者都能理解。</think>iOS应用的启动流程可以分为**pre-main阶段****main函数后阶段**两个核心部分,以下是详细流程说明: --- ### 一、Pre-Main阶段(系统主导) #### 1. 内核准备 ```mermaid graph TD A[用户点击图标] --> B[创建进程] B --> C[分配虚拟内存] C --> D[加载主二进制文件] ``` - 分配4GB虚拟内存空间(实际物理内存按需分配) - 解析Mach-O文件头(检查架构兼容性) #### 2. dyld动态链接 ```objc // dyld核心任务: 1. 递归加载依赖的动态库(UIKit, Foundation等) 2. 符号绑定(Rebase/Bind) 3. 运行初始化方法(+load方法) ``` - 动态库加载顺序:主程序 → 系统库 → 嵌入的第三方库 - 符号修正:ASLR地址偏移修正(PIC技术) #### 3. Runtime初始化 ```objc // objc运行时核心操作: _objc_init() → map_images() // 类/协议注册 → load_images() // 执行+load方法 → initialize() // 首次调用前的准备 ``` - 注册所有OC类(包括Category添加的方法) - 初始化C++静态对象 --- ### 二、Main函数后阶段(开发者可控) #### 1. main()入口 ```objc int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } ``` - 最后一个参数决定主AppDelegate类 #### 2. UIApplication初始化 ```objc UIApplicationMain() → init UIApplication实例 → 创建Main RunLoop → 触发AppDelegate生命周期回调 ``` #### 3. 生命周期回调顺序 ```mermaid sequenceDiagram participant D as didFinishLaunching participant C as configurationForConnecting participant W as windowScene participant R as rootViewController AppDelegate->>AppDelegate:D:application:didFinishLaunching AppDelegate->>SceneDelegate:C:configurationForConnecting SceneDelegate->>SceneDelegate:D:scene:willConnectToSession SceneDelegate->>UIWindow:initWithWindowScene: UIWindow->>UIWindow:setRootViewController: UIWindow->>UIWindow:makeKeyAndVisible ``` --- ### 三、关键性能指标 | 阶段 | 优化重点 | 参考耗时 | |---------------|--------------------------|----------| | pre-main | 减少动态库/ObjC类数量 | ≤400ms | | main()到首帧 | 延迟非必要初始化 | ≤1s | | 完整启动 | 冷启动/热启动区分 | ≤2s | --- ### 四、启动优化策略 1. **动态库处理** ```shell # 查看动态库依赖 otool -L YourApp.app/YourApp ``` - 将多个动态库合并为单个 - 尽量使用静态库(.a文件) 2. **+load方法优化** ```objc // 避免在+load中执行耗时操作 + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 必要初始化 }); } ``` 3. **首屏渲染加速** ```swift // 使用LaunchScreen.storyboard代替静态图片 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 异步执行非关键任务 DispatchQueue.global().async { setupAnalytics() } return true } ``` --- ### 五、调试技巧 1. **测量pre-main耗时** ```shell # Xcode添加环境变量 DYLD_PRINT_STATISTICS = 1 ``` 输出示例: ``` Total pre-main time: 350ms dylib loading time: 150ms rebase/binding time: 80ms ObjC setup time: 70ms initializer time: 50ms ``` 2. **Time Profiler分析** - 使用Instruments的`App Launch`模板 - 捕获从进程创建到首帧渲染的完整调用树 --- ### 六、冷启动 vs 热启动 | 类型 | 特征 | 优化重点 | |----------|----------------------------|-----------------------| | 冷启动 | 进程不在内存,需完整初始化 | pre-main阶段优化 | | 热启动 | App仍在后台 | 恢复状态时的资源加载 | 实际开发中推荐使用`FBRetainCycleDetector`等工具检测启动期间的循环引用问题,同时注意`NSUserDefaults`的首次读取可能造成I/O阻塞。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值