適用于iOS的應用程序編程指南(六)

實施特定應用功能的策略

不同的應用程序有不同的需求,但是一些是在許多類型的應用程序常見的行為,以下部分提供有關如何在應用程序中實現特定類型的功能的指導。

隱私策略

在設計應用程序時,保護用戶的隱私是重要的考慮因素。隱私保護包括保護用戶的數據,包括用戶的身份和個人信息。系統框架已經提供隱私控制來管理諸如聯系人之類的數據,但是您的應用程序應該采取措施保護您在本地使用的數據。

使用磁盤加密保護數據

數據保護使用內置硬件以磁盤上的加密格式存儲文件,并根據需要進行解密。當用戶的設備被鎖定時,受保護的文件是無法訪問的,甚至是創建它們的應用程序。在應用程序可以訪問其受保護的文件之前,用戶必須解鎖設備(通過輸入相應的密碼)。

大多數iOS設備都提供數據保護,并符合以下要求:

用戶設備上的文件系統必須支持數據保護。大多數設備支持此行為。

用戶必須為設備設置一個活動的密碼鎖。

要保護文件,請向文件添加一個屬性,指示所需的保護級別。使用NSData類或NSFileManager類添加此屬性。編寫新文件時,可以使用具有相應保護值的writeDataFile:options:NSData的error:method作為寫入選項之一。對于現有文件,可以使用NSFileManager的setAttributes:ofItemAtPath:error:method來設置或更改NSFileProtectionKey的值。使用這些方法時,請為文件指定以下保護級別之一:

無保護 - 文件已加密,但不受密碼保護,并在設備鎖定時可用。指定NSDataWritingFileProtectionNone選項(NSData)或NSFileProtectionNone屬性(NSFileManager)。

完成 - 設備被鎖定時,文件被加密并且不可訪問。指定NSDataWritingFileProtectionComplete選項(NSData)或NSFileProtectionComplete屬性(NSFileManager)。

完成,除非已經打開 - 文件已加密。當設備被鎖定時,無法訪問關閉的文件。用戶解鎖設備后,您的應用程序可以打開該文件并使用該文件。如果用戶在文件打開時鎖定設備,您的應用程序可以繼續訪問它。指定NSDataWritingFileProtectionCompleteUnlessOpen選項(NSData)或NSFileProtectionCompleteUnlessOpen屬性(NSFileManager)。

完成,直到第一次登錄 - 文件被加密,無法訪問,直到設備啟動并且用戶已解鎖一次。指定NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication選項(NSData)或NSFileProtectionCompleteUntilFirstUserAuthentication屬性(NSFileManager)。

如果您保護文件,您的應用程序必須準備好丟失對該文件的訪問。啟用完整的文件保護后,當用戶鎖定設備時,您的應用程序將失去讀取和寫入文件內容的能力。您可以使用以下技術之一跟蹤對受保護文件狀態的更改:

應用程序委托可以實現applicationProtectedDataWillBecomeUnavailable:和applicationProtectedDataDidBecomeAvailable:methods。

任何對象都可以注冊UIApplicationProtectedDataWillBecomeUnavailable和UIApplicationProtectedDataDidBecomeAvailable通知。

任何對象都可以檢查共享UIApplication對象的protectedDataAvailable屬性的值,以確定文件當前是否可訪問。

對于新文件,建議您在寫入任何數據之前啟用數據保護。如果使用writeToFile:options:error:方法將NSData對象的內容寫入磁盤,則會自動進行。對于現有文件,添加數據保護將使用新的受保護版本替換未受保護的文件。

識別您的應用程序的唯一用戶

只有在這樣做時,才能識別應用程序的用戶,才能為該用戶提供明確的收益。如果您只需要將應用的一個用戶與其他用戶區分開來,則iOS提供可幫助您執行此操作的標識符。但是,如果您需要更高級別的安全性,您可能需要自己做更多的工作。例如,提供金融服務的應用程序可能希望提示用戶登錄憑據,以確保用戶有權訪問特定帳戶。

重要提示:在識別用戶時,始終要透明了解您獲取的任何信息的意圖。識別用戶是不可接受的,防止您可以偷偷跟蹤它們。

以下是一些常見的情況,可能需要您識別用戶,以及如何實現它們的解決方案。

您希望將用戶鏈接到服務器上的特定帳戶。包括登錄屏幕,要求用戶安全地輸入帳戶信息。通過以加密形式存儲,始終保護您從用戶收集的帳戶信息。

