適用于iOS的應(yīng)用程序編程指南(八)

應(yīng)用間通信

應(yīng)用程式只能間接與設(shè)備上的其他應(yīng)用進(jìn)行通信。您可以使用AirDrop與其他應(yīng)用程序共享文件和數(shù)據(jù)。您還可以定義自定義網(wǎng)址方案,以便應(yīng)用程序可以使用URL向應(yīng)用發(fā)送信息。

注意:您也可以使用UIDocumentInteractionController對(duì)象或文檔選擇器在應(yīng)用程序之間發(fā)送文件。有關(guān)添加對(duì)文檔交互控制器的支持的信息,請(qǐng)參閱iOS的文檔交互編程主題。Document Interaction Programming Topics for iOS,有關(guān)使用文檔選擇器打開文件的信息,請(qǐng)參閱文檔選擇器編程指南。Document Picker Programming Guide

支持AirDrop

AirDrop可讓您與附近的設(shè)備共享照片,文檔,URL和其他類型的數(shù)據(jù)。 AirDrop利用對(duì)等網(wǎng)絡(luò)查找附近的設(shè)備并連接到它們。

將文件和數(shù)據(jù)發(fā)送到另一個(gè)應(yīng)用程序

要使用AirDrop發(fā)送文件和數(shù)據(jù),請(qǐng)使用UIActivityViewController對(duì)象從您的用戶界面中顯示活動(dòng)表。創(chuàng)建此視圖控制器時(shí),指定要共享的數(shù)據(jù)對(duì)象。視圖控制器僅顯示支持指定數(shù)據(jù)的活動(dòng)。對(duì)于AirDrop,您可以指定圖像,字符串,URL和其他幾種類型的數(shù)據(jù)。您還可以傳遞采用UIActivityItemSource協(xié)議的自定義對(duì)象。

要顯示活動(dòng)視圖控制器,可以使用類似于清單6-1所示的代碼?;顒?dòng)視圖控制器自動(dòng)使用指定對(duì)象的類型來確定在活動(dòng)表中顯示的活動(dòng)。您不必明確指定AirDrop活動(dòng)。但是,您可以使用視圖控制器的excludedActivityTypes屬性來防止工作表顯示特定類型。在iPad上顯示活動(dòng)視圖控制器時(shí),必須使用彈出窗口。

清單6-1在iPhone上顯示活動(dòng)表

- (void)displayActivityControllerWithDataObject:(id)obj{

UIActivityViewController * vc =[[UIActivityViewController alloc]

initWithActivityItems:@ [obj] applicationActivities:nil];

[self presentViewController:vcanimated:YES completion:nil];

}

有關(guān)使用活動(dòng)視圖控制器的更多信息,請(qǐng)參閱UIActivityViewController類參考UIActivityViewController Class Reference。有關(guān)活動(dòng)及其支持的數(shù)據(jù)類型的完整列表,請(qǐng)參閱UIActivity類參考。UIActivity Class Reference

接收文件和數(shù)據(jù)發(fā)送到您的應(yīng)用程序

要使用AirDrop接收發(fā)送到您的應(yīng)用程序的文件,請(qǐng)執(zhí)行以下操作:

在Xcode中,聲明支持您的應(yīng)用程序能夠打開的文檔類型。

在你的應(yīng)用程序委托中,實(shí)現(xiàn)應(yīng)用程序:openURL:sourceApplication:annotation:method。使用該方法接收其他應(yīng)用程序發(fā)送的數(shù)據(jù)。

您的Xcode項(xiàng)目的“信息”選項(xiàng)卡包含一個(gè)“文檔類型”部分,用于指定應(yīng)用程序支持的文檔類型。至少必須指定文檔類型的名稱和表示數(shù)據(jù)類型的一個(gè)或多個(gè)UTI。例如,要聲明對(duì)PNG文件的支持,您將包含public.png作為UTI字符串。iOS使用指定的UTI來確定您的應(yīng)用是否有資格打開給定的文檔。

將符合條件的文檔轉(zhuǎn)移到應(yīng)用程序的容器后,iOS會(huì)啟動(dòng)應(yīng)用程序(如果需要),并調(diào)用應(yīng)用程序:openURL:sourceApplication:annotation:其應(yīng)用程序委托的方法。如果您的應(yīng)用程序處于前臺(tái),則應(yīng)使用此方法打開該文件并將其顯示給用戶。如果您的應(yīng)用程序在后臺(tái),您可能只會(huì)確定該文件在那里,以便以后打開它。因?yàn)橥ㄟ^AirDrop傳輸?shù)奈募鞘褂脭?shù)據(jù)保護(hù)進(jìn)行加密的,除非設(shè)備當(dāng)前被解鎖,否則無法打開文件。

您的應(yīng)用程序有權(quán)讀取和刪除它收到的文件,但沒有權(quán)限寫入該文件。如果您打算修改文件,則必須將其移出當(dāng)前位置才能執(zhí)行此操作。建議您刪除文件的原始版本。

有關(guān)在應(yīng)用程序中支持文檔類型的更多信息,請(qǐng)參閱基于文檔的iOS應(yīng)用程序編程指南。Document-Based App Programming Guide for iOS

使用URL方案與Apps通信

URL方案允許您通過您定義的協(xié)議與其他應(yīng)用程序進(jìn)行通信。要與實(shí)現(xiàn)這種方案的應(yīng)用程序通信,您必須創(chuàng)建一個(gè)格式正確的URL,并要求系統(tǒng)將其打開。要實(shí)現(xiàn)對(duì)自定義方案的支持,您必須聲明對(duì)該方案的支持,并處理使用該方案的傳入U(xiǎn)RL。

