ios应用程序启动过程
ios应用程序启动过程
Lambda8421 发表于3年前
ios应用程序启动过程
  • 发表于 3年前
  • 阅读 145
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 转载
iOS 应用程序运行流程





UIApplicationMain
  main.m main 函数中执行了 UIApplicationMain 这个方法,这 是 ios 程序的入口点  
intUIApplicationMain (intargc,char*argv[], NSString *principalClassName, NSString *delegateClassName)  
  argc argv : ISO C 标准 main 函数的参数,直接传递 给 UIApplicationMain 进行相关处理即可  
  principalClassName :指定应用程序类,该类必须 是 UIApplication ( 或子类 ) 。如果为 nil, 则用 UIApplication 类 作为默认值  
  delegateClassName :指定应用程序类的代理类,该类必须遵 守 UIApplicationDelegate 协议  

UIApplicationMain
  此函数会根据 principalClassName 创建 UIApplication   对象,根据 delegateClassName 创建一个 delegate 对象 ,并将该 delegate 对象赋值给 UIApplication 对象中 的 delegate 属性
  UIApplication 对象会依次给 delegate 对象发送不同的 消息,接着会建立应用程序的 main runloop (事件循环) ,进行事件的处理 ( 首先会调用 delegate 对象的
application:didFinishLaunchingWithOptions:)
  程序正常退出时这个函数才返回。如果进程要被系统强制 杀死,一般这个函数还没来得及返回进程就终止了


  如果设置了主 xib 文件(在 Info.plist 中指定 , key NSMainNibFile ),就会在主 xib 文件中 寻找 UIApplication 和连接它的 delegate 。因 此在主 xib 文件中, File’s Owner 必须 为 UIApplication( 或子类 ) ,并且建立一个遵 守 UIApplicationDelegate delegate 对象, 建立 UIApplication delegate 对象的关联关系
四大对象关系图


iOS 中的 mvc
 

UIApplication
  UIApplication 是应用程序的核心,每一个程序在运行期必须 有 UIApplication (或子类)的一个实例( 有且仅有一个 ),通 过 [UIApplication sharedApplication] 可以得到这个单例实例 的指针
  UIApplication 帮助管理应用程序的生命周期,而它通过 delegate   来履行这个任务
  UIApplication 可以接收事件,把所有用户事件都放入队列,逐个 处理,它会发送当前事件给一个合适的目标控件进行处理。它还将 部分   事件转给 delegate 对象来处理 ,delegate 可处理的事件包括:应用程 序的生命周期事件 ( 如程序启动和关闭 ) 、系统事件 ( 如来电 )


UIApplication
  [UIApplication sharedApplication].windows :   在本应用中打开的 UIWindow 列表,这样就可以接触应用
中的任何一个 UIView 对象
  [UIApplication sharedApplication].keyWindow :   用来接收 键盘 以及 非触摸类 的消息事件的 UIWindow ,而
且程序中每个时刻只能有一个 UIWindow keyWindow   如果某个 UIWindow 内部的文本框不能输入文字,可能是 因为这个 UIWindow 不是 keyWindow

下面是这个类的一些功能:
1. 设置 icon 上的数字图标
// 设置主界面 icon 上的数字图标,在 2.0 中引进,   缺省为 0
[UIApplicationsharedApplication].applicationIconBadgeNumber = 4;
2.
设置摇动手势的时候,是否支持 redo,undo 操作
//
摇动手势,是否支持 redo undo 操作。
//3.0
以后引进,缺省 YES
[UIApplicationsharedApplication].applicationSupportsShakeToEdit =
YES ;
3. 判断程序运行状态
// 判断程序运行状态,在 2.0 以后引入
/*
UIApplicationStateActive,
UIApplicationStateInactive,
UIApplicationStateBackground
*/
if([UIApplicationsharedApplication].applicationState ==UIApplicationStateInactive){
NSLog(@"
程序在运行状态 ");
}
4.
阻止屏幕变暗进入休眠状态
//
阻止屏幕变暗,慎重使用 , 缺省为 no 2.0
[UIApplicationsharedApplication].idleTimerDisabled =YES;
(慎重使用本功能,因为非常耗电)
5.
显示联网状态
//
显示联网标记   2.0
[UIApplicationsharedApplication].networkActivityIndicatorVisible =YES;

6. map 上显示一个地址
NSString* addressText =
@"1 Infinite Loop, Cupertino, CA 95014" ;
// URL encode the spaces
addressText= [addressTextstringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString* urlText = [NSStringstringWithFormat:@"
http://maps.google.com/maps?q=%@ ", addressText];
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:urlText]];

7. 发送电子邮件
NSString *recipients =
@"mailto:first@example.com?cc=second@example.com,third@example.com&subject=Hello from California!" ;
NSString *body =
@"&body=It is raining in sunny California!" ;
NSString *email = [NSStringstringWithFormat:
@"%@%@" , recipients, body];
email = [emailstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:email]];
8. 打电话到一个号码
// Call Google 411
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@" tel://8004664411 "]];

