问题1: IOS APP生命周期
iOS APP的生命周期指的是从应用启动到终止期间所经历的一系列状态和事件。了解这些生命周期阶段对于开发者来说至关重要,因为它影响到如何管理资源、保存数据以及在不同状态下如何响应用户交互。
以下是iOS APP生命周期的简化概述:
启动(Launch):
用户点击APP图标,系统开始加载APP。
main 函数被调用,这是APP的入口点。
UIApplicationMain 函数启动事件循环。
AppDelegate 的 application(_:didFinishLaunchingWithOptions:) 被调用,用于应用的最后初始化。前台活动(Foreground Active):
APP在前台运行,用户可以与之交互。
此时,APP处于活动状态,可以接收触摸事件和其他用户输入。进入后台(Entering Background):
用户按下Home键或切换至其他APP,当前APP进入后台。
applicationWillResignActive(:) 被调用,表示APP即将变为非活动状态。
紧接着,applicationDidEnterBackground(:) 被调用,此时APP已经在后台运行。后台运行(Background Running):
APP在后台仍然可以执行一些特定的任务,但会受到系统限制。
开发者可以请求额外的后台执行时间来完成重要任务。进入前台(Entering Foreground):
当用户重新打开APP时,它会从前台非活动状态变为活动状态。
applicationWillEnterForeground(:) 在APP即将进入前台之前被调用。
applicationDidBecomeActive(:) 在APP已经完全进入前台并且处于活动状态时被调用。终止(Termination):
在某些情况下,系统可能会决定终止APP以释放内存,这通常发生在内存紧张时。
applicationWillTerminate(_:) 在APP即将被终止时被调用,但这个方法在实际使用中不常触发,因为iOS会尽量保持APP在后台而不是直接终止它。挂起(Suspended):
如果APP在后台停留了很长时间且没有执行任何后台任务,系统可能会将其挂起,以释放资源给其他正在前台运行的APP。
挂起状态下,APP保持在内存中但不执行任何代码。
开发者需要在这些生命周期方法中添加适当的逻辑,以确保资源的正确管理和用户数据的保存。同时,也要注意遵循iOS的后台执行和省电策略,以避免影响用户体验和系统性能。
问题2 最好也是要提一下, 从冷启动开始
问题1追问: 简述main之后调用方法
-
执行
main→UIApplicationMain创建UIApplication对象
创建UIApplication的delegate对象
开启消息循环
-
delegate对象开始监听系统事件
app启动完成后会调用delegate对象的程序加载完成方法
didFinishLaunchingWithOptions-
如果设置了main storyboard 会创建UIWindow对象(没有则不创建)
创建main storyboard中绑定的controller对象(stroyboard中箭 头指向的控制器)
设置给window对象的rootViewController
设置window对象可见
问题2: IOS APP 从冷启动开始都做了什么
iOS APP的冷启动过程是一个复杂而有序的流程,它涉及多个阶段和组件的初始化。以下是一个典型的从冷启动开始所经历的主要步骤:
- 启动阶段:
用户点击APP图标后,系统内核(Kernel)会创建一个新的进程来运行这个APP。
系统加载APP的可执行文件,这是一个Mach-O格式的文件,它包含了APP的所有代码和资源。
接着,系统会加载动态链接器(dyld),这是Apple的动态链接器,负责装载Mach-O文件(如可执行文件、动态库等)。
- dyld的工作:
dyld会分析应用依赖的动态库,并递归加载这些库。
进行rebase指针调整和bind符号绑定。rebase是因为iOS采用了ASLR技术(地址空间布局随机化),加载APP的内存地址是随机的,所以需要根据随机的偏移量对原来的地址做重定向。bind则是进行符号绑定,指向image外部动态库的指针被符号(symbol)绑定。
初始化ObjC运行时环境,包括注册ObjC类、初始化类对象等。
调用所有类和分类的+load方法,以及调用C++静态初始化器和由attribute((constructor))修饰的函数。
- main函数执行:
在完成了上述动态链接和初始化工作后,dyld会调用main函数,这标志着APP正式进入执行阶段。
在main函数中,通常会设置应用程序的代理(AppDelegate),并开始应用的事件循环。
- AppDelegate的初始化:
在AppDelegate中,最重要的方法是application(_:didFinishLaunchingWithOptions:),这个方法在应用启动并完成初始化后被调用。开发者通常在这里进行应用的最后初始化设置,如设置根视图控制器、配置全局变量等。
- 首屏渲染:
设置完根视图控制器后,应用会开始进行首屏的渲染工作。这通常包括加载和布局界面元素、加载数据等。只有当首屏渲染完成后,用户才能看到应用的界面并开始与之交互。
- 后续执行:
在冷启动完成后,应用会进入正常的运行状态,响应用户的操作和执行相关的业务逻辑。
需要注意的是,冷启动过程中涉及很多底层的操作和系统调用,这些都是由iOS系统自动完成的。开发者主要关注的是AppDelegate中的初始化设置和首屏渲染的性能优化,以确保应用能够快速、流畅地启动并呈现给用户。
问题2追问: Apple的动态链接器dyld怎么装载Mach-O文件 (问的少)
Apple的动态链接器dyld装载Mach-O文件的过程是一个复杂而精细的流程,涉及多个步骤。以下是dyld装载Mach-O文件的基本过程:
准备阶段:在iOS系统中,每个程序依赖的动态库都需要通过dyld来加载。当系统内核做好程序启动的准备后,会将控制权交给dyld来进行后续的加载工作。
加载共享缓存:为了提高程序启动速度和运行效率,iOS系统使用了共享缓存机制。多个系统库被合并成一个大的缓存文件,存放在特定的目录下。当dyld需要加载某个系统库时,它会首先从共享缓存中提取相应的库。
处理Mach-O文件:dyld接下来会处理Mach-O文件。Mach-O是Apple用于可执行文件、目标代码、动态库以及内核转储的标准文件格式。dyld需要解析Mach-O文件的头部信息,获取关于文件结构、符号表、重定位表等的关键信息。
绑定和重定向:在加载过程中,dyld会进行符号绑定和重定向。这涉及到将Mach-O文件中的符号引用与实际的内存地址相关联。由于iOS采用了地址空间布局随机化(ASLR)技术,因此加载时的地址是随机的,需要进行相应的rebase(重定位基地址)和bind(符号绑定)操作。
初始化Objective-C运行时:如果Mach-O文件中包含Objective-C代码,dyld还需要初始化Objective-C运行时环境,包括注册类、初始化类对象等。
调用初始化方法:在加载完Mach-O文件并完成相关初始化后,dyld会调用所有类和分类的+load方法,以及C++静态初始化器和由attribute((constructor))修饰的函数。
查找并返回程序入口点:最后,dyld会查找Mach-O文件的入口点,并将控制权交给程序的主函数,从而完成整个加载过程。
需要注意的是,dyld的装载过程涉及很多底层的操作和系统调用,这些都是由iOS系统自动完成的。开发者一般不需要直接参与这个过程,但了解这些原理有助于更好地理解iOS应用程序的启动和运行机制。
问题3: AppDelegate相关代理方法
- 启动基本完成, 程序准备开始运行
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- 程序将要进入非活动状态
- (void)applicationWillResignActive:(UIApplication *)application
- 程序已进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application
- 程序将要进入前台
- (void)applicationWillEnterForeground:(UIApplication *)application
- 程序进入活跃状态
- (void)applicationDidBecomeActive:(UIApplication *)application
- 程序将要退出
- (void)applicationWillTerminate:(UIApplication *)application
问题4: ViewController的生命周期
当一个视图控制器被创建,并在屏幕上显示的时候。 代码的执行顺序
-
alloc
创建对象,分配内存空间 -
init
对象初始化 -
awakeFromNib
xib或者storyboard创建view, 加载完成之后执行 -
loadView
用于创建Controller的View -
viewDidLoad
View加载完成,视图控制器将视图层次结构加载到内存之后调用 -
viewWillAppear
视图将要出现 -
viewWillLayoutSubviews
通知视图控制器其视图将要布局它的子视图。 -
viewDidLayoutSubviews
通知视图控制器其视图已完成子视图布局。 -
viewDidAppear
视图已完成屏幕上渲染 -
viewWillDisappear
视图将被移除 -
viewDidDisappear
视图已被移除 -
dealloc
视图销毁,对init和viewDidLoad中创建的对象进行释放 -
didReceiveMemoryWarning
如果应用收到内存警告时会调用此方法。
问题5: UIView的生命周期
-
initWithFrame / init
视图初始化 -
didAddSubview
添加子视图 (没有子视图不调用)
initWithCoder
xib初始化视图后调用, 如果xib中添加了子控件会在didAddSubview方法后调用 -
awakeFromNib
xib创建view, 加载完成之后执行 -
willMoveToSuperview
视图即将加入父视图 / 视图即将从父视图移除 调用 -
didMoveToSuperview
视图已加入父视图 / 视图已从父视图移除 调用 -
willMoveToWindow
视图即将加入window视图 / 视图即将从window视图移除 调用 -
didMoveToWindow
视图已加入window视图 / 视图已从window视图移除 调用 -
layoutSubviews
布局子控件 -
drawRect
绘制视图 -
willRemoveSubview
子视图从本视图移除时调用 (没有子视图不调用) -
removeFromSuperview
从父控件中移除 -
dealloc
销毁
问题6: APP启动有几种
冷启动: 从零/杀死启动APP热启动: APP存在后台, 点击APP图标再次启动, Background → Foreground