注意:Apple為http,mailto,tel和sms URL方案提供了內(nèi)置的支持。它還支持針對(duì)地圖,YouTube和iPod應(yīng)用程序的基于http的URL。這些方案的處理程序是固定的,不能更改。如果您的網(wǎng)址類型包含與Apple定義的方案相同的方案,則Apple提供的應(yīng)用程序?qū)?huì)啟動(dòng),而不是您的應(yīng)用程序。有關(guān)Apple支持的方案的信息,請(qǐng)參閱AppleURL Scheme Reference。Apple URL Scheme Reference

發(fā)送一個(gè)URL到另一個(gè)應(yīng)用程序

當(dāng)您要將數(shù)據(jù)發(fā)送到實(shí)現(xiàn)自定義URL方案的應(yīng)用程序時(shí),請(qǐng)創(chuàng)建一個(gè)適當(dāng)格式的URL并調(diào)用該應(yīng)用對(duì)象的openURL:方法。openURL:方法使用注冊(cè)的方案啟動(dòng)該應(yīng)用,并將您的URL傳遞給它。在這一點(diǎn)上,控制傳遞給新的應(yīng)用程序。

以下代碼片段說明了一個(gè)應(yīng)用程序如何請(qǐng)求另一個(gè)應(yīng)用程序的服務(wù)(在本示例中為“todolist”是應(yīng)用程序注冊(cè)的假設(shè)自定義方案):

NSURL * myURL = [NSURL URLWithString:@“todolist://www.acme.com?Quarterly%20Report#200806231300”];

[UIApplication sharedApplication] openURL:myURL];

如果您的應(yīng)用程序定義了自定義URL方案,那么它應(yīng)該實(shí)現(xiàn)該方案的處理程序,如實(shí)現(xiàn)自定義URL方案中所述。有關(guān)系統(tǒng)支持的URL方案的詳細(xì)信息,包括有關(guān)如何格式化URL的信息,請(qǐng)參閱AppleURL Scheme Reference。Apple URL Scheme Reference.

實(shí)現(xiàn)自定義URL方案

如果您的應(yīng)用程序可以接收特殊格式的URL,則應(yīng)該使用系統(tǒng)注冊(cè)相應(yīng)的URL方案。應(yīng)用程序經(jīng)常使用自定義URL方案向其他應(yīng)用程序提供服務(wù)。例如,“地圖”應(yīng)用程序支持顯示特定地圖位置的URL。

注冊(cè)自定義URL方案

要為您的應(yīng)用注冊(cè)URL類型,請(qǐng)?jiān)趹?yīng)用的Info.plist文件中包含CFBundleURLTypes密鑰。CFBundleURLTypes鍵包含一系列字典,每個(gè)字典都定義了應(yīng)用程序支持的URL方案。表6-1說明了要包含在每個(gè)字典中的鍵和值。

表6-1 CFBundleURLTypes屬性的鍵和值

CFBundleURLName

包含URL方案的抽象名稱的字符串。為了確保唯一性,建議您指定反向DNS風(fēng)格的標(biāo)識(shí)符,例如com.acme.myscheme。

您指定的字符串也用作應(yīng)用程序的InfoPlist.strings文件中的鍵。密鑰的值是可讀取的方案名稱。

CFBundleURLSchemes

包含URL方案名稱的字符串?dāng)?shù)組,例如http,mailto,tel和sms。

注意:如果有多個(gè)第三方應(yīng)用程序注冊(cè)以處理相同的URL方案,則目前沒有確定哪個(gè)應(yīng)用程序?qū)⒈毁x予該方案的過程。

處理URL請(qǐng)求

具有自己的自定義URL方案的應(yīng)用程序必須能夠處理傳遞給它的URL。所有網(wǎng)址都會(huì)在啟動(dòng)時(shí)或應(yīng)用程序運(yùn)行時(shí)或后臺(tái)傳遞到您的應(yīng)用程序代理。要處理傳入的URL,您的代理應(yīng)該實(shí)現(xiàn)以下方法:

使用應(yīng)用程序:willFinishLaunchingWithOptions:和應(yīng)用程序:didFinishLaunchingWithOptions:方法來檢索有關(guān)URL的信息,并決定是否要打開它。如果任一方法返回NO,您的應(yīng)用程序的URL處理代碼不被調(diào)用。

使用應(yīng)用程序:openURL:sourceApplication:annotation:打開文件的方法。

如果您的應(yīng)用程序在URL請(qǐng)求到達(dá)時(shí)未運(yùn)行,則會(huì)將其啟動(dòng)并移動(dòng)到前臺(tái),以便可以打開該URL。您的應(yīng)用程序的實(shí)現(xiàn):willFinishLaunchingWithOptions:或應(yīng)用程序:didFinishLaunchingWithOptions:方法應(yīng)從其選項(xiàng)字典中檢索URL,并確定應(yīng)用程序是否可以打開它。如果可以,返回YES并讓您的應(yīng)用程序:openURL:sourceApplication:annotation:((或application:handleOpenURL :)方法處理URL的實(shí)際打開。 (如果您實(shí)現(xiàn)這兩種方法,兩者都必須返回YES才能打開URL)。圖6-1顯示了要求打開URL的應(yīng)用程序的修改啟動(dòng)順序。


圖6-1啟動(dòng)應(yīng)用程序以打開URL

如果您的應(yīng)用正在運(yùn)行,但是在后臺(tái)發(fā)生或在URL請(qǐng)求到達(dá)時(shí)被暫停,則將其移動(dòng)到前臺(tái)以打開該URL。 此后不久,系統(tǒng)調(diào)用委托人的應(yīng)用程序:openURL:sourceApplication:annotation:檢查URL并將其打開。圖6-2顯示了將應(yīng)用程序移動(dòng)到前臺(tái)以打開URL的修改過程。


圖6-2 喚醒一個(gè)后臺(tái)應(yīng)用來打開一個(gè)URL

注意:支持自定義URL方案的應(yīng)用程序可以指定在啟動(dòng)應(yīng)用程序以處理URL時(shí)顯示不同的啟動(dòng)映像。有關(guān)如何指定這些啟動(dòng)映像的詳細(xì)信息,請(qǐng)參閱打開URL時(shí)顯示自定義啟動(dòng)映像。Displaying a Custom Launch Image When a URL is Opened.

所有網(wǎng)址都傳遞到NSURL對(duì)象中的應(yīng)用程序。您可以定義URL的格式,但NSURL類符合RFC1808規(guī)范,因此支持大多數(shù)URL格式約定。具體來說,該類包括返回由RFC 1808定義的URL的各個(gè)部分的方法,包括用戶,密碼,查詢,片段和參數(shù)字符串。您的自定義方案的“協(xié)議”可以使用這些URL部分來傳送各種信息。

在應(yīng)用程序的實(shí)現(xiàn)中:openURL:sourceApplication:annotation:如清單6-2所示,傳入的URL對(duì)象在其查詢和片段部分中傳達(dá)特定于應(yīng)用程序的信息。代理提取此信息- 在這種情況下,即將完成任務(wù)的名稱以及該任務(wù)到期的日期 - 并與之建立應(yīng)用程序的模型對(duì)象。此示例假定用戶正在使用公歷。如果您的應(yīng)用程序支持非公歷日歷,則需要相應(yīng)地設(shè)計(jì)您的URL方案,并準(zhǔn)備好處理代碼中的其他日歷類型。

- (BOOL)application:(UIApplication *)applicationopenURL:(NSURL *)url

sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{

if ([[url scheme]isEqualToString:@"todolist"]) {

ToDoItem *item =[[ToDoItem alloc] init];

NSString *taskName =[url query];

if (!taskName || ![selfisValidTaskString:taskName]) { // must have a task name

returnNO;

}

taskName = [taskNamestringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

item.toDoTask =taskName;

NSString *dateString =[url fragment];

if (!dateString ||[dateString isEqualToString:@"today"]) {

item.dateDue = [NSDate date];

} else {

if(![self isValidDateString:dateString]) {

return NO;

}

//format: yyyymmddhhmm (24-hour clock)

NSString*curStr = [dateString substringWithRange:NSMakeRange(0, 4)];

NSIntegeryeardigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(4, 2)];

NSIntegermonthdigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(6, 2)];

NSIntegerdaydigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(8, 2)];

NSIntegerhourdigit = [curStr integerValue];

curStr =[dateString substringWithRange:NSMakeRange(10, 2)];

NSIntegerminutedigit = [curStr integerValue];

NSDateComponents *dateComps = [[NSDateComponents alloc] init];

[dateComps setYear:yeardigit];

[dateComps setMonth:monthdigit];

[dateComps setDay:daydigit];

[dateComps setHour:hourdigit];

[dateComps setMinute:minutedigit];

NSCalendar *calendar = [s[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];

NSDate*itemDate = [calendar dateFromComponents:dateComps];

if(!itemDate) {

return NO;

}

item.dateDue = itemDate;

}

[(NSMutableArray*)self.list addObject:item];

return YES;

}

return NO;

}

一定要驗(yàn)證從傳遞到您的應(yīng)用程序的URL獲得的輸入;請(qǐng)參閱“安全編碼指南”Validating Input and Interprocess Communication,Secure Coding Guide中的驗(yàn)證輸入和進(jìn)程間通信,以了解如何避免與URL處理相關(guān)的問題。要了解Apple定義的URL方案,請(qǐng)參閱AppleURL Scheme Reference。Apple URL Scheme Reference

打開URL時(shí)顯示自定義啟動(dòng)映像

支持自定義URL方案的應(yīng)用程序可以為每個(gè)方案提供自定義的啟動(dòng)映像。當(dāng)系統(tǒng)啟動(dòng)您的應(yīng)用程序來處理URL并且沒有相關(guān)快照可用時(shí),它會(huì)顯示您指定的啟動(dòng)映像。要指定啟動(dòng)映像,請(qǐng)?zhí)峁┟Q使用以下命名約定的PNG映像:

<basename>-<url_scheme><other_modifiers>.png

在這個(gè)命名約定中,basename表示由應(yīng)用程序的Info.plist文件中的UILaunchImageFile鍵指定的基本映像名稱。如果不指定自定義基本名稱,請(qǐng)使用字符串Default。名稱的部分是您的URL方案名稱。要為myappURL方案指定一個(gè)通用啟動(dòng)映像,您應(yīng)該在應(yīng)用程序的捆綁包中添加名為Default-myapp@2x.png的映像文件。 (@ 2x修飾符表示圖像用于Retina顯示器,如果您的應(yīng)用程序還支持標(biāo)準(zhǔn)分辨率顯示,則還將提供Default-myapp.png圖像。)

有關(guān)可以在啟動(dòng)映像名稱中包含的其他修飾符的信息,請(qǐng)參閱“信息屬性列表密鑰引用”Information Property List Key Reference.中的UILaunchImageFile名稱鍵的說明。

性能提示

在開發(fā)應(yīng)用程序的每個(gè)步驟中,考慮您的設(shè)計(jì)選擇對(duì)應(yīng)用程序整體性能的影響。電源使用和內(nèi)存消耗是iOS應(yīng)用程序的重要考慮因素,還有許多其他注意事項(xiàng)。以下部分將介紹在整個(gè)開發(fā)過程中應(yīng)考慮的因素。