9. 发送短信
// Text to Google SMS
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@"sms://466453"]];

10. 打开一个网址
// Lanuch any iPhone developers favsite
[[UIApplicationsharedApplication]openURL:[NSURLURLWithString:@" http://itunesconnect.apple.com "]];

UIApplicationDelegate
  在开发过程中, UIApplication 是一个非常重要的全局对象。但在实 际编程中我们并不直接和 UIApplication 对象打交道,而是和其代理 打交道,它的代理必须遵守 UIApplicationDelegate 协议,代理 ��   供了相关的生命周期方法来处理应用程序的系统事件
>
    ios 设备的内存极其优先,如果为 app 分配了太多内存,操作系统会终 止 app 的运行,在 UIApplication 接收到这个事件后它会调用代理 的 applicationDidReceiveMemoryWarning 方法,代理在这个方 法内可以进行释放内存的操作以防止操作系统强制终止应用程序的运行
>
 
>
  UIApplicationDelegat
>
    ios 并不是多任务的操作系统,所以 app 很容易受到打扰。比如一个来 电可能导致 app 失去焦点,如果这个时候接听了电话,那么 app 会自动 终止运行  
>
    还有很多其它类似的事件会导致 app 失去焦点
>
    app 失去焦点前 会调用代理的 applicationWillResignActive  
>
    app 再次获取焦点 时会调用代理的 applicationDidBecomeActive  
>
    在运行 app 锁屏 会调用代理的 applicationWillResignActive  
>
    屏幕被解锁 时,会调用代理的 applicationDidBecomeActive
>
  UIApplicationDelegate 生命周期方法说明
>
  1 - ( void )applicationWillResignActive:(UIApplication *)application
> / /
从主动到非活动状态的应用程序时发送。这可导致产生某些类型的临时中断(如传入电话呼叫或 SMS 消息)   ,或者当用户退出应用程序,它开始过渡到的背景状态。
>     / /
使用此方法暂停正在进行的任务,禁用定时器,踩下油门, OpenGL ES 的帧速率。游戏应该使用这种方法来暂停游戏。
>
 
>
  2 - ( void )applicationDidBecomeActive:(UIApplication *)applicatio
>
  说明:当应用程序入活动状态执行,这个刚好跟上面那个方法相反
>
  3 - ( void )applicationDidEnterBackground:(UIApplication *)application
>
  说明:当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可
> / /
使用这个方法来释放共享资源,保存用户数据,废止定时器,并存储足够的应用程序状态信息的情况下被终止后,将应用程序恢复到目前的状态。
>     / /
如果你的应用程序支持后台运行,这种方法被调用,而不是 applicationWillTerminate  :当用户退出。
>
 
>
  4 - ( void )applicationWillEnterForeground:(UIApplication *)applicatio
>
  说明:当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反。
>
  5 - ( void )applicationWillTerminate:(UIApplication *)applicatio
>
  // 不支持多任务的时候调用
>
>
  说明:当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作。这个需要要设置 UIApplicationExitsOnSuspend 的键值(自动设置)。
>
  6 - ( void )applicationDidReceiveMemoryWarning:(UIApplication *)applicatio
>
  说明: iPhone 设备只有有限的内存,如果为应用程序分配了太多内存操作系统会终止应用程序的运行,在终止前会执行这个方法,通常可以在这里进行内存清理工作防止程序被终止
>
  7 - ( void )applicationSignificantTimeChange:(UIApplication*)applicatio
>
  说明:当系统时间发生改变时执行
>
  8 - ( void )applicationDidFinishLaunching:(UIApplication*)applicatio
>
  说明:当程序载入后执行
>
  9 - ( void )application:(UIApplication)application willChangeStatusBarFrame:(CGRect)newStatusBarFram
>
  说明:当 StatusBar 框将要变化时执行
>
  10 - ( void )application:(UIApplication*)application willChangeStatusBarOrientation
> (UIInterfaceOrientation)newStatusBarOrientatio
> duration:(NSTimeInterval)duratio
>
  说明:当 StatusBar 框方向将要变化时执行