您想區分不同設備上運行的應用程序的實例。使用UIDevice類的identifierForVendor屬性來獲取一個區分一個設備上的用戶與其他設備上的用戶的ID?,F在,該技術可以讓您識別特定的用戶。單個用戶可以具有多個設備,每個設備具有不同的ID值。

您想要為廣告目的識別用戶。使用ASIdentifierManager類的advertisingIdentifier屬性獲取用戶的ID。

由于用戶可以在所有iOS設備上運行應用程序,因此Apple不提供在多個設備上識別同一用戶的方法。如果您需要識別特定用戶,則必須使用普遍唯一的ID(UUID),登錄帳戶或其他類型的身份識別系統提供您自己的解決方案。

評級限制

用戶可以設置限制,指定要在應用程序中使用的媒體的評級。如果您的應用程序根據限制播放媒體或修改其行為,則需要確定當前設置并在設置更改時進行響應。

要獲取當前設置,請獲取共享的standardUserDefaults對象,并使用objectForKey:方法查看以下項的值:

媒體評級鍵值

com.apple.content-rating.ExplicitBooksAllowed

布爾。如果此鍵的值為NO,則不允許顯式書籍

com.apple.content-rating.ExplicitMusicPodcastsAllowed

布爾。如果此鍵的值為NO,則不允許顯示音樂,電影和播客。

com.apple.content-rating.AppRating

的NSNumber。該鍵的值范圍為0到1000.不允許其評級高于當前鍵值的應用程序。

com.apple.content-rating.MovieRating

的NSNumber。該鍵的值范圍為0到1000.不允許其分級高于當前鍵值的影片。

com.apple.content-rating.TVShowRating

的NSNumber。該鍵的值范圍為0到1000.不允許其等級高于當前鍵值的電視節目。

注意:如果objectForKey:為特定鍵返回nil,則表示有關此特定限制的信息不可用。在這種情況下,您的應用程序可以使用自己的策略來確定適當的評級。

要檢測用戶何時更改限制,請注冊通知NSUserDefaultsDidChangeNotification。共享的standardUserDefaults對象在檢測到位于其中一個持久域中的首選項的更改時,會將此通知發送到您的應用程序。

應用程序評級是針對我們的國家/地區代碼定義的,并被普遍應用。表5-1顯示了與每個美國應用程序評級相關聯的值。

表5-1應用程序評分

評分名稱

數值

4+

100

9+

200

12+

300

17+

600

電影和電視的評分因國家而異。如果一個國家或地區沒有為電影或電視節目指定評級系統,您的應用程序應使用自己的策略來確定適當的評級。雖然大多數地區定義電影評級,但只有少數人定義電視節目評分。

區域可以定義多個評級級別,每個級別與描述評級的名稱和范圍為0到1000的數字相關聯。例如,美國使用字符串“G”,數字100指定最低級別電影評級等級。

即使您的應用沒有播放媒體,也可能需要將自己的評分系統映射到電影或電視節目評分系統。例如,只有當美國電影評級“R”被允許時,游戲才能啟用某些功能。要查看當前的評分列表,請下載此文檔的伴隨文件(鏈接位于頁面頂部)。

支持多種版本的iOS

支持最新版本的iOS以及一個或多個早期版本的應用程序必須使用運行時檢查來防止在舊版本的iOS上使用較新的API。運行時檢查會阻止您的應用程序在嘗試使用當前操作系統上不可用的功能時崩潰。

您可以進行幾種類型的檢查:

要確定一個類是否存在,請查看它的Class對象是否為nil。鏈接器對于任何未知的類對象返回nil,使得可以使用類似于以下的條件檢查:

if ([UIPrintInteractionController class]) {

// Create an instance of the class anduse it.

}

else {

// The print interaction controller isnot available so use an alternative technique.

}

要確定方法是否可用于現有類,請使用instancesRespondToSelector:class方法或respondToSelector:instance方法。

要確定基于C的函數是否可用,請執行函數名為NULL的布爾比較。如果符號不為NULL,則可以調用該函數。例如:

if(UIGraphicsBeginPDFPage!= NULL){

UIGraphicsBeginPDFPage();

}

有關如何編寫支持多個部署目標的代碼的更多信息和示例,請參閱SDK兼容性指南。SDK Compatibility Guide

保持你的應用程序的視覺外觀Across Launches

