========================程序的啟動原理====================
一、 項目中的常見文件
** XxxxTest文件夾用來做單元測試。
** Products目錄。
* 在Mac程序下,生產的可執行文件app會存放到該目錄下。
* iOS程序產生的是ipa文件。這個目錄對于iOS程序來說意義不大。
1. Info.plist(全局配置文件, 非常重要, 不能刪除 ):
* 在xcode5 中是"項目名稱-Info.plist"表示項目的全局配置文件,非常重要。在舊版本xcode中(和xcode6.1),該文件名稱就叫Info.plist。
** 注意:自己創建的plist文件中不要包含Info關鍵字。
* Info.plist中的一些配置項:
1> Bundle display name(在xcode6.1中叫做"Bundle name"), 表示軟件安裝到手機上后,顯示的名稱。
** 當修改了該名稱后,為了保證有效, 點擊Product -> Clean, 同時將軟件從模擬器中卸載掉,然后再重新運行。
2> Bundle identifier, app的唯一標識。
3> Bundle version, 每次發布軟件的版本號。每次向AppStore上傳的同一個軟件, 新的版本號必須大于舊的, 否則無法上傳。
4> Main storyboard file base name, 對應的就是選中"項目" -> "General" -> "Deployment Info" -> "Main Interface" 中的設置。
5> Supported interface orientations, 標識設備所支持的方向。對應的選中"項目" -> "General" -> "Deployment Info" -> "Device Orientation"。iPhone只支持三種方向, 不支持上下旋轉(iPad支持)。Portrait(豎屏)、Landscape Left(橫屏向左)、Landscape Right(橫屏向右)。
6> Info.plist就是一個xml文件, 用記事本打開看一下。(可擴展性標記語言)
xml: Exrenible Markup language (可擴展性標記語言)
HTML: HyperText Markup language (超文本標記語言)
2. pch文件:(Prefix Header File)(頭文件)
* 遇到的問題:
1> 整個項目中很多地方都在使用某個類的頭文件。
2> 整個項目中很多地方都在使用同一個"宏"
3> 在項目中很多地方用到了NSLog()函數, 想一下子全都清除掉。
* 解決上面的問題, 可以通過使用PCH文件(Prefix Header File)。
* pch文件就是一個頭文件(類似于*.h文件)。
** 注意: PCH文件的特點, 項目中的所有其他代碼文件無需顯示導入該PCH文件, 默認就都可以訪問(其他文件無需手動#import該 pch文件就能使用)。
* 主要作用:
1> 可以放一些公用的宏定義。
2> 把公共的Model類的#import導入寫到pch文件中。
3> 自定義NSLog()。例如: #define CZLog(...) NSLog(__VA_ARGS__)
** 遇到的問題: 在項目中很多地方用到了NSLog()函數, 想一下子全都清除掉。
* 在xcode6以后, 默認沒有創建pch文件。
** 需要自己新建一個
** 創建方式: 選擇"Supporting Files" -> 右鍵 -> "New File" -> "Other" -> "PCH File" -> "PrefixHeader.pch"。
* 在該文件中定義如下宏:
** #define ABC 10
* 選中項目 -> Build Setting -> All -> 搜索"prefix head" -> 修改Prefix Header的內容為:
** "$(SRCROOT)/$(PRODUCT_NAME)/PrefixHeader.pch" (如果有問題,換下面的方式,可能會與中文有關)
** 或者
** "$(SRCROOT)/對應的文件夾名/PrefixHeader.pch"
** 參考連接: http://www.cnblogs.com/YouXianMing/p/3989155.html
3. UIApplication對象介紹:
1> 一個UIApplication代表是一個應用程序,而且是單例的。
** 用來封裝整個應用程序的一個對象, 比如當應用程序執行到某個時期要做什么, 生命周期等。
2> 獲取UIApplication對象: [UIApplication sharedApplication];(單例的)
3> 當一個iOS程序啟動后,首先創建的第一個對象就是UIApplication對象。
4> 利用UIApplication可以做一些應用級別的操作。
* 應用級別操作:
1> QQ有消息的時候右上角的消息條數。
// 獲取UIApplication對象。
UIApplication *app = [UIApplication sharedApplication];
// 設置右上角, 有10條消息
app.applicationIconBadgeNumber = 10;
// 取消顯示消息
app.applicationIconBadgeNumber = 0;
2> 聯網操作時,狀態欄上的等待圖標指示器。waiting圖標。
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
3> 利用UIApplication打開某個資源:
** 系統會自動根據協議識別使用某個app打開。
UIApplication *app = [UIApplication sharedApplication];
** 打開一個網頁:
[app openURL:[NSURL URLWithString:@"http://ios.icast.cn"]];
** 打電話
[app openURL:[NSURL URLWithString:@"tel://10086"]];
** 發短信
[app openURL:[NSURL URLWithString:@"sms://10086"]];
** 發郵件
[app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];
** 使用openURL方法也可以打開其他應用,在不同應用之間互相調用對方。
** 美圖秀秀, 點擊分享到"新浪微博", 打開"新浪微博"選擇賬號, 跳轉回"美圖秀秀", 開始分享
** 喜馬拉雅, 使用微博、QQ 賬號 登錄。都需要應用程序間跳轉。
5. UIApplicationDelegate介紹。
** 新建完項目以后的那個AppDelegate文件, 就是UIApplication的代理對象。
* 并且該代理對象已經被設置好了, 無需我們手動設置了。
* 在main函數中進行的設置
** App容易受到干擾。正在玩游戲,一個電話打過來了。
* 應用程序的生命周期事件(如程序啟動和關閉)
* 系統事件(如來電)
* 內存警告
* … …
** 處理這些干擾事件,就要用到AppDelegate代理對象了。
** 總結: AppDelegate的主要作用就是處理(監聽)應用程序本身的各種事件:
* 應用程序啟動完畢
* 應用程序進入后臺
* 應用程序進入前臺
* 內存警告
* 等等, 都是應用程序自身的一些事件
** 要想成為UIApplication的代理對象, 必須遵守:UIApplicationDelegate協議。
** 代理中的若干方法介紹:
//app啟動完成后執行
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions
{
NSLog(@"didFinishLaunchingWithOptions");
return YES;
}
//app即將推出活動狀態的時候調用(失去焦點,不可交互) //此方法在來電或來短信的時候被調用
//
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
NSLog(@"applicationWillResignActive");
}
//app進入后臺的時候調用
//在此方法中保存應用程序的數據和狀態
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(@"applicationDidEnterBackground");
}
//app即將進入前臺的時候調用
//在此方法中還原應用程序的數據和狀態
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
NSLog(@"applicationWillEnterForeground");
}
//app獲得焦點的時候調用(能夠和用戶交互)
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was
//inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(@"applicationDidBecomeActive");
}
//app將被銷毀
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also
//applicationDidEnterBackground:.
NSLog(@"applicationWillTerminate");
}
//app收到系統的內存警告,清除不必要的內存
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
NSLog(@"applicationDidReceiveMemoryWarning");
}
6. UIApplicationMain函數介紹。
7. iOS程序啟動過程。參考PPT上的圖片。
1> 打開程序。
2> 調用main函數。
3> 在main函數中調用: UIApplicationMain()函數。
* 在UIApplicationMain()函數中:
1. 創建UIApplication對象
2. 創建AppDelegate代理對象
3. 將AppDelegate代理對象設置給UIApplication對象。
4. 在UIApplicationMain()函數開啟一個"死循環(事件循環)", 所以程序不會退出, 我們可以任意使用。在這個"死循環(事件循環)"中程序不斷監聽用戶的各種事件, 依次處理(依靠"事件隊列"實現)。
5. 程序啟動完畢: 觸發application:didFinishLaunchingWithOptions事件。
在此方法中創建了application對象和它的代理對象,并設置了application的代理對象,開啟消息循環。在消息循環中不斷監測 消息隊列 中是否有新的事件(隊列是先進先出)(事件隊列的目的是為了提高性能) (消息循環可以簡單理解為一個死循環,當系統有事件被觸發,事件所執行的方法會放入消息隊列中,消息循環的作用就是不停的去消息 隊列中看有沒有新的消息,有就執行,用戶感覺不到等待的時間)
//先檢查info.plist文件中是否有principalClassName鍵,如果沒有的話UIApplicationMain中的principalClassName參 數,會默認被指定為UIApplication ,代理類的會被初始化
4> 程序退出。
8. UIWindow對象。
** UIWindow是一種特殊的UIView, UIWindow也是繼承自UIView。
** 通常一個app只會有一個UIWindow對象。
** iOS程序啟動完畢后,創建的第一個視圖控件就是UIWindow,接著創建控制器的view,最后將控制器的view添加到UIWindow上,于是控制器的view就顯示在屏幕上了
** 一個iOS程序之所以能顯示到屏幕上,完全是因為它有UIWindow
** 在文檔中找: Cocoa Touch Layer -> UIKit -> Guides -> View Controller Programming Guide for iOS -> View Controller Basics -> 關于UIWindow與控制器View的關系圖片。
** 創建過程UIWindow -> UIViewController -> UIView -> 把UIView加到UIWindow對象中。
** 通過storyboard方式啟動項目的方式:
1> 新建一個Single View Application, 觀察AppDelegate中的代碼, 雖然有application: didFinishLaunchingWithOptions:方法, 但是并沒有在該方法中創建任何UIWindow。
2> 帶Main.storyboard的啟動方式:
1. 啟動App
3. 創建UIWindow對象。在該方法中系統已經生成了創建UIWindow的方法, 不需要我們手動編寫。
4. 根據Info.plist文件配置(Main Interface),找到需要加載的storyboard文件(Main.storyboard)
5. 找到Main.storyboard中的Is Initial View Controller 對應的控制器類, 創建該控制器對象。
6. 根據storyboard中的配置, 創建控制器對應的view。
7. 設置UIWindow的根控制器(rootViewController)為剛才創建的控制器。
調用AppDelegate的 application: didFinishLaunchingWithOptions:方法
8. 顯示UIWindow([self.window makeKeyAndVisible])。
9. 注意, 當刪除了Main Interface 中的配置后, 在運行程序會看到“黑屏”, 因為沒有創建"控制器", 也沒有設置UIWindow的rootViewController。
================== 演示在一個空項目中創建界面內容 ========================
** xcode6以后沒有創建空項目的模板, 所以可以新建一個Single View Application, 然后刪除Main.storyboard 、 默認的ViewController、 同時設置Main Interface為空。然后運行, "黑屏"。因為沒有UIWindow, 就沒有界面。
** 開始一步一步創建界面:
1> 在application: didFinishLaunchingWithOptions:方法中創建UIWindow對象。
2> 新建一個自己的控制器類, 并在該控制的viewDidLoad事件中, 設置控制器的view的背景色為紅色。
3> 將控制器的view添加到UIWindow中。
**通過設置UIWindow的根控制器的方式
** 思考: 如果不使用控制器, 直接把控件添加到UIWindow中行不行?
** 總結程序啟動完整過程。(有storyboard和沒有storyboard的分別總結)
一、 有storyboard文件
1. 調用main函數。
2. 調用UIApplicationMain函數。
3. 創建UIApplication對象 、 AppDelegate對象
4. 設置UIApplicatio對象的代理是AppDelegate對象。
5. AppDelegate對象開始監聽"系統事件(應用程序的事件)",進入"事件循環"
6. 程序啟動完畢后調用 application: didFinishLaunchingWithOptions:方法。
7. 在application: didFinishLaunchingWithOptions:方法中自動創建:
* UIWindow對象。
* 根據Info.plist文件配置(Main Interface),找到需要加載的storyboard文件(Main.storyboard)
* 找到Main.storyboard中的Is Initial View Controller 對應的控制器類, 創建該控制器對象。
* 根據storyboard中的配置, 創建控制器對應的view。
* 設置UIWindow的根控制器(rootViewController)為剛才創建的控制器。
* 顯示UIWindow([self.window makeKeyAndVisible])。
二、 沒有storyboard文件
1. 調用main函數。
2. 調用UIApplicationMain函數。
3. 創建UIApplication對象 、 AppDelegate對象
4. 設置UIApplicatio對象的代理是AppDelegate對象。
5. AppDelegate對象開始監聽"系統事件(應用程序的事件)",進入"事件循環"。
6. 程序啟動完畢后調用 application: didFinishLaunchingWithOptions:方法。
7. 在application: didFinishLaunchingWithOptions:方法中手動創建:
* UIWindow
* 控制器
* 設置UIWindow的根控制器是剛才創建的控制器
* 顯示UIWindow
*** 總結:UIApplication(應用程序對象) 、 AppDelegate(應用程序代理對象) 、 UIWindow(窗口對象) 、 ViewController(控制器對象)四個對象的關系。
*** 注意:要讓設計器中看到的與最終手機屏幕上看到的一致,則把設計器中的ViewController的大小調整到與手機模擬器大小一致了。否則可能會出現在設計器中看到的與模擬器中運行的結果不一致問題。
*** 注意:在xcode幫助文檔中查看iOS7 和 iOS6中控件的不同展示方式:iOS8.1 -> User Experience -> Guides -> iOS 7 UI Transition Guide, 然后打開右側的子菜單。