>
  11 - ( BOOL )application:(UIApplication*)application handleOpenURL:(NSURL*)ur
>
  说明:当通过 url 执行
>
  12 - ( void )application:(UIApplication*)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientatio
>
  说明:当 StatusBar 框方向变化完成后执行
>
  13 - ( void )application:(UIApplication*)application didChangeSetStatusBarFrame:(CGRect)oldStatusBarFram
>
  说明:当 StatusBar 框变化完成后执行
>
 
>
  UIWindow
>
    UIWindow 是一种特殊的 UIView ,通常在一个 app 中只会有一 个 UIWindow ,但可以手动创建多个 UIWindow  
>
    UIWindow 的主要作用 :  
>
  1   供一个区域来显示视
>
  2   将事件分发给视
>
  3   UIViewController 协同工作,方便完成设备方向旋转的支持
>
 
>
  UIWindow
>
  添加 UIView UIWindow 中两种常见方式 :
>
  1   addSubview :直接将 UIView 添加到 UIWindow 中,程序负责维 护 UIView 的生命周期以及刷新,但并不会理会 UIView 对应 的 UIViewController
>
  2   rootViewController :自动将 UIViewController 对应的 UIView   添加到 UIWindow 中,同时负责维护 UIViewController UIView 的 生命周期
>
    常用方
>
  1   makeKeyWindow : 让当前 UIWindow 变成 keyWindo
>
  2   makeKeyAndVisible : 让当前 UIWindow 变成 keyWindow ,并显示出来
>
 
>
  UIViewControlle
>
    UIViewController 属于 MVC 模型中的 C(Controller), 说的更具体 点它是一个视图控制器 , 管理着一个视图 (UIView)
>
    一个 UIViewController 应该只管理一个 view hierarchy ,通常 来说一个完整的 view hierarchy 指的是占满整一个屏幕。而很多 ap p 满屏中会有各个区域分管不同的功能,一些开发者喜欢直接新建一 个 UIViewController 和一套相应的 view 来完成所要的功能,这样 做完全不符合 Apple �� 供的设计规范 UIViewController view

  可以利用 xib 文件来初始化 view;   也可以使用自定义view ,那就必须覆盖 loadView 方法来创建这个 viewUIViewController view lazy loading , 当你访 问其 view 属性时 ,view 会从 xib 文件载入或者通过代码创 建 ( 覆盖 loadView 方法 , 自定义其 view hierarchy) ★  可以用 isViewLoaded 方法判断一个 UIViewController   view 是否已经被加载 UIViewController 生命周期方法的
r>
    view 加载后调用 viewDidLoad , 这里可以进行一些数据的请求或加载 , 用来更新界面 view 将要被加入 view hierarchy 时调用 viewWillAppear , 完成  加入时调用 viewDidAppear   view 将要从 view hierarchy 中移除时调用 viewWillDisappear  , 完成移除时调用 viewDidDisappear     当内存紧张时 ,   调用 didReceiveMemoryWarning , 其默认实现是如 果当前 UIViewController view superview nil, 则将 view 释 放且调用 viewDidUnload ,   viewDidUnload 中你可以进行后继的内 存清理工作 ( 主要是界面元素的释放 , 当再次加载的时候需要重建 )  (这里的 view 是指 UIViewController 内部的 view 属性)   工程名 -Info.plist

  建立一个工程后,会在 Supporting files 文件夹下看到一个 工程名   -Info.plist 的文件,该文件对工程做一些运行期的配置,很重要, 不能删除     在旧版本 Xcode 创建的工程中,这个配置文件的名字就叫 Info.plist

  如果使用文本编辑器打开这个文件,会发现这是一个 XML 格式的文本  文件,一般不用文本编辑器直接编辑这个文件,而是通过 Xcode 编辑

  项目中还有一个 InfoPlist.strings 的文件,跟 Info.plist 文件的 本地化相关   工程名 -Info.plist

  常见属性 ( 红色部分是用文本编辑器打开时看到的 key)

  Localiztion native development region( CFBundleDevelopmentRegion ) - 本地化相关     Bundle display name( CFBundleDisplayName ) - 程序安装后显 示的名称 , 限制在 10 - 12 个字符,如果超出,将被显示缩写名称

  Icon file( CFBundleIconFile ) -app 图标名称 , 一般为 Icon.png

  Bundle version( CFBundleVersion ) - 应用程序的版本号,每次  往 App Store 上发布一个新版本时,需要增加这个版本号

  Main nib file base name( NSMainNibFile ) - nib 文件的名称

  Bundle identifier( CFBundleIdentifier ) - 项目的唯一标识, 部署到真机时用到   工程名 -Prefix.pch

  一般来说,可以将项目中经常用到的一些头文件放在这里来 import , 整个项目都可以访问这个文件的内容,这样既节省了手动添加 import 的时间,也有助于加速编译

  在这里定义的宏,整个项目都可以访问   pch 文件中添加下列预处理指令,然后在项目中使用 Log(...) 来输出 日志信息,就可以在发布应用的时候,一次性将 NSLog 语句移除(在调 试模式下,才有定义 DEBUG )  #ifdef DEB