即使您的應用程序支持后臺執行,也不能永久運行。在某些時候,系統可能需要終止您的應用程序以釋放當前前臺應用程序的內存。但是,用戶不應該關心應用程序是否已在運行或終止。從用戶的角度來說,退出應用程序似乎暫時中斷。當用戶返回到應用程序時,該應用程序應始終將用戶返回到最后一個使用點,以便用戶可以繼續執行任何正在進行的任務。這種行為為用戶提供了更好的體驗,并且與UIKit內置的狀態恢復支持相對容易實現。

UIKit中的狀態保存系統提供了一個簡單但靈活的基礎設施,用于保存和恢復應用程序視圖控制器和視圖的狀態?;A設施的工作是在適當的時候推動維護和恢復過程。為此,UIKit需要您的應用程序的幫助。只有您了解您的應用程序的內容,因此只有您可以編寫保存和還原該內容所需的代碼。當您更新應用程序的UI時,只有您知道如何將較舊的保留內容映射到界面中的較新對象。

你有三個地方必須在你的應用程序中考慮狀態保存:

您的應用程序委托對象,它管理應用程序的頂級狀態

您的應用程序的視圖控制器對象,用于管理應用程序用戶界面的整體狀態

您的應用的自定義視圖,可能有一些需要保留的自定義數據

UIKit允許您選擇要保留的用戶界面的哪些部分。如果您已經有處理狀態保存的自定義代碼,則可以繼續使用該代碼,并根據需要將部分遷移到UIKit狀態保存系統。

在您的應用程序中啟用狀態保存和恢復

狀態保存和恢復不是自動功能,應用程序必須選擇使用它。應用程序通過在其應用程序委托中實施以下方法來表明他們對該功能的支持:

應用:shouldSaveApplicationState:

應用:shouldRestoreApplicationState:

通常,這些方法的實現只是返回YES,表示可以發生狀態保存和恢復。但是,要在不發生操作的情況下,有條件地保留和恢復狀態的應用程序可以返回NO。例如,在發布對您的應用程序的更新后,您可能希望從應用程序返回NO:shouldRestoreApplicationState:方法,如果您的應用程序無法從先前版本有效地還原狀態。

保存和恢復過程

狀態保存和恢復是一個選擇加入功能,并在您的應用程序的幫助下工作。您的應用程序標識應該保留的對象,UIKit會在適當的時候執行保存和還原這些對象的工作。因為UIKit處理這么多的過程,所以它有助于了解它在幕后做的事情,以便您了解自定義代碼如何適應整體方案。

在考慮狀態保護和恢復時,首先有助于分離兩個進程。 UIKit會在適當的時間保留應用的狀態,例如當您的應用程序從前臺移動到后臺時。當UIKit確定需要新的狀態信息時,它會查看應用程序的視圖和視圖控制器,以查看哪些應該保留。對于每個對象,UIKit將保存相關數據寫入加密的磁盤文件。下一次您的應用程序從頭開始,UIKit會查找該文件,如果存在,則使用它來嘗試恢復應用程序的狀態。因為文件是加密的,所以狀態保存和恢復只有在設備解鎖時才會發生。

在恢復過程中,UIKit使用保留的數據重新構建您的界面,但實際對象的創建將由您的代碼來處理。因為您的應用程序可能會自動從故事板文件加載對象,只有您的代碼知道需要創建哪些對象,哪些對象可能已經存在,并且可以簡單地返回。在創建每個對象之后,UIKit將使用保留的狀態信息來初始化它們。

在保存和恢復過程中,您的應用程序有很少的責任。

在保存期間,您的應用程序負責:

告訴UIKit它支持狀態保存。

告訴UIKit應該保留哪些視圖控制器和視圖。

編碼任何保留對象的相關數據。

在恢復期間,您的應用程序負責:

告訴UIKit它支持狀態恢復。

提供(或創建)UIKit請求的對象。

解碼保存對象的狀態并使用它將對象返回到之前的狀態。

在應用程序的責任方面,最重要的是告訴UIKit在后續啟動期間保留哪些對象并提供這些對象。 這兩個行為是設計應用程序的保存和恢復代碼時應該花費大部分時間的地方。他們也是您對實際過程最有控制力的地方。 為了理解為什么會這樣,它有助于看一個例子。