降低應(yīng)用程序的功耗

移動(dòng)設(shè)備上的功耗總是一個(gè)問題。 iOS中的電源管理系統(tǒng)通過關(guān)閉當(dāng)前未使用的任何硬件功能來節(jié)省電力。您可以通過優(yōu)化使用以下功能來幫助延長(zhǎng)電池壽命:

CPU

Wi-Fi,藍(lán)牙和基帶(EDGE,3G)無線電

核心位置框架

加速度計(jì)

磁盤

您的優(yōu)化目標(biāo)應(yīng)該是以盡可能最有效的方式進(jìn)行最多的工作。您應(yīng)該始終使用Instruments優(yōu)化您的應(yīng)用程序的算法。但即使最優(yōu)化的算法仍然可能會(huì)對(duì)設(shè)備的電池壽命產(chǎn)生負(fù)面影響。在編寫代碼時(shí),您應(yīng)該考慮以下準(zhǔn)則:

避免進(jìn)行需要輪詢的工作。輪詢阻止CPU進(jìn)入睡眠狀態(tài)。而不是輪詢,使用NSRunLoop或NSTimer類來根據(jù)需要安排工作。

將共享UIApplication對(duì)象的idleTimerDisabled屬性設(shè)置為NO,盡可能。閑置定時(shí)器在指定的不活動(dòng)時(shí)間段后關(guān)閉設(shè)備的屏幕。如果您的應(yīng)用程序不需要屏幕保持打開,請(qǐng)讓系統(tǒng)將其關(guān)閉。如果您的應(yīng)用程序由于屏幕關(guān)閉而產(chǎn)生副作用,您應(yīng)該修改代碼以消除副作用,而不必不必要地禁用空閑計(jì)時(shí)器。

合并工作盡可能最大限度地延長(zhǎng)空閑時(shí)間。一般來說,一次執(zhí)行一組計(jì)算的能力通常要比在較長(zhǎng)時(shí)間內(nèi)以小塊執(zhí)行的方式更少。周期性地做一點(diǎn)點(diǎn)工作需要更頻繁地喚醒CPU,并使其進(jìn)入可以執(zhí)行任務(wù)的狀態(tài)。

避免頻繁訪問磁盤。例如,如果您的應(yīng)用程序?qū)顟B(tài)信息保存到磁盤,只有當(dāng)該狀態(tài)信息發(fā)生變化時(shí),才能進(jìn)行此操作,并盡可能合并更改,以避免以頻繁的間隔寫入較小的更改。

不要比所需要的繪制圖像更快。繪圖是一個(gè)昂貴的操作,當(dāng)談到電量。不要依靠硬件來調(diào)節(jié)幀速率。僅繪制與應(yīng)用程序?qū)嶋H需要的幀數(shù)一樣多。

如果您使用UIAccelerometer類接收常規(guī)加速度計(jì)事件,則在不需要時(shí)將其禁用。類似地,將事件傳送的頻率設(shè)置為適合您需要的最小值。有關(guān)更多信息,請(qǐng)參閱UIKit應(yīng)用程序的事件處理指南。Event Handling Guide for UIKit Apps.

傳輸?shù)骄W(wǎng)絡(luò)的數(shù)據(jù)越多,運(yùn)行無線電的功率就越多。實(shí)際上,訪問網(wǎng)絡(luò)是您可以執(zhí)行的功耗最大的操作。您可以按照以下準(zhǔn)則減少該時(shí)間:

只有在需要的時(shí)候才能連接到外部網(wǎng)絡(luò)服務(wù)器,不要輪詢這些服務(wù)器。

當(dāng)您必須連接到網(wǎng)絡(luò)時(shí),傳送完成該作業(yè)所需的最少數(shù)據(jù)量。使用緊湊的數(shù)據(jù)格式,不要包含被忽略的多余的內(nèi)容。

以突發(fā)方式發(fā)送數(shù)據(jù),而不是隨著時(shí)間擴(kuò)散傳輸數(shù)據(jù)包。當(dāng)檢測(cè)到缺少活動(dòng)時(shí),系統(tǒng)會(huì)關(guān)閉Wi-Fi和單元收音機(jī)。當(dāng)它在更長(zhǎng)的時(shí)間內(nèi)傳輸數(shù)據(jù)時(shí),您的應(yīng)用程序使用的功率要比在較短時(shí)間內(nèi)傳輸相同數(shù)據(jù)量的功率大得多。

當(dāng)使用NSURLSession類排列多個(gè)上傳或下載任務(wù)時(shí),將這些項(xiàng)目排隊(duì)在一起,而不是等待一個(gè)完成,然后再啟動(dòng)下一個(gè)。當(dāng)這樣做最有效時(shí),系統(tǒng)管理自動(dòng)執(zhí)行排隊(duì)的任務(wù)。

只要有可能,使用Wi-Fi無線電連接到網(wǎng)絡(luò)。 Wi-Fi使用更少的功率,并且優(yōu)于蜂窩無線電。

如果您使用Core Location框架來收集位置數(shù)據(jù),請(qǐng)盡快禁用位置更新,并將距離過濾器和精度等級(jí)設(shè)置為適當(dāng)?shù)闹?。核心位置使用可用的GPS,小區(qū)和Wi-Fi網(wǎng)絡(luò)來確定用戶的位置。雖然核心位置很難最大限度地減少這些無線電的使用,但設(shè)置準(zhǔn)確性和過濾器值可使核心位置在不需要的情況下完全關(guān)閉硬件。有關(guān)詳細(xì)信息,請(qǐng)參閱位置和地圖編程指南。Location and Maps Programming Guide.

儀器應(yīng)用程序包含幾種用于收集電力相關(guān)信息的工具。您可以使用這些儀器來收集關(guān)于功耗的一般信息,并收集Wi-Fi和藍(lán)牙無線電,GPS接收器,顯示器和CPU等硬件的特定測(cè)量。您還可以啟用設(shè)備上的能量診斷日志記錄來收集信息。有關(guān)使用儀器收集電源相關(guān)數(shù)據(jù)的信息,請(qǐng)參閱“儀器用戶指南”Instruments User Guide。有關(guān)如何啟用設(shè)備上的能源診斷日志記錄的信息,請(qǐng)參閱Instruments幫助。Instruments Help.

有效地使用內(nèi)存

鼓勵(lì)應(yīng)用程序盡可能少地使用內(nèi)存,以便系統(tǒng)可以在內(nèi)存中保留更多的應(yīng)用程序,或?qū)⒏嗟膬?nèi)存專用于真正需要它的前臺(tái)應(yīng)用程序。系統(tǒng)可用內(nèi)存量與應(yīng)用程序的相對(duì)性能之間存在直接的相關(guān)性。較少的可用內(nèi)存意味著系統(tǒng)更有可能在滿足未來內(nèi)存請(qǐng)求時(shí)遇到問題。

為了確保有足夠的可用內(nèi)存可用,您應(yīng)該最小化應(yīng)用程序的內(nèi)存使用情況,并在系統(tǒng)要求您釋放內(nèi)存時(shí)做出響應(yīng)。

觀察低內(nèi)存警告

當(dāng)系統(tǒng)向您的應(yīng)用發(fā)出低內(nèi)存警告時(shí),立即做出回應(yīng)。低內(nèi)存警告是您刪除對(duì)不需要的對(duì)象的引用的機(jī)會(huì)。響應(yīng)這些警告是至關(guān)重要的,因?yàn)椴贿@樣做的應(yīng)用程序更有可能被終止。該系統(tǒng)使用以下API向您的應(yīng)用程序提供內(nèi)存警告:

應(yīng)用程序委托的applicationDidReceiveMemoryWarning:方法。

你的UIViewController類的didReceiveMemoryWarning方法。

UIApplicationDidReceiveMemoryWarningNotification通知。

調(diào)度源類型為DISPATCH_SOURCE_TYPE_MEMORYPRESSURE。這種技術(shù)是唯一可以用來區(qū)分記憶壓力的嚴(yán)重性的技術(shù)。

收到任何這些警告后,您的處理程序方法應(yīng)該立即釋放任何不需要的內(nèi)存。使用警告來清除緩存并釋放圖像。如果您沒有使用大型數(shù)據(jù)結(jié)構(gòu),請(qǐng)將這些結(jié)構(gòu)寫入磁盤并釋放數(shù)據(jù)的內(nèi)存副本。

如果您的數(shù)據(jù)模型包含已知的可清除資源,則可以為UIApplicationDidReceiveMemoryWarningNotification通知提供相應(yīng)的管理對(duì)象注冊(cè),并直接刪除對(duì)其可清除資源的強(qiáng)引用。處理此通知直接避免了通過應(yīng)用程序代理路由所有內(nèi)存警告呼叫的需要。

注意:您可以使用iOS Simulator中的模擬內(nèi)存警告命令在低內(nèi)存條件下測(cè)試應(yīng)用程序的行為。

減少應(yīng)用程序的內(nèi)存占用

從低占有率開始,您可以在以后擴(kuò)大應(yīng)用程序的空間。表7-1列出了有關(guān)如何減少應(yīng)用程序整體內(nèi)存占用的一些提示。

表7-1降低應(yīng)用程序內(nèi)存占用空間的技巧

小帖士

采取行動(dòng)

消除內(nèi)存泄漏。

由于內(nèi)存是iOS中的關(guān)鍵資源,因此您的應(yīng)用程序不應(yīng)該有內(nèi)存泄漏。使用儀器應(yīng)用程序來跟蹤代碼中的泄漏,無論是在模擬器還是在實(shí)際的設(shè)備上。有關(guān)使用儀器的更多信息,請(qǐng)參閱儀器用戶指南。Instruments User Guide

使資源文件盡可能小。

文件駐留在磁盤上,但在使用之前必須加載到內(nèi)存中。壓縮所有圖像文件使其盡可能小。 (要壓縮PNG圖像- iOS應(yīng)用程序的首選圖像格式 - 請(qǐng)使用pngcrush工具。)您可以使用NSPropertyListSerialization類以二進(jìn)制格式編寫屬性列表文件。

對(duì)于大型數(shù)據(jù)集使用Core Data或SQLite。

如果您的應(yīng)用程序操作大量的結(jié)構(gòu)化數(shù)據(jù),請(qǐng)將其存儲(chǔ)在Core Data持久存儲(chǔ)或SQLite數(shù)據(jù)庫中,而不是在平面文件中。Core Data和SQLite都提供了管理大型數(shù)據(jù)集的有效方法,而不需要將整個(gè)集合一次存儲(chǔ)在內(nèi)存中。

負(fù)載資源懶惰。

在實(shí)際需要之前,您不應(yīng)該加載資源文件。預(yù)取資源文件可能似乎是節(jié)省時(shí)間的一種方式,但這種做法實(shí)際上會(huì)減慢您的應(yīng)用程序的速度。另外,如果你最終不使用資源,加載它浪費(fèi)內(nèi)存沒有好的目的。

聰明地分配內(nèi)存

表7-2列出了改善應(yīng)用程序內(nèi)存使用情況的提示。

表7-2分配內(nèi)存的提示

小貼士

采取行動(dòng)

對(duì)資源施加大小限制。

避免在較小的資源文件中加載大型資源文件。不要使用高分辨率圖像,而是使用適合iOS設(shè)備的尺寸。如果您必須使用大量資源文件,請(qǐng)查找在任何給定時(shí)間僅加載所需文件部分的方法。例如,不是將整個(gè)文件加載到內(nèi)存中,而是使用mmap和munmap函數(shù)將文件的部分映射到內(nèi)存中。有關(guān)將文件映射到內(nèi)存的更多信息,請(qǐng)參閱“文件系統(tǒng)性能指南”。

避免無限制的問題集。

無限制的問題集可能需要任意大量的數(shù)據(jù)進(jìn)行計(jì)算。如果該集合需要比可用內(nèi)存更多的內(nèi)存,您的應(yīng)用程序可能無法完成計(jì)算。您的應(yīng)用程序應(yīng)盡可能避免此類設(shè)置,并處理已知內(nèi)存限制的問題。

有關(guān)ARC和內(nèi)存管理的詳細(xì)信息,請(qǐng)參閱Transition to to ARCRelease Notes。Transitioning to ARC Release Notes

調(diào)整您的網(wǎng)絡(luò)代碼

iOS中的網(wǎng)絡(luò)堆棧包括用于通過iOS設(shè)備的無線電硬件進(jìn)行通信的多個(gè)接口。主要的編程接口是CFNetwork框架,它構(gòu)建在CoreFoundation框架中的BSD套接字和不透明類型之上,以與網(wǎng)絡(luò)實(shí)體進(jìn)行通信。您還可以在Foundation框架中使用NSStream類和在系統(tǒng)的CoreOS層中找到的低級(jí)BSD套接字。

有關(guān)如何使用CFNetwork框架進(jìn)行網(wǎng)絡(luò)通信的信息,請(qǐng)參閱“CFNetwork編程指南”CFNetwork Programming Guide和“CFNetwork框架參考”CFNetwork Framework Reference。有關(guān)使用NSStream類的信息,請(qǐng)參閱FoundationFramework Reference。Foundation Framework Reference

高效網(wǎng)絡(luò)技巧

通過網(wǎng)絡(luò)實(shí)現(xiàn)接收或傳輸數(shù)據(jù)的代碼是設(shè)備上功耗最大的操作之一。最大限度地減少發(fā)送或接收數(shù)據(jù)的時(shí)間有助于延長(zhǎng)電池壽命。為此,在編寫與網(wǎng)絡(luò)相關(guān)的代碼時(shí),您應(yīng)該考慮以下提示:

對(duì)于您所控制的協(xié)議,將數(shù)據(jù)格式定義為盡可能的緊湊。

避免使用聊天協(xié)議。

無論何時(shí)可以以突發(fā)方式發(fā)送數(shù)據(jù)包。

蜂窩和Wi-Fi無線電設(shè)計(jì)為在沒有活動(dòng)時(shí)關(guān)閉電源。但是,根據(jù)收音機(jī),這樣做可能需要幾秒鐘。如果您的應(yīng)用程序每隔幾秒鐘發(fā)送一小段數(shù)據(jù),無線電可能會(huì)保持上電狀態(tài),即使沒有實(shí)際執(zhí)行任何操作,也會(huì)繼續(xù)消耗電力。而不是更頻繁地發(fā)送少量數(shù)據(jù),最好是以更大的間隔發(fā)送更大量的數(shù)據(jù)。

當(dāng)通過網(wǎng)絡(luò)進(jìn)行通信時(shí),數(shù)據(jù)包可能隨時(shí)丟失。因此,在編寫網(wǎng)絡(luò)代碼時(shí),應(yīng)該確保在處理故障時(shí)盡可能的保持穩(wěn)定。執(zhí)行響應(yīng)網(wǎng)絡(luò)條件變化的處理程序是完全合理的,但是如果這些處理程序不被稱為一致的,則不要感到驚訝。例如,Bonjour網(wǎng)絡(luò)回調(diào)可能不總是立即響應(yīng)于網(wǎng)絡(luò)服務(wù)的消失而被調(diào)用。Bonjour系統(tǒng)服務(wù)在收到服務(wù)正在消失的通知時(shí)立即調(diào)用瀏覽回調(diào),但網(wǎng)絡(luò)服務(wù)可能會(huì)在沒有通知的情況下消失。如果提供網(wǎng)絡(luò)服務(wù)的設(shè)備意外失去網(wǎng)絡(luò)連接或傳輸中的通知丟失,則可能會(huì)發(fā)生這種情況。

使用Wi-Fi

如果您的應(yīng)用使用Wi-Fi無線電接入網(wǎng)絡(luò),則必須通過將UIRequiresPersistentWiFi密鑰包含在應(yīng)用的Info.plist文件中來通知系統(tǒng)。包含此鍵可以讓系統(tǒng)知道如果它檢測(cè)到任何活動(dòng)的Wi-Fi熱點(diǎn),它應(yīng)該顯示網(wǎng)絡(luò)選擇對(duì)話框。它也讓系統(tǒng)知道在應(yīng)用程序運(yùn)行時(shí)不應(yīng)該嘗試關(guān)閉Wi-Fi硬件。

為了防止Wi-Fi硬件使用太多的電源,iOS有一個(gè)內(nèi)置計(jì)時(shí)器,如果沒有正在運(yùn)行的應(yīng)用程序通過UIRequiresPersistentWiFi鍵請(qǐng)求使用它,則在30分鐘后完全關(guān)閉硬件。如果用戶啟動(dòng)包含該鍵的應(yīng)用程序,則iOS會(huì)在應(yīng)用程序的生命周期內(nèi)有效地禁用計(jì)時(shí)器。然而,一旦該應(yīng)用程序退出或暫停,系統(tǒng)將重新啟用計(jì)時(shí)器。

注意:請(qǐng)注意,即使UIRequiresPersistentWiFi的值為true,當(dāng)設(shè)備空閑(即屏幕鎖定)時(shí),也不起作用。該應(yīng)用被視為無效,雖然它可能在某些級(jí)別上運(yùn)行,但它沒有Wi-Fi連接。

有關(guān)UIRequiresPersistentWiFi鍵和Info.plist文件的鍵的更多信息,請(qǐng)參閱信息屬性列表文件。The Information Property List File

飛行模式警報(bào)

如果您的應(yīng)用程序在設(shè)備處于飛行模式時(shí)啟動(dòng),系統(tǒng)可能會(huì)顯示警報(bào)以通知用戶該事實(shí)。僅當(dāng)滿足以下所有條件時(shí),系統(tǒng)才會(huì)顯示此警報(bào):

您的應(yīng)用程序的信息屬性列表(Info.plist)文件包含UIRequiresPersistentWiFi鍵,該鍵的值設(shè)置為true。

當(dāng)設(shè)備當(dāng)前處于飛行模式時(shí),您的應(yīng)用會(huì)啟動(dòng)。

切換到飛行模式后,設(shè)備上的Wi-Fi尚未手動(dòng)重新啟用。

改進(jìn)文件管理

最小化寫入磁盤的數(shù)據(jù)量。文件操作相對(duì)較慢,涉及寫入到具有有限壽命的閃存驅(qū)動(dòng)器。幫助您最大限度減少文件相關(guān)操作的一些具體技巧包括:

只寫文件的更改部分,并可以匯總更改。避免寫出整個(gè)文件只是為了更改幾個(gè)字節(jié)。

定義文件格式時(shí),將經(jīng)過頻繁修改的內(nèi)容組合在一起,以最大限度地減少每次需要寫入磁盤的塊的總數(shù)。

如果您的數(shù)據(jù)由隨機(jī)訪問的結(jié)構(gòu)化內(nèi)容組成,請(qǐng)將其存儲(chǔ)在Core Data持久存儲(chǔ)或SQLite數(shù)據(jù)庫中,特別是如果您正在操縱的數(shù)據(jù)量可能增長(zhǎng)到超過幾兆字節(jié)。

避免將緩存文件寫入磁盤。此規(guī)則的唯一例外是當(dāng)您的應(yīng)用程序退出時(shí),您需要編寫狀態(tài)信息,以便在下次啟動(dòng)時(shí)將應(yīng)用重新設(shè)置為相同的狀態(tài)。

使應(yīng)用程序備份更有效率

通過iCloud進(jìn)行無線備份,或用戶通過iTunes同步設(shè)備。在備份期間,文件將從設(shè)備傳輸?shù)接脩舻挠?jì)算機(jī)或iCloud帳戶。應(yīng)用程序沙箱中文件的位置確定是否備份和還原這些文件。如果您的應(yīng)用程序創(chuàng)建許多大型文件,并定期更改,并將其放置在備份的位置,則備份可能會(huì)減慢。在編寫文件管理代碼時(shí),您需要注意這一事實(shí)。

應(yīng)用程序備份最佳做法

您無需以任何方式準(zhǔn)備應(yīng)用程序進(jìn)行備份和恢復(fù)操作。具有活動(dòng)iCloud帳戶的設(shè)備在適當(dāng)?shù)臅r(shí)間將其應(yīng)用數(shù)據(jù)備份到iCloud。對(duì)于插入計(jì)算機(jī)的設(shè)備,iTunes會(huì)對(duì)應(yīng)用程序的數(shù)據(jù)文件進(jìn)行增量備份。但是,iCloud和iTunes不會(huì)備份以下目錄的內(nèi)容:

<application_Home>/AppName.app

<Application_Data>/Library/Caches

<Application_Data>/tmp

為了防止同步過程花費(fèi)很長(zhǎng)時(shí)間,請(qǐng)選擇將文件放在應(yīng)用程序主目錄中的位置。存儲(chǔ)大文件的應(yīng)用程序可能會(huì)減慢備份到iTunes或iCloud的過程。這些應(yīng)用程序還可以消耗大量的用戶可用存儲(chǔ)空間,這可能會(huì)鼓勵(lì)用戶刪除應(yīng)用程序或禁用將該應(yīng)用的數(shù)據(jù)備份到iCloud??紤]到這一點(diǎn),您應(yīng)該按照以下準(zhǔn)則存儲(chǔ)應(yīng)用程序數(shù)據(jù):

關(guān)鍵數(shù)據(jù)應(yīng)存儲(chǔ)在 / Documents目錄中。關(guān)鍵數(shù)據(jù)是您的應(yīng)用無法重新創(chuàng)建的任何數(shù)據(jù),例如用戶文檔和其他用戶生成的內(nèi)容。

支持文件包括應(yīng)用程序下載或生成的文件,并且您的應(yīng)用程序可以根據(jù)需要重新創(chuàng)建。存儲(chǔ)應(yīng)用程序支持文件的位置取決于當(dāng)前的iOS版本。

在iOS 5.1及更高版本中,將支持文件存儲(chǔ)在/ Library / Application Support目錄中,并使用setResourceValue:forKey:error:method將NSURLIsExcludedFromBackupKey屬性添加到相應(yīng)的NSURL對(duì)象。(如果您使用的是Core Foundation,請(qǐng)使用CFURLSetResourcePropertyForKey函數(shù)將kCFURLIsExcludedFromBackupKey鍵添加到CFURLRef對(duì)象。)應(yīng)用此屬性可防止將文件備份到iTunes或iCloud。如果您有大量的支持文件,您可以將它們存儲(chǔ)在自定義子目錄中,并將擴(kuò)展屬性應(yīng)用于目錄。

在iOS 5.0及更早版本中,將支持文件存儲(chǔ)在/ Library / Caches目錄中,以防止它們被備份。如果您定位到iOS 5.0.1,請(qǐng)參閱如何防止文件備份到iCloud和iTunes?How do I prevent files from being backed up to iCloud and iTunes有關(guān)如何從備份中排除文件的信息。

緩存數(shù)據(jù)應(yīng)存儲(chǔ)在 / Library /Caches目錄中。您應(yīng)該放在Caches目錄中的文件示例包括(但不限于)數(shù)據(jù)庫緩存文件和可下載內(nèi)容,例如雜志,報(bào)紙和地圖應(yīng)用程序使用的文件。您的應(yīng)用程序應(yīng)該能夠優(yōu)雅地處理系統(tǒng)刪除緩存數(shù)據(jù)以釋放磁盤空間的情況。

臨時(shí)數(shù)據(jù)應(yīng)存儲(chǔ)在 / tmp目錄中。臨時(shí)數(shù)據(jù)包括您不需要長(zhǎng)時(shí)間持續(xù)的任何數(shù)據(jù)。記住在完成這些文件時(shí)刪除這些文件,以便它們不會(huì)繼續(xù)占用用戶設(shè)備上的空間。

雖然iTunes支持應(yīng)用程序包本身,但在每次同步操作中都不會(huì)這樣做。直接從設(shè)備購買的應(yīng)用程序?qū)⒃谠撛O(shè)備下一次與iTunes同步時(shí)進(jìn)行備份。不過,在后續(xù)的同步操作中,應(yīng)用程序不會(huì)備份,除非應(yīng)用程序包本身已更改(例如,應(yīng)用程序已更新)。

有關(guān)如何使用應(yīng)用程序目錄的其他指導(dǎo),請(qǐng)參閱文件系統(tǒng)編程指南。File System Programming Guide

應(yīng)用更新期間保存的文件

當(dāng)用戶下載應(yīng)用程序更新時(shí),iTunes會(huì)將更新安裝到新的應(yīng)用程序目錄中。然后將用戶的數(shù)據(jù)文件從舊安裝移動(dòng)到新的應(yīng)用程序目錄,然后再刪除舊的安裝。在更新過程中,保證以下目錄中的文件被保留:

<Application_Data>/Documents

<Application_Data>/Library

雖然其他用戶目錄中的文件也可能會(huì)被移動(dòng),但是在更新后,您不應(yīng)該依賴它們存在。

移動(dòng)主線程

確保限制您在應(yīng)用程序的主線程上所做的工作類型。主線程是您的應(yīng)用程序處理觸摸事件和其他用戶輸入的位置。為了確保您的應(yīng)用程序始終對(duì)用戶做出響應(yīng),您不應(yīng)該使用主線程來執(zhí)行長(zhǎng)時(shí)間運(yùn)行或潛在的無界任務(wù),例如訪問網(wǎng)絡(luò)的任務(wù)。相反,您應(yīng)該始終將這些任務(wù)移到后臺(tái)線程上。這樣做的首選方法是使用GrandCentral Dispatch(GCD)或NSOperation對(duì)象異步執(zhí)行任務(wù)。

將任務(wù)移動(dòng)到后臺(tái)使您的主線程可以自由地繼續(xù)處理用戶輸入,這在您的應(yīng)用程序啟動(dòng)或退出時(shí)尤其重要。在這些時(shí)期,您的應(yīng)用程序有望及時(shí)響應(yīng)事件。如果您的應(yīng)用程序的主線程在啟動(dòng)時(shí)被阻止,系統(tǒng)可能會(huì)在應(yīng)用程序啟動(dòng)之前終止應(yīng)用程序。如果主線程在退出時(shí)被阻止,系統(tǒng)可以在應(yīng)用程序有機(jī)會(huì)寫出關(guān)鍵的用戶數(shù)據(jù)之前,同樣可以殺死應(yīng)用程序。

有關(guān)使用GCD,操作對(duì)象和線程的更多信息,請(qǐng)參閱并發(fā)編程指南。Concurrency Programming Guide

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,263評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,874評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,095評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,330評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,775評(píng)論 18 139
  • 關(guān)于iOS應(yīng)用程序架構(gòu) 應(yīng)用程序需要與iOS一起工作,以確保他們提供出色的用戶體驗(yàn)。 除了為您的應(yīng)用程序的設(shè)計(jì)和用...
    nicedayCoco閱讀 1,232評(píng)論 0 1
  • 處理應(yīng)用程序狀態(tài)轉(zhuǎn)換的策略 對(duì)于應(yīng)用程序的每個(gè)可能的運(yùn)行時(shí)狀態(tài),系統(tǒng)在您的應(yīng)用程序處于該狀態(tài)時(shí)具有不同的期望值。當(dāng)...
    nicedayCoco閱讀 876評(píng)論 0 0
  • 實(shí)施特定應(yīng)用功能的策略 不同的應(yīng)用程序有不同的需求,但是一些是在許多類型的應(yīng)用程序常見的行為,以下部分提供有關(guān)如何...
    nicedayCoco閱讀 1,498評(píng)論 0 0
  • 機(jī)會(huì)地獲取少量?jī)?nèi)容 需要定期檢查新內(nèi)容的應(yīng)用程序可以要求系統(tǒng)將其喚醒,以便它們可以啟動(dòng)該內(nèi)容的提取操作。要支持此模...
    nicedayCoco閱讀 364評(píng)論 0 0