r> #define  Log(...) NSLog(__VA_ARGS_
r>
  #el
r> #define
  Log(...)   /*
r>
  #end
r>
  r>  

开发iOS6 的注意

  iOS6 新特性: auto layout 属性,此属性只针 iOS6 及以上版本   iOS6 以下版本运行时可能会出现的异常信息: Terminating app due to uncaug
r> excepti
r> ‘NSInvalidUnarchiveOperationException
r> reason: ‘Could not instantiate cla
r> named NSLayoutConstrain
r>
    具体场景: Xcode 4.5   选择 iPhone/iPad 5.0/5.1 Simulator (模拟器)   解决办法:需要关闭 storyboard xib 界面文 件的 Use Autolayout   选项,这是因为 Auto Layout 特性是 iOS 6 新增加的,在之前的   5.0/5.1 Simulator 模拟器中不支持 nib 文件

★  nib 文件是 iOS 中用来 述视图的 xml 格式的 文本文件, 现在拓展名为 xib ,用 Interface Builder 打开可以 生成图形界面式的

  某书中著名的一句话 :Interface Builder 把窗口、 菜单栏以及窗口上的各种控件对象都 冻结 在一个 NIB   文件里 ; 程序运行时,这些对象将会 苏醒

  加载 Nib 文件时,会将文件中的 述转化为应用程序可 以操作的真正对象,所有在 Interface Builder 中建 立的关联(如 File’s Owner 和其他对象之间的关联) 都能够在运行时重新建立起来   nib 文件

★  nib 文件是指应用程序一启动就装载的 nib 文件,它 的 File’s Owner 一定要是个 UIApplication( 或子类 )   ,并且新建一个 delegate 对象、建立 UIApplication   delegate 对象的关联 nib 文件的设置方法 r>   Info.plist 通过 NSMainNibFile 这个 key 可以设置主 nib 文件 nib 文件的设置方法
r>
  Summary Main Interface
r>
  UIView ★  UIView iOS 中界面元素的基础,所有的界面元素都继承它,可以说 在 iPhone 中你能看到的、摸到的,都是 UIView ★  UIView 的基本功
r>
  1   绘图和动画 ( CALayer CAAnimation 实现
r>
  2   事件处理 ( 继承了 UIRsponde
r>
    一个 UIView 可以包含和管理若干个子视图,决定着子视图的位置和大小 UIView 常用属性

★  frame 父视图 坐标系中的位置 (CGPoint origin) 和大小 (CGSize size)

★  bounds 本视图 坐标系中的位置 (CGPoint origin x y 永远为 0) 和大 小 (CGSize size)

★  center
视图的中点在 父视图 坐标系中的
r>
    UIView*superview
r>
    NSArray*subviews 所有的子
r>
    UIWindow *window   当前视图所在的
r>
    BOOLuserInteractionEnabled YES 代表接收触摸事件     在父视图坐标系中,父视图的左上角为坐标原点 (0,
r>
    在本视图坐标系中,本视图的左上角为坐标原点 (0, 0) UIKit 坐标系
r>  (0,0) View1 View2 的父视图   View2. frame   = {x=70,y=50,width=60,height=40} View2. bounds   = {x=0,y=0,width=60,height=40} View2. center   = (x=100, y=70) •  UIKit 框架中的坐标系都如左图所示,视图的 左上角为原点 (0,0) , x 轴向右正向延伸, y   轴向下正向延伸 •  View3 frame x , y
r>
  1   如果绿色视图是 View3 的父视图,那么 View3 x , y 为红色箭头的
r> 2
  如果 View1 View3 的父视图,那么 View3x , y 为蓝色箭头的宽度 UIView 常用方法和属性 ★  -(void)removeFromSuperview 从父视图中移除 ( 当前视图的计数器会 -1) ★  -(void)addSubview:(UIView*)view 添加一个子视图 ( 新添加的子视图在父视图的最上面,子视图的 计数器会 +1) ★  -(UIView*)viewWithTag:(NSInteger)tag   根据 tag 找到对应的子视图 iOS 关闭键盘的方法 r>     调用 UIView endEditing 方法,例如 [self.view endEditing :YES];   成功关闭键盘的条件 :self.view 或者其子视图是第一响应者 iOS 关闭键盘的方法 2

递归找到第一响应者,让它把键盘给退
r>  [[self findFirstResponder:self.view] resignFirstResponder ]; - (UIView*)findFirstResponder:(UIView*)view   { for ( UIView *childView in view.subviews
r>  {   //   遍历子视
r>
  if ( [childView respondsToSelector:@selector(isFirstResponder)] && [childView isFirstResponder] )  { return childView;  //   如果 childView 是第一响应
r>
  }       UIView *result = [self findFirstResponder:childView
r> if (result) return result
r>  } return nil; 

1.先执行main函数,main内部会调用UIApplicationMain函数

  2.UIApplicationMain函数里面做了什么事情:

  1> 创建UIApplication对象

  2> 创建UIApplication的delegate对象—–PYAppDelegate

  3> 开启一个消息循环

  每监听到对应的系统事件时,就会通知MJAppDelegate

  4> 为应用程序创建一个UIWindow对象(继承自UIView),设置为PYAppDelegate的window属性

  5> 加载Info.plist文件,读取最主要storyboard文件的名称

  6> 加载最主要的storyboard文件,创建白色箭头所指的控制器对象

  7> 并且设置第6步创建的控制器为UIWindow的rootViewController属性(根控制器)

  8> 展示UIWindow,展示之前会将添加rootViewController的view到UIWindow上面(在这一步才会创建控制器的view)

  [window addSubview: window.rootViewControler.view];

  进入main函数,在main.m的main函数中执行了UIApplicationMain这个方法,这是ios程序的入口点!

  int UIApplicationMain(int argc, char argv[], NSString principalClassName, NSString *delegateClassName)

  argc、argv:ISO C标准main函数的参数,直接传递给UIApplicationMain进行相关处理即可

  principalClassName:指定应用程序类,该类必须是UIApplication(或子类)。如果为nil,则用UIApplication类作为默认值

  delegateClassName:指定应用程序类的代理类,该类必须遵守UIApplicationDelegate协议

  此函数会根据principalClassName创建UIApplication对象,根据delegateClassName创建一个delegate对象,并将该delegate对象赋值给UIApplication对象中的delegate属性

  lUIApplication对象会依次给delegate对象发送不同的消息,接着会建立应用程序的main runloop(事件循环),进行事 件的处理(首先会调用delegate对象的 application:didFinishLaunchingWithOptions:)

  程序正常退出时这个函数才返回。如果进程要被系统强制杀死,一般这个函数还没来得及返回进程就终止了

  下面我们有图有真相吧!!!

Xcode4.2以后iOS应用的启动顺序变化


以上是Xcode4.2中不采用storyboard用的默流程于采用了storyboard用,UIApplicationMain()将会外加载应用的主要storyboard文件,从而建窗口和初始视图

 

main()变化

 

main()仍然是用的起点,其代如下:

int main(int argc, char *argv[])

{

    @autoreleasepool {

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

    }

}

main()采用了新的@autoreleasepool{}函数,以便支持LLVM3.0。但化不会影响 的启动顺序。需要注意的是UIApplicationMain第四个参数的化,之前是nil,里已更改了。如果看相关文档(https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKitFunctionReference/Reference/reference.html#//apple_ref/doc/uid/TP40006894-CH3-SW7,就会知道第四个参数代表“用的delegate初始化的名”,如果从用的主nib文件加代理象,参数指定nil

然,我用代理将不会由之前的MainWindow.xib,而是直接由UIApplicationMain()函数建。实际上,目中已不再有MainWindow.xib文件。

 

 

Xcode4.2及以后版本去掉main nib文件的原因很可能是storyboarding入。storyboards基于视图控制器,而非视图或窗口。

 

最后,non-storyboarding用,由于取消了MainWindow.xib,因此在AppDelegate.m中的didFinishLauchingWithOptions:方法中就需要增加一些初始化的代,如下:

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

{

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Override point for customization after application launch.

  if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {

      self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil];

  } else {

      self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil];

  }

  self.window.rootViewController = self.viewController;

    [self.window makeKeyAndVisible];

    return YES;

}


共有 人打赏支持
粉丝 11
博文 120
码字总数 121640
×
Lambda8421
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: