从 C 语言的 main 入手看 iOS 应用启动过程及进化

简介: 从 C 语言的 main 入手看 iOS 应用启动过程及进化太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)本文遵循“署名-非商业用途-保持一致”创作公用协议转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。

从 C 语言的 main 入手看 iOS 应用启动过程及进化

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino否则,出自本博客的文章拒绝转载或再转载,谢谢合作。


iOS 应用启动流程,这个话题早在09年就非常熟悉,然而时隔多年,不知是否还熟悉,尤其 StoryBoard 的引入,那么下面就一起来看看吧,如果确实说明白了,给个评论,或哪里有不足,需要完善,也给个指点。


由于 Objective-C 是对 C 的扩展,那么 main 函数理所当然地继承了程序入口的位置,而不像安卓,虽然它的程序入口点可能也是 main ,但那是掩埋在系统框架之内根源处的,也或许叫别的名字,想了解可参考Android系统启动过程


在 XCode 5.1.1 (2014-07-20 周日,此时 iOS 8 已经发布,但还未正式上架应用,beta 3 据说已经可以供开发者偿鲜)中新建一个单视图应用 (Single View Application)。


XCode 工程中总有很多罗里巴山的文件,不过这也正是它先进之处,控制权集中,撒出多个点,来供开发者配置以改变应用的运行效果,或许用傻瓜式的应用架构方式更贴切一些,不过,像 iOS 这样不开源的架构,是否长此以往,我们的后代人是否会真的变成傻瓜,对架构内部的程序艺术完全不了解,丧失了这种架构能力了呢?!


切入主题,程序入口 main.m 文件如下:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
main 函数,和 C 语言中的一模一样,带两个参数,argc 是参数个数,argv 是参数的字符串数组,或者叫列表也行。


上面两行中 #import 是Objective-C 中新引入的和 #include 宏指令一样的功能,引入其它头文件。

之所以要引入这个新的指令来包含头文件,是因为 #include 会存在重复引入的问题,即一个头文件被引入多次,那么就可能定义了多个对象或变量,那是会出错的。

所以,在 C 中,会使用宏指令来判断一个头文件中的预定义宏名是否存在,不存在则在宏条件判断中使用 #include 引入头文件,否则不走这一宏分支,头文件就不会被引入。

Availability.h 头文件的精简结构如下,虽然在 -Prefix.pch 文件中是使用 #import 引入的该头文件,但也不可掉以轻心,因为该文件还有可能在 C 代码中使用 #include 引入,所以仍然加了 C 样式的唯一引入宏结构:

#ifndef __AVAILABILITY__
#define __AVAILABILITY__

#include <AvailabilityInternal.h>

#endif /* __AVAILABILITY__ */


这么麻烦地对头文件进行处理,真是浪费时间,那么 #import 应运而生,不用担心头文件重复引入的问题。不过,别高兴得太早,循环引用的问题,还是没办法解决的,这个就要用到另一个 @class 来声明类名的存在性以便在声明文件中定义对象引用,而实际头文件引入放在实现文件中。对于复杂些的逻辑,还是得靠你的整体把握能力来避勉循环引用:即 A 引入 B,B 引入 A 。


iOS 应用程序中最常,也算是基本约定俗成的,要引入两个框架:UIKit 和 Foundation。

而在 main.m 文件中,注释掉 UIKit 的引入,程序也是会正常运行的,因为并未用到 UIKit,只是程序模板默认加上的,可能是其它类型应用会用到吧,也许!


自动释放池,这里不作深入介绍,因为我们当前创建的应用,都默认是支持 ARC (自动引用计数 Automatic Reference Counting)的,所以使用这种配套的自动释放池方法。

@autoreleasepool {

}


早先的方式,已经过时,这种新的方式,也支持 MRC(手动引用计数 Manual Reference Counting)源文件的引入,只不过需要对源文件编译部分进行相应参数设置,需加上 

 -fno-objc-arc

相反,在早期的 MRC 项目中,所使用的方式已经过时,当下的 XCode 不会给你创建这样的模板代码,而且在用以 ARC 为主的工程时,那个也不会得到预编译的很好处理。如果确实需要的话,在 MRC 项目中引入 ARC 的源文件,在该 ARC 源文件的编译选项中,输入

 -fobjc-arc

ARC 无非就是预编译时,由编译器根据需要替你把需要加上的 MRC 中的相应内存管理的代码加上去,而非真正的动态内存管理,即垃圾回收,所以它的效率和内存占用率要优质得多。



接下来,

UIApplicationMain
将成为重点内容,它就相当于 MFC 中 WinMain  函数。

对于有窗体的应用,它要呈现界面元素,并响应用户的操作,比如鼠标、键盘、触屏等用户操作事件,以及移动设备的各类传感器事件等等。

这就要求带窗体的应用,要能够循环查询到这样的事件,或许事件直接触发可能会更快捷和节省资源,但那样的后果是耦合性太强了。

所以,轮询和事件触发相结合,适当的分配,才能达到预期的效果。事件由系统分发给每个或者移动设备的当前应用,这需要由事件缓存池来完成,而每个应用轮询期自身得到的事件,或由外部置入事件池,或由内部发生,得到这个事件后,再进行触发,而将需要直接调用的回调函数进行缓存,注意啦,在应用内,变成了缓存事件回调函数,而非事件本身,事件本身相当于直接传递。由此就很好地解决了轮询带来的低效率和高资源占用与直接回调的耦合紧密等等弱点。


UIApplicationMain 函数有四个参数:

UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

第一个和第二个参数是 C 程序体的传入参数,直接传入,不做任何加工;

第三个参数是要创建的 iOS 应用实例的类,该类是 UIApplication 或其子类,每个应用仅此一个;

第四个参数是要创建的应用代理类,该类的功能其实是可以在应用类中完成的,不过这种代理模式能很好地把这部分功能从应用类中分离出来,这也是苹果采用代理模式处理各种事件响应的高明之处。开发人员只要按代理方法的要求进行处理就可以了,对于统一规范预期工作任务很有帮助,不至于由于开发人员的疏忽,而把程序结构搞得混乱。


下面就针对 UIApplicationMain 未指定代理类和指定代理类两种情况对进一步的应用启动过程作以简要叙述。

还有就是主窗体的来源,应用类实例会从 xxx-Info.plist 中读取相关配置信息。

其中 Main nib file base name 决定了从 nib 文件还是 StoryBoard 中获取应用的窗口信息,

1、如果是 nib,那么主窗口也从该文件中读取,接下来,就可以从应用代理中,给主窗口设置根视图控制器;

    如果 UIApplicationMain 的第四个参数应用代理类为 nil 的话,应用不知如何找到应用代理类进行实例化,它就会在这个主 nib 文件中寻找,寻找的规则是:

    (这句话是错的,不是第四参为空,才从 nib 中取应用代理,而是指定了主 nib ,就会默认在这里找,如果找不到,再使用第四参)

    a、主 nib 文件中的 File's Owner 是当前应用类,所以设成 UIApplication 或你指定的子类,然后在主 nib 中添加一个对象,设定它为应用代理类,

          并从 File's Owner 拉线到刚加入的设成应用代理类的对象上,这时会弹出选项,选择 Outlets 下的 delegate,这样就把应用代理类关联到了应用类的代理引用上了。

          以下 UIApplication.h 中 UIApplication 的声明可以让你明白应用类与应用代理类的关系是组合的方式,至于面向对象复用机制的两种方式继承与组合,可以参见 继承和组合

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIApplication : UIResponder <UIActionSheetDelegate>
{
  @package
    id <UIApplicationDelegate>  _delegate;
    b、如果仍找不到的话,也即 File's Owner 所对应的应用类的 delegate 未指定应用代理类,就会偿试使用   UIApplicationMain 的第四个参数应用代理类。

    c、如果 主 nib 中 File's Owner 没给应用的 delegate 指定代理类,而且 UIApplicationMain 的第四个参数也为 nil,这时应用一样会启动, nib 中的主窗口一样会显示,但没有代理类,无法接收到应用类的应用生命周期的相关事件。

2、如果是 StoryBoard,主窗口就由应用自动创建,并将 StoryBoard 的第一个视图控制器作为主窗口的根视图控制器。

3、如果为空,则应用就不管主窗口的事情了,由开发者自已来搞定,此时运行,会在模拟器中看到黑屏应用,即没有窗口,当然了,状态栏作为窗口的一部分。

      那么,主窗口从哪儿创建呢?这就涉及到应用代理类了,因为窗口是可以动态创建并指定的,而且窗口是可以有多个的,但主窗口只有一个,并且只有一个处于可见状态。


写了一下午,边测试,边确认,边构思、组织语言与代码,尚缺一些实际测试过程的截图确认,这个后续再补上。

好像是缺点什么,当时想到了,专注于某一个话题后再回到上面来俯视,发现忘记了......

回头再说,想起来,随时补充。

当然了,这其中还包括,查到的与我这里话题相当的十几二十篇非常不错的参考资料,先建个所有Chrome 的标签组吧,今天真的有点累了,该天再续。





目录
相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
160 4
|
2月前
|
存储 算法 C语言
通义灵码在考研C语言和数据结构中的应用实践 1-5
通义灵码在考研C语言和数据结构中的应用实践,体验通义灵码的强大思路。《趣学C语言和数据结构100例》精选了五个经典问题及其解决方案,包括求最大公约数和最小公倍数、统计字符类型、求特殊数列和、计算阶乘和双阶乘、以及求斐波那契数列的前20项和。通过这些实例,帮助读者掌握C语言的基本语法和常用算法,提升编程能力。
89 4
|
2月前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
2月前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
77 1
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
26天前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
43 1
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
27天前
|
网络协议 物联网 数据处理
C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势
本文探讨了C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势。文章详细讲解了使用C语言实现网络通信程序的基本步骤,包括TCP和UDP通信程序的实现,并讨论了关键技术、优化方法及未来发展趋势,旨在帮助读者掌握C语言在网络通信中的应用技巧。
38 2
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。