圖5-1顯示了用戶與幾個選項卡進行交互之后,選項卡欄界面的視圖控制器層次結構。 您可以看到,一些視圖控制器將自動加載作為應用程序的主要故事板文件的一部分,但是某些視圖控制器已被呈現或被推送到不同選項卡中的視圖控制器上。在沒有狀態恢復的情況下,僅在主故事板文件中的視圖控制器將在后續啟動期間恢復。 通過添加對狀態恢復的支持,您可以保留所有的視圖控制器。


圖5-1示例視圖控制器層次結構

UIKit僅保留那些具有分配的恢復標識符的對象。 恢復標識符是一個字符串,用于標識UIKit和您的應用程序的視圖或視圖控制器。此字符串的值僅對您的代碼有意義,但是此字符串的存在告訴UIKit它需要保留標記的對象。

在保存過程中,UIKit會拖動應用程序的視圖控制器層次結構,并保留具有恢復標識符的所有對象。如果視圖控制器沒有恢復標識符,則該視圖控制器及其所有視圖和子視圖控制器都不會被保留。 圖5-2顯示了先前視圖層次結構的更新版本,現在具有應用于大多數(但不是全部)視圖控制器的恢復標識。


圖5-2添加恢復標識查看控制器

根據您的應用程序,保留每個視圖控制器可能或可能無意義。如果視圖控制器顯示臨時信息,則可能不希望在恢復時返回到同一點,而是選擇將用戶返回到界面中更穩定的位置。

對于您選擇保留的每個視圖控制器,您還需要確定以后如何還原它。 UIKit提供了兩種重建對象的方式。您可以讓您的應用程序委托重新創建它,或者您可以將恢復類分配給視圖控制器,并讓該類重新創建它。恢復類實現UIViewControllerRestoration協議,并負責在還原時查找或創建指定的對象。以下是有關何時使用每一個的提示:

如果視圖控制器在啟動時始終從應用程序的主要故事板文件中加載,請不要分配恢復類。相反,讓您的應用程序委托找到該對象,或者利用UIKit的支持來隱式查找恢復的對象。

對于在啟動時未從主故事板文件加載的視圖控制器,請分配恢復類。最簡單的選擇是使每個視圖控制器都有自己的恢復類。

在保存過程中,UIKit識別要保存并將每個受影響對象的狀態寫入磁盤的對象。每個視圖控制器對象都有機會寫出任何要保存的數據。例如,選項卡視圖控制器保存所選選項卡的標識。UIKit還將視圖控制器的恢復類信息保存到磁盤。如果任何視圖控制器的視圖具有恢復標識符,UIKit也會要求它們保存狀態信息。

下一次應用程序啟動時,UIKit照常加載應用程序的主要故事板或nib文件,調用應用程序委托的應用程序:willFinishLaunchingWithOptions:方法,然后嘗試恢復應用程序的上一個狀態。它的第一件事是要求您的應用程序提供與保留的控件對象匹配的一組視圖控制器對象。如果給定的視圖控制器具有分配的恢復類,則要求該類提供對象;否則,應用程序代理被要求提供。

保存過程的流程

圖5-3顯示了在狀態保存期間發生的高級事件,并顯示了應用程序的對象如何受到影響。在保存甚至發生之前,UIKit會通過調用應用程序來發出應用程序委托:shouldSaveApplicationState:method。如果該方法返回YES,則UIKit將開始收集和編碼應用程序的視圖和視圖控制器。完成后,將編碼的數據寫入磁盤。


圖5-3高級流界面保存

下次應用程序啟動時,系統會自動查找保留的狀態文件,如果存在,則使用它來恢復您的界面。由于此狀態信息僅與應用程序的上一個和當前啟動周期相關,因此在應用程序完成啟動后,該文件通常會被丟棄。只要恢復您的應用程序出現錯誤,該文件也會被丟棄。例如,如果您的應用程序在恢復過程中崩潰,系統會在下一個啟動周期內自動丟棄狀態信息,以避免再次崩潰。

恢復過程的流程

圖5-4顯示了狀態恢復期間發生的高級別事件,并顯示了應用程序的對象如何受到影響。標準初始化和UI加載完成后,UIKit會通過調用應用程序來調用應用程序:shouldRestoreApplicationState:method來請求您的應用程序委托狀態恢復。這是您的應用程序代理機會檢查保留的數據,并確定是否可以恢復狀態。如果是,UIKit使用應用程序委托和恢復類來獲取對應用程序的視圖控制器的引用。然后向每個對象提供需要將其恢復到其先前狀態的數據。


圖5-4恢復用戶界面的高級流程

雖然UIKit有助于還原各個視圖控制器,但它不會自動恢復這些視圖控制器之間的關系。相反,每個視圖控制器負責編碼足夠的狀態信息以將其自身恢復到其先前的狀態。例如,導航控制器對其導航堆棧上的視圖控制器的順序的信息進行編碼。然后,它稍后使用此信息將這些視圖控制器返回到堆棧上的先前位置。具有嵌入式子視圖控制器的其他視圖控制器同樣負責編碼他們以后要還原他們的孩子所需的任何信息。

注意:并非所有視圖控制器都需要對子視圖控制器進行編碼。例如,標簽欄控制器不編碼有關其子視圖控制器的信息。相反,假設您的應用程序遵循在創建標簽欄控制器本身之前創建適當的子視圖控制器的常規模式。

由于您負責重新創建應用程序的視圖控制器,因此您可以在恢復過程中更改您的界面。例如,您可以重新排序標簽欄控制器中的選項卡,并仍然使用保留的數據將每個選項卡返回到之前的狀態

。當然,如果您對視圖控制器層次結構進行了顯著的更改,例如在應用程序更新期間,您可能無法使用保留的數據。

當您排除視圖控制器組時會發生什么?

當視圖控制器的恢復標識符為零時,該視圖控制器及其管理的任何子視圖控制器不會自動保留。例如,在圖5-5中,由于導航控制器沒有恢復標識符,因此從保留的數據中省略它及其所有子視圖控制器和視圖。


圖5-5從自動保存過程中排除視圖控制器

即使您決定不保留視圖控制器,這并不意味著所有這些視圖控制器完全從視圖層次結構中消失。 在啟動時,您的應用程序仍然可以創建視圖控制器作為其默認設置的一部分。例如,如果任何視圖控制器從應用程序的故事板文件自動加載,它們仍將出現,盡管是默認配置,如圖5-6所示。



圖5-6加載默認視圖控制器

還有一點需要注意的是,即使視圖控制器沒有自動保留,您仍然可以編碼對該視圖控制器的引用并手動保留。在圖5-5中,第一導航控制器的三個子視圖控制器具有恢復標識符,即使父導航控制器沒有。如果您的應用程序委托(或任何保留的對象)編碼對這些視圖控制器的引用

,則會保留其狀態。即使導航控制器中的命令未被保存,您仍然可以使用這些引用來重新創建視圖控制器,并在后續的啟動周期將它們安裝在導航控制器中。

實施狀態保護和恢復清單

支持狀態保存和恢復需要修改您的應用程序委托并查看控制器對象以對狀態信息進行編碼和解碼。如果您的應用程序有任何自定義視圖也具有可保留的狀態信息,那么您還需要修改這些對象。

在您的代碼中添加狀態保存和恢復時,請使用以下列表來提醒您需要編寫的代碼。

(必需)實現應用程序:shouldSaveApplicationState:和應用程序:shouldRestoreApplicationState:應用程序委托中的方法;請參閱在您的應用程序中啟用狀態保存和恢復。

(必需)通過為其restoreIdentifier屬性分配一個非空字符串,將恢復標識符分配給要保留的每個視圖控制器;請參閱標記您的視圖控制器進行保存。

如果要保存特定視圖的狀態,請將非空字符串分配給它們的restoreIdentifier屬性;看保持你的意見狀態。

(必需)從應用程序顯示應用程序的窗口:willFinishLaunchingWithOptions:應用程序委托的方法。狀態恢復機構需要窗口,以便可以恢復應用程序界面的滾動位置和其他相關位。

將修復類分配給相應的視圖控制器。 (如果不這樣做,您的應用程序委托被要求在還原時提供相應的視圖控制器。)請參閱在啟動時恢復視圖控制器。

(推薦)使用encodeRestorableStateWithCoder:和decodeRestorableStateWithCoder編碼和解碼視圖的狀態并查看控制器:這些對象的方法;請參閱編碼和解碼視圖控制器的狀態。

使用應用程序對應用程序的任何版本信息或其他狀態信息進行編碼和解碼:willEncodeRestorableStateWithCoder:和應用程序:didDecodeRestorableStateWithCoder:應用程序委托的方法;請參閱保存應用程序的高級狀態。

作為表視圖和集合視圖的數據源的對象應實現UIDataSourceModelAssociation協議。雖然不是必需的,但是該協議有助于在這些類型的視圖中保留所選和可見的項目。請參閱實施易于維護的數據源。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容