知識(shí)點(diǎn)2

27、ViewController的didReceiveMemoryWarning是在什么時(shí)候調(diào)用的仇轻?默認(rèn)的操作是什么玩焰?

當(dāng)程序接到內(nèi)存警告時(shí)View Controller將會(huì)收到這個(gè)消息:didReceiveMemoryWarning

從iOS3.0開始欲主,不需要重載這個(gè)函數(shù)别凹,把釋放內(nèi)存的代碼放到viewDidUnload中去。

這個(gè)函數(shù)的默認(rèn)實(shí)現(xiàn)是:檢查controller是否可以安全地釋放它的view(這里加粗的view指的是controller的view屬性)洽糟,比如view本身沒有superview并且可以被很容易地重建(從nib或者loadView函數(shù))炉菲。

如果view可以被釋放,那么這個(gè)函數(shù)釋放view并調(diào)用viewDidUnload坤溃。

你可以重載這個(gè)函數(shù)來釋放controller中使用的其他內(nèi)存拍霜。但要記得調(diào)用這個(gè)函數(shù)的super實(shí)現(xiàn)來允許父類(一般是UIVIewController)釋放view。

如果你的ViewController保存著view的子view的引用薪介,那么祠饺,在早期的iOS版本中,你應(yīng)該在這個(gè)函數(shù)中來釋放這些引用汁政。而在iOS3.0或更高版本中道偷,你應(yīng)該在viewDidUnload中釋放這些引用.

viewDidUnload 被廢棄

在iOS4和iOS5系統(tǒng)中,當(dāng)內(nèi)存不足记劈,應(yīng)用受到MemoryWarning時(shí)勺鸦,系統(tǒng)就會(huì)自動(dòng)調(diào)用當(dāng)前沒有在界面上得ViewController的viewDidUnload方法。通常情況下目木,未顯示在界面的ViewController是UINavigationController Push棧中未在棧頂?shù)腣iewController换途,以及UITabBarController中未顯示的子ViewController。這些ViewController都在MemoryWarning事件發(fā)生時(shí)刽射,讓系統(tǒng)自動(dòng)調(diào)用viewDidUnload 方法军拟。

在iOS6中,由于viewDidUnload 事件任何情況下都不會(huì)被觸發(fā)誓禁,所以蘋果在文檔中建議懈息,應(yīng)該將回收內(nèi)存的相關(guān)操作移到另一個(gè)函數(shù)didReceiveMemoryWarning中。但是如果僅僅寫成:(以下為錯(cuò)誤示例)

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

if(self.isViewLoaded&&!self.view.window)

{

self.view=nil;

}

}

在iOS6以后现横,不建議將view置為nil的原因如下:

1漓拾,UIView有一個(gè)CALayer成員變量,CALayer用于將自己畫到屏幕上戒祠,如下圖:

2骇两,CALayer是一個(gè)bitmap圖像的容器類,當(dāng)UIView調(diào)用自身的drawRect時(shí)姜盈,CALayer才會(huì)創(chuàng)建bitmap圖像類低千。

3,具體占內(nèi)存的其實(shí)是一個(gè)bitmap圖像類,CALayer只占48Bytes示血,UiView只占96Bytes棋傍。而一個(gè)iPad的全屏UIView的bitmap類會(huì)占到12MB的大小。

4难审,在iOS6瘫拣,當(dāng)系統(tǒng)發(fā)出MemoryWarning時(shí),系統(tǒng)會(huì)自動(dòng)回收bitmap類告喊,但是不回收UIView和CALayer類麸拄。這樣既能回收大部分內(nèi)存,又能在需要bitmap類時(shí)黔姜,通過調(diào)用UIView的drawRect:方法重建拢切。

內(nèi)存優(yōu)化:

蘋果系統(tǒng)對(duì)上面的內(nèi)存回收做了一個(gè)優(yōu)化:

1,當(dāng)一段內(nèi)存被分配時(shí)秆吵,它會(huì)被標(biāo)記成“In User”,以防止被重復(fù)使用淮椰。當(dāng)內(nèi)存被釋放時(shí),這段內(nèi)存被標(biāo)記為“Not in use”,這樣有新的內(nèi)存申請(qǐng)時(shí),這塊內(nèi)存就可能被分配給其他變量悲关。

2,CALayer包括的具體的bitmap內(nèi)容的私有成員變量類型為CABackingStore黔牵,當(dāng)收到MemoryWarning時(shí),CABackingStore類型的內(nèi)存會(huì)被標(biāo)記為Volatile類型爷肝,表示這塊內(nèi)存可能再次被原變量使用猾浦。

這樣,有了上面優(yōu)化后灯抛,當(dāng)收到MemoryWarning時(shí)金赦,雖然所有的CALayer所包含的bitmap內(nèi)存被標(biāo)記成volatile了,但是只要這塊內(nèi)存沒有被復(fù)用对嚼,當(dāng)需要重建bitmap內(nèi)存時(shí)夹抗,可以直接被復(fù)用,避免了再次調(diào)用UIView的drawRect:方法纵竖。

簡(jiǎn)單說:對(duì)于iOS6漠烧,不需要做任何以前viewDidUnload的事情,更不需要把以前viewDidUnload的代碼移到didReceiveMemoryWarning方法中靡砌。



30. frame和bounds有什么不同已脓?

31.ViewController生命周期

按照?qǐng)?zhí)行順序排列

- initWithCoder:通過nib文件初始化時(shí)觸發(fā)

- awakeFromNib:nib文件被加載的時(shí)候,會(huì)發(fā)送一個(gè)awakeFromNib的消息到nib文件中的每個(gè)對(duì)象

- loadView:開始加載視圖控制器自帶的view

- viewDidLoad:視圖控制器的view被加載完成

- viewWillAppear:視圖控制器的view將要顯示在window上

- updateViewConstraints:視圖控制器的view開始更新AutoLayout約束

- viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置

- viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置

- viewDidAppear:視圖控制器的view已經(jīng)展現(xiàn)到window上

- viewWillDisappear:視圖控制器的view將要從window上消失

- viewDidDisappear:視圖控制器的view已經(jīng)從window上消失


為什么不建議重載loadView通殃?

永遠(yuǎn)不要主動(dòng)調(diào)用這個(gè)函數(shù)度液。view controller會(huì)在view的property被請(qǐng)求并且當(dāng)前view值為nil時(shí)調(diào)用這個(gè)函數(shù)。如果你手動(dòng)創(chuàng)建view,你應(yīng)該重載這個(gè)函數(shù),且不要在重載的時(shí)候調(diào)用[super loadview]堕担。如果你用IB創(chuàng)建view并初始化view controller已慢,那就意味著你使用initWithNibName:bundle:方法,這時(shí)霹购,你不應(yīng)該重載loadView函數(shù)佑惠。

這個(gè)方法系統(tǒng)的默認(rèn)實(shí)現(xiàn)是這樣:

1;尋找有關(guān)可用的nib文件的信息,根據(jù)這個(gè)信息來加載nib文件 ? ? ? //所以齐疙,nib的加載過程是在loadview中完成的哦兢仰。

2;如果沒有有關(guān)nib文件的信息,默認(rèn)創(chuàng)建一個(gè)空白的UIView對(duì)象剂碴,然后把對(duì)象成賦值給view controller的主view。

所以轻专,如果你決定重載這個(gè)函數(shù)時(shí)忆矛,你也應(yīng)該完成這些步驟:

把子類的view賦給view屬性(property)(你create的view必須是唯一的實(shí)例,并且不被其他任何controller共享)请垛,而且你重載的這個(gè)函數(shù)不應(yīng)該調(diào)用super催训,這個(gè)也是為了保持主view與controller的單一映射關(guān)系。


一宗收、結(jié)構(gòu)

按結(jié)構(gòu)可以對(duì)ios的所有ViewController分成兩類:

1漫拭、主要用于展示內(nèi)容的ViewController,這種ViewController主要用于為用戶展示內(nèi)容混稽,并與用戶交互采驻,如UITableViewController,UIViewController匈勋。

2礼旅、用于控制和顯示其他ViewController的ViewController。這種ViewController一般都是一個(gè)ViewController的容器洽洁。如UINavigationController痘系,UITabbarController。它們都有一個(gè)屬性:viewControllers饿自。其中UINavigationController表示一種Stack式結(jié)構(gòu)汰翠,push一個(gè)ViewController或pop一次,因此后一個(gè)ViewController一般會(huì)依賴前一個(gè)ViewController昭雌。而UITabbarController表示一個(gè)Array結(jié)構(gòu)复唤,各個(gè)ViewController是并列的。

第一種ViewController會(huì)經(jīng)常被繼承烛卧,用來顯示不同的數(shù)據(jù)給用戶苟穆。而第二種很少被繼承,除非你真的需要自定義它。

二雳旅、Controller和View的生命周期

這里指的View是指Controller的View跟磨。它作為Controler的屬性,生命周期在Controller的生命周期內(nèi)攒盈。就是說你的Controller不能在view釋放前就釋放了抵拘。

圖2 ViewController生命周期


當(dāng)你alloc并init了一個(gè)ViewController時(shí),這個(gè)ViewController應(yīng)該是還沒有創(chuàng)建view的型豁。ViewController的view是使用了lazyInit方式創(chuàng)建僵蛛,就是說你調(diào)用的view屬性的getter:[self view]。在getter里會(huì)先判斷view是否創(chuàng)建迎变,如果沒有創(chuàng)建充尉,那么會(huì)調(diào)用loadView來創(chuàng)建view。loadView完成時(shí)會(huì)繼續(xù)調(diào)用viewDidLoad衣形。loadView和viewDidLoad的一個(gè)區(qū)別就是:loadView時(shí)還沒有view驼侠。而viewDidLoad時(shí)view以及創(chuàng)建好了。

當(dāng)view被添加其他view中之前時(shí)谆吴,會(huì)調(diào)用viewWillAppear倒源,而之后會(huì)調(diào)用viewDidAppear。

當(dāng)view從其他view中移出之前時(shí)句狼,會(huì)調(diào)用viewWillDisAppear笋熬,而之后會(huì)調(diào)用viewDidDisappear。

當(dāng)view不在使用腻菇,而且是disappeared胳螟,受到內(nèi)存警告時(shí),那么viewController會(huì)將view釋放并將其指向nil筹吐。

三旺隙、代碼組織(如何設(shè)計(jì)良好的viewcontroller)

ViewController生命周期中有那么多函數(shù),一個(gè)重要問題就是什么代碼該寫在什么地方骏令。

1蔬捷、init里不要出現(xiàn)創(chuàng)建view的代碼。良好的設(shè)計(jì)榔袋,在init里應(yīng)該只有相關(guān)數(shù)據(jù)的初始化周拐,而且這些數(shù)據(jù)都是比較關(guān)鍵的數(shù)據(jù)。init里不要掉self.view凰兑,否則會(huì)導(dǎo)致viewcontroller創(chuàng)建view妥粟。(因?yàn)関iew是lazyinit的)。

2吏够、loadView中只初始化view勾给,一般用于創(chuàng)建比較關(guān)鍵的view如tableViewController的tabView滩报,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前)播急,最好也不要初始化一些非關(guān)鍵的view脓钾。如果你是從nib文件中創(chuàng)建的viewController在這里一定要首先調(diào)用super的loadView方法,但建議不要重載這個(gè)方法桩警。

3可训、viewDidLoad 這時(shí)候view已經(jīng)有了,最適合創(chuàng)建一些附加的view和控件了捶枢。有一點(diǎn)需要注意的是握截,viewDidLoad會(huì)調(diào)用多次(viewcontroller可能多次載入view,參見圖2)烂叔。

4谨胞、viewWillAppear 這個(gè)一般在view被添加到superview之前,切換動(dòng)畫之前調(diào)用蒜鸡。在這里可以進(jìn)行一些顯示前的處理胯努。比如鍵盤彈出,一些特殊的過程動(dòng)畫(比如狀態(tài)條和navigationbar顏色)术瓮。

5、viewDidAppear 一般用于顯示后贰健,在切換動(dòng)畫后胞四,如果有需要的操作,可以在這里加入相關(guān)代碼伶椿。

6辜伟、viewDidUnload 這時(shí)候viewController的view已經(jīng)是nil了。由于這一般發(fā)生在內(nèi)存警告時(shí)脊另,所以在這里你應(yīng)該將那些不在顯示的view釋放了导狡。比如你在viewcontroller的view上加了一個(gè)label,而且這個(gè)label是viewcontroller的屬性偎痛,那么你要把這個(gè)屬性設(shè)置成nil旱捧,以免占用不必要的內(nèi)存,而這個(gè)label在viewDidLoad時(shí)會(huì)重新創(chuàng)建踩麦。


ViewController是iOS開發(fā)中MVC模式中的C枚赡,ViewController是view的controller,ViewController的職責(zé)主要包括管理內(nèi)部各個(gè)view的加載顯示和卸載谓谦,同時(shí)負(fù)責(zé)與其他ViewController的通信和協(xié)調(diào)贫橙。在ios中,有兩類ViewController反粥,一類是顯示內(nèi)容的卢肃,比如UIViewController疲迂、UITableViewController等,同時(shí)還可以自定義繼承自UIViewController的ViewController莫湘;另一類是ViewController容器尤蒿,UINavigationViewController和UITabBarController等,UINavigationController是以Stack的形式來存儲(chǔ)和管理ViewController逊脯,UITabBarController是以Array的形式來管理ViewController优质。和Android中Activity一樣,IOS開發(fā)中军洼,ViewController也有自己的生命周期(Lifecycle)巩螃。

首先來看看View的加載過程,如下圖:


從圖中可以看到匕争,在view加載過程中首先會(huì)調(diào)用loadView方法避乏,在這個(gè)方法中主要完成一些關(guān)鍵view的初始化工作,比如UINavigationViewController和UITabBarController等容器類的ViewController甘桑;接下來就是加載view拍皮,加載成功后,會(huì)接著調(diào)用viewDidLoad方法跑杭,這里要記住的一點(diǎn)是铆帽,在loadView之前,是沒有view的德谅,也就是說爹橱,在這之前,view還沒有被初始化窄做。完成viewDidLoad方法后愧驱,ViewController里面就成功的加載view了,如上圖右下角所示椭盏。

在Controller中創(chuàng)建view有兩種方式组砚,一種是通過代碼創(chuàng)建、一種是通過Storyboard或Interface Builder來創(chuàng)建掏颊,后者可以比較直觀的配置view的外觀和屬性糟红,Storyboard配合IOS6后推出的AutoLayout,應(yīng)該是Apple之后主推的一種UI定制解決方案乌叶,后期我會(huì)專門介紹一篇使用AutoLayout進(jìn)行UI制作的文章改化。言歸正傳,通過IB或Storyboard創(chuàng)建view枉昏,在Controller中創(chuàng)建view后陈肛,會(huì)在Controller中對(duì)view進(jìn)行一些操作,會(huì)出現(xiàn)如下代碼:

@interface MyViewController()

@property (nonatomic) IBOutlet id myButton;

@property (nonatomic) IBOutlet id myTextField;

- (IBAction)myAction:(id)sender;

@end

這里用IBOutlet標(biāo)記了一個(gè)UIButton和一個(gè)UITextField兄裂,用IBAction來標(biāo)記UIButton的響應(yīng)事件句旱,IBOutlet和IBAction都是一個(gè)整形常量阳藻,用來標(biāo)記控件,通過一張圖能比較清晰的看清他們之間的關(guān)系:


上圖中谈撒,MyViewController是繼承自UIViewController的一個(gè)自定義ViewController腥泥,它包含兩個(gè)View,一個(gè)是UIButton啃匿,一個(gè)是UITextField蛔外,從箭頭的指向性上就可以比較好的理解IBOutlet和IBAction了。IBOutlet是告訴Interface Builder溯乒,此實(shí)例變量被連接到nib文件中的view對(duì)象夹厌,IBOutlet本身不做任何操作,只是一個(gè)標(biāo)記作用裆悄。IBAction同樣是個(gè)標(biāo)記關(guān)鍵字矛纹,它只能標(biāo)記方法,它告訴IB用IBAction標(biāo)記的方法可以被某個(gè)控件觸發(fā)光稼。

通過編程的方式創(chuàng)建view或南,如下代碼:

- (void)loadView

{

CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];

UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];

contentView.backgroundColor = [UIColor blackColor];

self.view = contentView;

levelView = [[LevelView alloc] initWithFrame:applicationFrame viewController:self];

[self.view addSubview:levelView];

}

上述代碼首先得到屏幕的frame,然后根據(jù)該frame生成了一個(gè)contentView艾君,并指定當(dāng)前ViewController的root view為contentView采够,然后生成了一個(gè)LevelView的自定義View并將它通過addSubview:方法添加到當(dāng)前ViewController當(dāng)中,完成view的初始化加載冰垄。

關(guān)于loadView方法的重寫蹬癌,官方文檔中有一個(gè)明顯的注釋,原文如下:

Note: When overriding the loadView method to create your views programmatically, you should not call super. Doing so initiates the default view-loading behavior and usually just wastes CPU cycles. Your own implementation of the loadView method should do all the work that is needed to create a root view and subviews for your view controller.

意思是當(dāng)通過代碼方式去創(chuàng)建你自己的view時(shí)播演,在loadView方法中不應(yīng)該調(diào)用super冀瓦,如果調(diào)用[super loadView]會(huì)影響CPU性能伴奥。

接下來我們看看ViewController中的view是如何被卸載的:


從圖中可以看到写烤,當(dāng)系統(tǒng)發(fā)出內(nèi)存警告時(shí),會(huì)調(diào)用didReceiveMemoeryWarning方法拾徙,如果當(dāng)前有能被釋放的view洲炊,系統(tǒng)會(huì)調(diào)用viewWillUnload方法來釋放view,完成后調(diào)用viewDidUnload方法尼啡,至此暂衡,view就被卸載了。此時(shí)原本指向view的變量要被置為nil崖瞭,具體操作是在viewDidUnload方法中調(diào)用self.myButton = nil;

小結(jié)一下:

loadView和viewDidLoad的區(qū)別就是狂巢,loadView時(shí)view還沒有生成,viewDidLoad時(shí)书聚,view已經(jīng)生成了唧领,loadView只會(huì)被調(diào)用一次藻雌,而viewDidLoad可能會(huì)被調(diào)用多次(View可能會(huì)被多次加載),當(dāng)view被添加到其他view中之前斩个,會(huì)調(diào)用viewWillAppear胯杭,之后會(huì)調(diào)用viewDidAppear。當(dāng)view從其他view中移除之前受啥,調(diào)用viewWillDisAppear做个,移除之后會(huì)調(diào)用viewDidDisappear。當(dāng)view不再使用時(shí)滚局,受到內(nèi)存警告時(shí)居暖,ViewController會(huì)將view釋放并將其指向?yàn)閚il。

ViewController的生命周期中各方法執(zhí)行流程如下:

init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc


33. OC中是如何實(shí)現(xiàn)線程同步的核畴?


@synchronized: 添加同步鎖

NSLock:加鎖

NSCondition:加條件鎖

dispatch_async(dispatch_get_main_queue(), ^{}); :異步主線程

NSOperationQueue:添加線程依賴

NSOperationQueue:設(shè)置最大并發(fā)數(shù)為1


36. 編程中膝但,保存數(shù)據(jù)有哪幾種方式?

NSKeyedArchiver:

1.1關(guān)于數(shù)據(jù)的持久化存儲(chǔ)的幾種方式

說到NSKeyedArchiver,也就先要了解下iOS開發(fā)中關(guān)于數(shù)據(jù)持久化存儲(chǔ)的幾種方式:1.屬性列表 2.對(duì)象歸檔 3.數(shù)據(jù)庫存儲(chǔ)(SQLite) 4.Apple提供的CoreData存儲(chǔ)工具谤草,關(guān)于以上存儲(chǔ)方式的使用場(chǎng)景和各自的優(yōu)缺點(diǎn)跟束,我在此就不再贅述了,今天主要談一談關(guān)于第二類存儲(chǔ)方式-對(duì)象歸檔的使用方法和特點(diǎn)丑孩。

1.2什么是對(duì)象歸檔

歸檔是一種很常用的文件儲(chǔ)存方法冀宴,幾乎任何類型的對(duì)象都能夠被歸檔儲(chǔ)存(實(shí)際上是一種文件保存的形式)。

蘋果提供了NSKeyedArchiver和NSKeyedUnarchiver兩個(gè)類以供我們把對(duì)象序列化和反序列化温学,在存儲(chǔ)之前使用NSKeyedArchiver進(jìn)行序列化操作略贮,并且寫入本地文件,在使用之前使用NSKeyedUnarchiver進(jìn)行反序列化的操作仗岖,以供提取使用逃延!

1.3什么場(chǎng)景下會(huì)使用到對(duì)象歸檔

在實(shí)際的開發(fā)過程中,我們會(huì)使用各種數(shù)據(jù)存儲(chǔ)的方式轧拄。

如果是簡(jiǎn)單的進(jìn)行一些系統(tǒng)提供的類型揽祥,例如NSArray,NSDictionary,NSString,BOOL,NSInteger,NSfloat等基本數(shù)據(jù)類型或者對(duì)象,我們可以選擇系統(tǒng)提供的NSUserDefault這個(gè)單例檩电,使用簡(jiǎn)單方便拄丰,但是僅僅只能對(duì)以上這些特定的數(shù)據(jù)格式進(jìn)行存儲(chǔ),是否有些局限性俐末?而且屬性屬性列表這種方式又是否安全呢料按?可能這些這些條件NSUserDefault都無法滿足!

對(duì)于一些規(guī)律性的卓箫,量級(jí)比較大的數(shù)據(jù)载矿,又有規(guī)律可循的數(shù)據(jù),我們可以選擇建表或者使用Apple提供的CoreData進(jìn)行持久化的存儲(chǔ)烹卒!

那么如果數(shù)據(jù)的量級(jí)不是很大闷盔,沒有必要?jiǎng)佑脭?shù)據(jù)庫或者是CoreData這種大規(guī)模的殺傷性武器的時(shí)候魂挂,而且又對(duì)數(shù)據(jù)的安全性和持久性有那么些要求的時(shí)候,我們最好去選擇對(duì)象序列化這種中等殺傷性工具了馁筐!

1.4對(duì)象歸檔的使用方法

使用歸檔的方法對(duì)系統(tǒng)提供的基本類型和基本對(duì)象進(jìn)行歸檔的操作涂召,在這里不再闡述了,如果明白了對(duì)自定義對(duì)象的歸檔和解檔敏沉,那么系統(tǒng)的基本數(shù)據(jù)類型和基本對(duì)象的歸檔和解檔也就相對(duì)很easy了果正!

具體的使用方法我就借用目前我正在開發(fā)維護(hù)的代碼進(jìn)行以下說明:

目前我們產(chǎn)品的需求是在用戶登錄之后,就持久化存儲(chǔ)用戶的登陸相關(guān)信息盟迟,在后續(xù)使用中不需要再次登陸秋泳。當(dāng)用戶沒有退出登錄,卸載程序后攒菠,重新從App Store中現(xiàn)在app后迫皱,同樣保持登陸狀態(tài)。

如果僅僅是登陸之后的登陸狀態(tài)辖众,使用NSUserDefault完全可以實(shí)現(xiàn)卓起,只是安全性不是太好而已,但是當(dāng)用戶在登陸狀態(tài)卸載并且重新安裝app后凹炸,仍然要保持登陸狀態(tài)的話戏阅,那這個(gè)問題就值得思考下了!

基于以上需求啤它,也就是說我們要把用戶的登陸信息存儲(chǔ)在一個(gè)地方奕筐,這個(gè)地方要滿足的條件是1.持久化存儲(chǔ),用戶登錄后变骡,再次打開app不需要重新進(jìn)行登錄????? 2.用戶在登錄的狀態(tài)下卸載app离赫,再次重新安裝后,仍然保持卸載前的登陸狀態(tài)塌碌,也就是要完美重現(xiàn)卸載前的狀態(tài)渊胸!

基于以上的條件,我們自然而然地聯(lián)想到蘋果的sandBox機(jī)制誊爹,關(guān)于蘋果的sandBox機(jī)制蹬刷,我們不再詳述瓢捉,最關(guān)鍵的一點(diǎn)是:在sandBox中的Document目錄下存儲(chǔ)的文件频丘,會(huì)根據(jù)用戶的appleID同步到apple的服務(wù)端,也就是說如果再次安裝app的時(shí)候泡态,此app中的沙盒(sandBox)的Document目錄下的文件會(huì)被再次還原(用戶的app購買信息是和用戶的appleID綁定的)搂漠,那么需求就被完美的滿足了,具體的代碼實(shí)現(xiàn)以及注意事項(xiàng)請(qǐng)繼續(xù)向下閱讀:

@interface IHomeSession : NSObject

@property (nonatomic, strong) NSString *sessionId;? ? ? ? ????? //會(huì)話Id

@property (nonatomic, strong) NSDate?? *lastSessionDate;? ? //記錄上一次請(qǐng)求時(shí)間

@property (nonatomic, strong) NSString *token;? ? ? ? ? ? ?????? //ut值

@property (nonatomic, strong) NSString *alias;? ? ? ? ? ? ??????? //設(shè)備別名

@property (nonatomic, strong) NSString *username;? ? ? ? ??? //用戶名

//序列化對(duì)象的單例

+ (IHomeSession *)sharedMemory;

//保存

- (void)save;

//獲取ssesionId

- (NSString *)getSessionId;

//重置

- (void)reset;

@end

以上是.h文件

具體屬性可以根據(jù)自己的需求進(jìn)行添加

提供獲取序列化單例的方法某弦,方便在項(xiàng)目全局進(jìn)行獲取和使用

提供保存(save),重置(reset)桐汤,獲取sessionId(getSessionId)的api接口以供使用而克,也可以根據(jù)自己的需求添加api

最重要的一點(diǎn),是當(dāng)前類需要遵循NSCoding協(xié)議怔毛,NSCoding協(xié)議中有兩個(gè)方法员萍,都是requred方法,遵循該協(xié)議后拣度,必須實(shí)現(xiàn)碎绎。

以下是.m文件中NSCoding協(xié)議的具體實(shí)現(xiàn)

#pragma mark - NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder

{

[aCoder encodeObject:_sessionId forKey:@"_sessionId"];

[aCoder encodeObject:_lastSessionDate forKey:@"_lastSessionDate"];

[aCoder encodeObject:_token forKey:@"_token"];

[aCoder encodeObject:_username forKey:@"_username"];

[aCoder encodeObject:_alias forKey:@"_alias"];

}

通過以上編碼的方法,對(duì)當(dāng)前類中的屬性進(jìn)行逐一的鍵值編碼抗果!

- (id)initWithCoder:(NSCoder *)aDecoder

{

if (self = [super init])

{

_sessionId = [aDecoder decodeObjectForKey:@"_sessionId"];

_alias = [aDecoder decodeObjectForKey:@"_alias"];

_lastSessionDate = [aDecoder decodeObjectForKey:@"_lastSessionDate"];

_token = [aDecoder decodeObjectForKey:@"_token"];

_username = [aDecoder decodeObjectForKey:@"_username"];

}

return self;

}

通過以上解碼的方法筋帖,對(duì)當(dāng)前類中的屬性,根據(jù)鍵進(jìn)行逐一的逆向編碼冤馏,并返回一個(gè)當(dāng)前類的實(shí)例日麸!

實(shí)現(xiàn)了以上的協(xié)議方法后,我們就可對(duì)當(dāng)前的類對(duì)象進(jìn)行歸檔和解檔的操作了:

首先我們規(guī)定一個(gè)歸檔文件在沙盒中的存儲(chǔ)路徑逮光,寫在一個(gè)類方法中代箭,方便取用,為了滿足app重新安裝后仍然可以獲取到最后一次登陸信息的需求涕刚,我們把文件存儲(chǔ)在沙盒中的第一個(gè)文件夾(Document)中梢卸,這樣可以在程序重新安裝后自動(dòng)回復(fù),原理我在需求分析上已經(jīng)做了闡述副女!

+ (NSString *)path

{

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentDir = [paths objectAtIndex:0];

NSString *dstPath = [documentDir stringByAppendingPathComponent:@"user.data"];

return dstPath;

}

歸檔的方法蛤高,我們集成在save的接口中:

- (void)save

{

[NSKeyedArchiver archiveRootObject:self toFile:[IHomeSession path]];

}

解檔的方法我們集成在單例的獲取中,一定要先查找對(duì)應(yīng)路徑下的文件是否存在碑幅,如果存在進(jìn)行解檔操作戴陡,不存在的話重新生成一個(gè)單例,這樣會(huì)增強(qiáng)程序的健壯性沟涨,防止誤取單例恤批,造成程序崩潰喜庞!

+ (IHomeSession *)sharedMemory

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

if ([[NSFileManager defaultManager] fileExistsAtPath:[IHomeSession path]]) {

instance = [NSKeyedUnarchiver unarchiveObjectWithData:

[NSData dataWithContentsOfFile:[IHomeSession path]]];

}

else

{

instance = [[IHomeSession alloc] init];

}

});

return instance;

}

重置的接口中延都,我們需要?jiǎng)h除本地文件晰房,同時(shí)將單例中的各種屬性恢復(fù)到初始狀態(tài)殊者,然后將初始狀態(tài)下的對(duì)象保存歸檔!

- (void)reset

{

[[NSFileManager defaultManager] removeItemAtPath:[IHomeSession path] error:nil];

instance = [[IHomeSession alloc] init];

instance.sessionId = nil;

instance.lastSessionDate = nil;

instance.token = @"default";

instance.username = nil;

instance.alias = nil;

[instance save];

}

例2:

@implementation person

#pragma mark 寫入文件

-(void)encodeWithCoder:(NSCoder *)encoder{

[super encodeWithCoder:encoder];//不要忘了這個(gè)

[encoder encodeInt:self.age forKey:@"age"];

[encoder encodeObject:self.name forKey:@"name"];

[encoder encodeFloat:self.height forKey:@"height"];

}

#pragma mark 從文件中讀取

-(id)initWithCoder:(NSCoder *)decoder{

self = [super initWithCoder:decoder];//不要忘了這個(gè)

self.age = [decoder decodeIntForKey:@"age"];

self.name = [decoder decodeObjectForKey:@"name"];

self.height = [decoder decodeFloatForKey:@"height"];

return self;

}

//創(chuàng)建

-(void)createPerson{

person *p = [[[person alloc] init] autorelease];

p.age = 20;

p.name = @"Rio";

p.height =1.75f;

//獲得Document的路徑

NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"];//拓展名可以自己隨便取

[NSKeyedArchiver archiveRootObject:p toFile:path];

}

//讀取

-(void)readPerson{

NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"];

person *person1 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

NSLog(@"%@",person1);

}


特點(diǎn):

可以存儲(chǔ)自定義模型對(duì)象

NSKeyedArchiver歸檔相對(duì)較plist存儲(chǔ)而言,它可以直接存儲(chǔ)自定義模型對(duì)象海蔽,而plist文件需要將模型轉(zhuǎn)為字典才可以存儲(chǔ)自定義對(duì)象模型簸搞;

2.歸檔不能存儲(chǔ)大批量數(shù)據(jù)(相比較Sqlite而言),存儲(chǔ)數(shù)據(jù)到文件是將所有的數(shù)據(jù)一下子存儲(chǔ)到文件中准潭,從文件中讀取數(shù)據(jù)也是一下子讀取所有的數(shù)據(jù)趁俊;

缺點(diǎn):

假如你的文件中有100個(gè)對(duì)象了,然后你想在利用歸檔添加一個(gè)對(duì)象刑然,你需要先把所有的數(shù)據(jù)解檔出來寺擂,然后再加入你想添加的那個(gè)對(duì)象,同理泼掠,你想刪除一個(gè)文件中的一個(gè)對(duì)象也是怔软,需要解檔出所有的對(duì)象,然后將其刪除择镇。性能低這樣處理

4.1 基本使用:需要?dú)w檔的模型類必須要遵守NSCoding協(xié)議逝她,然后模型實(shí)現(xiàn)類中必須實(shí)現(xiàn)兩個(gè)方法:1>encodeWithCoder ->歸檔癣疟;2> initWithCoder:? - >解檔

4.2 使用注意:

如果父類也遵守了NSCoding協(xié)議,請(qǐng)注意:

應(yīng)該在encodeWithCoder:方法中加上一句[superencodeWithCode:encode];// 確保繼承的實(shí)例變量也能被編碼,即也能被歸檔應(yīng)該在initWithCoder:方法中加上一句self = [superinitWithCoder:decoder];// 確保繼承的實(shí)例變量也能被解碼案淋,即也能被恢復(fù)

plist文件讀與寫:

通過代碼來創(chuàng)建plist文件妓湘,代碼如下:

//建立文件管理

NSFileManager *fm = [NSFileManager defaultManager];

//找到Documents文件所在的路徑

NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUSErDomainMask, YES);

//取得第一個(gè)Documents文件夾的路徑

NSString *filePath = [path objectAtIndex:0];

//把TestPlist文件加入

NSString *plistPath = [filePath stringByAppendingPathComponent:@"test.plist"];

//開始創(chuàng)建文件

[fm createFileAtPath:plistPath contents:nil attributes:nil];

//刪除文件

[fm removeItemAtPath:plistPath error:nil];

在寫入數(shù)據(jù)之前员咽,需要把要寫入的數(shù)據(jù)先寫入一個(gè)字典中,創(chuàng)建一個(gè)dictionary:

//創(chuàng)建一個(gè)字典

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"zhangsan",@"1",@"lisi",@"2", nil];

//把數(shù)據(jù)寫入plist文件

[dic writeToFile:plistPath atomically:YES];

讀取plist中的數(shù)據(jù),形式如下:

//讀取plist文件装蓬,首先需要把plist文件讀取到字典中

NSDictionary *dic2 = [NSDictionary dictionaryWithContentsOfFile:plistPath];

//打印數(shù)據(jù)

NSLog(@"key1 is %@",[dic2 valueForKey:@"1"]);

NSLog(@"dic is %@",dic2);

關(guān)于plist中的array讀寫,代碼如下:

//把TestPlist文件加入

NSString *plistPaths = [filePath stringByAppendingPathComponent:@"tests.plist"];

//開始創(chuàng)建文件

[fm createFileAtPath:plistPaths contents:nil attributes:nil];

//創(chuàng)建一個(gè)數(shù)組

NSArray *arr = [[NSArray alloc] initWithObjects:@"1",@"2",@"3",@"4", nil];

//寫入

[arr writeToFile:plistPaths atomically:YES];

//讀取

NSArray *arr1 = [NSArray arrayWithContentsOfFile:plistPaths];

//打印

NSLog(@"arr1is %@",arr1);

偏好設(shè)置:

//1.獲取NSUserDefaults對(duì)象

NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];

//2保存數(shù)據(jù)(如果設(shè)置數(shù)據(jù)之后沒有同步, 會(huì)在將來某一時(shí)間點(diǎn)自動(dòng)將數(shù)據(jù)保存到Preferences文件夾下面)

[defaults setObject:@"yangyong" forKey:@"name"];

[defaults setInteger:23 forKey:@"age"];

[defaults setDouble:1.73f forKey:@"height"];

[defaults setObject:@"man" forKey:@"gender"];

//3.強(qiáng)制讓數(shù)據(jù)立刻保存

[defaults synchronize];

(1)偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的, 一般情況不要在偏好設(shè)置中保存其他數(shù)據(jù)屡久。如果利用系統(tǒng)的偏好設(shè)置來存儲(chǔ)數(shù)據(jù), 默認(rèn)就是存儲(chǔ)在Preferences文件夾下面的糙及,偏好設(shè)置會(huì)將所有的數(shù)據(jù)都保存到同一個(gè)文件中。

(2)使用偏好設(shè)置對(duì)數(shù)據(jù)進(jìn)行保存之后, 它保存到系統(tǒng)的時(shí)間是不確定的,會(huì)在將來某一時(shí)間點(diǎn)自動(dòng)將數(shù)據(jù)保存到Preferences文件夾下面聪蘸,如果需要即刻將數(shù)據(jù)存儲(chǔ),可以使用[defaults synchronize];

設(shè)置數(shù)據(jù)時(shí),synchornize方法強(qiáng)制寫入

UserDefaults設(shè)置數(shù)據(jù)時(shí)壤短,不是立即寫入,而是根據(jù)時(shí)間戳定時(shí)地把緩存中的數(shù)據(jù)寫入本地磁盤躲雅。所以調(diào)用了set方法之后數(shù)據(jù)有可能還沒有寫入磁盤應(yīng)用程序就終止了。出現(xiàn)以上問題钮科,可以通過調(diào)用synchornize方法強(qiáng)制寫入

(3)注意點(diǎn):所有的信息都寫在一個(gè)文件中,對(duì)比簡(jiǎn)單的plist可以保存和讀取基本的數(shù)據(jù)類型赃承。

好處:

1.存儲(chǔ)數(shù)據(jù)不需要關(guān)心文件名稱

2.快速存儲(chǔ)鍵值對(duì)

底層實(shí)現(xiàn):

它其實(shí)就是一個(gè)字典

Write寫入方式:永久保存在磁盤中。具體方法為:

第一步:獲得文件即將保存的路徑:

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);//使用C函數(shù)NSSearchPathForDirectoriesInDomains來獲得沙盒中目錄的全路徑抓于。該函數(shù)有三個(gè)參數(shù),目錄類型呕缭、User domain mask睬愤、布爾值砂豌。其中布爾值表示是否需要通過~擴(kuò)展路徑。而且第一個(gè)參數(shù)是不變的筐摘,即為NSSearchPathDirectory 柳畔。在iOS中后兩個(gè)參數(shù)也是不變的确沸,即為:NSUserDomainMask 和 YES观谦。

NSString *ourDocumentPath =[documentPaths objectAtIndex:0];

還有一種方法是使用NSHomeDirectory函數(shù)獲得sandbox的路徑盾剩。具體的用法為:

NSString *sandboxPath = NSHomeDirectory();

// Once you have the full sandbox path, you can create a path from it告私,但是不能在sandbox的本文件層上寫文件也不能創(chuàng)建目錄根悼,而應(yīng)該是此基礎(chǔ)上創(chuàng)建一個(gè)新的可寫的目錄酷麦,例如Documents,Library或者temp母廷。

NSString *documentPath = [sandboxPath stringByAppendingPathComponent:@"Documents"];//將Documents添加到sandbox路徑上馆揉,具體原因前面分析了勤讽!

這兩者的區(qū)別就是:使用NSSearchPathForDirectoriesInDomains比在NSHomeDirectory后面添加Document更加安全巢墅。因?yàn)樵撐募夸浛赡茉谖磥戆l(fā)送的系統(tǒng)上發(fā)生改變芹彬。

第二步:生成在該路徑下的文件:

NSString *FileName=[documentDirectory stringByAppendingPathComponent:fileName];//fileName就是保存文件的文件名

第三步:往文件中寫入數(shù)據(jù):

[data writeToFile:FileName atomically:YES];//將NSData類型對(duì)象data寫入文件,文件名為FileName

最后:從文件中讀出數(shù)據(jù):

NSData data=[NSData dataWithContentsOfFile:FileName options:0 error:NULL];//從FileName中讀取出數(shù)據(jù)

Sqlite:


生成路徑

+(NSString*)path{

NSArray*documentArr =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);NSString*documentPath = [documentArr firstObject];// crylown.db 為數(shù)據(jù)庫的名字NSString*path = [NSString stringWithFormat:@"%@/crylown.db",documentPath];returnpath;}

創(chuàng)建/打開數(shù)據(jù)庫

sqlite3 *database;

int databaseResult = sqlite3_open([[selfpath] UTF8String], &database);

if(databaseResult != SQLITE_OK) {

NSLog(@"創(chuàng)建/打開數(shù)據(jù)庫失敗,%d",databaseResult);

}

創(chuàng)建表

char*error;//? ? 建表格式: create table if not exists 表名 (列名 類型,....)? ? 注: 如需生成默認(rèn)增加的id: id integer primary key autoincrement

const char*createSQL ="create table if not exists list(id integer primary key autoincrement,name char,sex char)";

int tableResult = sqlite3_exec(database, createSQL, NULL, NULL, &error);

if(tableResult != SQLITE_OK) {? ?

?? ? NSLog(@"創(chuàng)建表失敗:%s",error);?

?? }

添加數(shù)據(jù)

// 對(duì)SQL語句執(zhí)行預(yù)編譯intsqlite3_prepare(sqlite3 *db,constchar*sql,intbyte,sqlite3_stmt **stmt,constchar**tail)

1.db代表打開的數(shù)據(jù)庫連接

2.sql代表的sql語句

3.byte代表SQL語句的最大長(zhǎng)度

4.傳出參數(shù),指向預(yù)編譯SQL語句產(chǎn)生的sqlite3_stmt

5.指向SQL語句中未使用的部分

int sqlite3_prapare_v2()版本,代表該函數(shù)的最新版本。

//? 添加//? sql語句格式: insert into 表名 (列名)values(值)

constchar*insertSQL ="insert into haha (name,sex)values('iosRunner','male')";

int insertResult = sqlite3_prepare_v2(database, insertSQL,-1, &stmt,nil);

if(insertResult != SQLITE_OK) {

NSLog(@"添加失敗,%d",insertResult);? ? ??

? }else{//? ? ? ??

? 執(zhí)行sql語句sqlite3_step(stmt);? ? ?

?? }

查找數(shù)據(jù)

//返回sqlite3_stmt(預(yù)編譯SQL語句產(chǎn)生的結(jié)果)const char* sqlite3_colum_int/text...(sqlite3_stmt *,intN)

根據(jù)結(jié)果返回的值的類型不同選擇int/text等,N代表列名在表中的位置。

//? ? 查找//? sql語句格式: select 列名from表名 where 列名 = 參數(shù)? ? ? 注:前面的列名為查詢結(jié)果里所需要看到的 列名,后面的 列名 = 參數(shù) 用于判斷刪除哪條數(shù)據(jù)

const char*searchSQL ="select id,name,sex from haha where name = 'puyun2'";

intsearchResult = sqlite3_prepare_v2(database, searchSQL, -1, &stmt,nil);

if(searchResult !=SQLITE_OK) {

NSLog(@"查詢失敗,%d",searchResult);? ? ? ?

?}else{

while(sqlite3_step(stmt) ==SQLITE_ROW) { ? ? ? ? ?// 查詢的結(jié)果可能不止一條,直到 sqlite3_step(stmt) !=SQLITE_ROW,查詢結(jié)束。

int idWord = sqlite3_column_int(stmt,0);

char *nameWord = (char*) sqlite3_column_text(stmt,1);

char*sexWord = (char*)sqlite3_column_text(stmt,2);

NSLog(@"%d,%s,%s",idWord,nameWord,sexWord);? ? ? ??

? ? }? ? ? ?

?}

修改數(shù)據(jù)

// 修改? ? ? // sql語句格式: update 表名set列名 = 新參數(shù) where 列名 = 參數(shù)? 注:前面的 列名 = 新參數(shù) 是修改的值, 后面的 列名 = 參數(shù) 用于判斷刪除哪條數(shù)據(jù)

const char*changeSQL ="update haha set name = 'buhao' where name = 'iosRunner'";

int updateResult = sqlite3_prepare_v2(database, changeSQL, -1, &stmt,nil);

if(updateResult !=SQLITE_OK) {

NSLog(@"修改失敗,%d",updateResult);? ? ?

?? }else{? ? ? ? ? ??

sqlite3_step(stmt);? ??

? ? }

刪除數(shù)據(jù)

//? ? ? ? 刪除//? ? ? ? sql語句格式: deletefrom表名 where 列名 = 參數(shù)? ? 注:后面的 列名 = 參數(shù) 用于判斷刪除哪條數(shù)據(jù)

const char*deleteSQL ="delete from haha where name = 'iosRunner'";

intdeleteResult = sqlite3_prepare_v2(database, deleteSQL, -1, &stmt,nil);

if(deleteResult !=SQLITE_OK) {

NSLog(@"刪除失敗,%d",deleteResult);? ? ??

? }else{? ? ? ??

? ? sqlite3_step(stmt);?

?? ? ? }

結(jié)束處理

//? ? ? ? 銷毀stmt,回收資源

sqlite3_finalize(stmt);

// ? ?關(guān)閉數(shù)據(jù)庫

sqlite3_close(database);

注意:寫入數(shù)據(jù)庫,字符串可以采用char方式侧蘸,而從數(shù)據(jù)庫中取出char類型晌坤,當(dāng)char類型有表示中文字符時(shí)商乎,會(huì)出現(xiàn)亂碼。這是因?yàn)閿?shù)據(jù)庫默認(rèn)使用ascII編碼方式祭阀。所以要想正確從數(shù)據(jù)庫中取出中文鹉戚,需要用NSString來接收從數(shù)據(jù)庫取出的字符串鲜戒。

NSfileManager:

/**

獲取Documents路徑

@return 返回Documents路徑

*/

- (NSString *)getDocumentsPath {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *path = [paths objectAtIndex:0];

return path;

}

/**

創(chuàng)建文件夾

@param folderName 文件夾的名字

*/

- (void)createDirectoryWithFolderName:(NSString *)folderName {

NSString *documentsPath =[self getDocumentsPath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *iOSDirectory = [documentsPath stringByAppendingPathComponent:folderName];

BOOL isSuccess = [fileManager createDirectoryAtPath:iOSDirectory withIntermediateDirectories:YES attributes:nil error:nil];

if (isSuccess) {

[self remindMessage:CreateFolderSuccess];

} else {

[self remindMessage:CreateFolderFail];

}

}

/**

創(chuàng)建文件

@param name 文件的名字

*/

- (void)createFileWithName:(NSString *)name {

NSString *documentsPath =[self getDocumentsPath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *iOSPath = [documentsPath stringByAppendingPathComponent:name];

BOOL isSuccess = [fileManager createFileAtPath:iOSPath contents:nil attributes:nil];

if (isSuccess) {

[self remindMessage:CreateFileSuccess];

} else {

[self remindMessage:CreateFileFail];

}

}

/**

往文件中寫內(nèi)容

@param name 文件的名字

@param content 要寫入的內(nèi)容

*/

- (void)writeFileWithName:(NSString *)name withContent:(NSString *)content {

NSString *documentsPath = [self getDocumentsPath];

NSString *iOSPath = [documentsPath stringByAppendingPathComponent:name];

BOOL isSuccess = [content writeToFile:iOSPath atomically:YES encoding:NSUTF8StringEncoding error:nil];

if (isSuccess) {

[self remindMessage:WriteFileSuccess];

} else {

[self remindMessage:WriteFileFail];

}

}

/**

讀取文件內(nèi)容

@param name 需要讀取的文件的名字

@return 返回讀取的內(nèi)容

*/

- (NSString *)readFileContentWithName:(NSString *)name {

NSString *documentsPath =[self getDocumentsPath];

NSString *iOSPath = [documentsPath stringByAppendingPathComponent:name];

NSString *content = [NSString stringWithContentsOfFile:iOSPath encoding:NSUTF8StringEncoding error:nil];

return content;

}

/**

判斷文件是否存在

@param filePath 文件路徑

@return 返回BOOL值

*/

- (BOOL)isExistAtPath:(NSString *)filePath {

NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isExist = [fileManager fileExistsAtPath:filePath];

if (!isExist) {

[self remindMessage:NoFileExist];

}

return isExist;

}

/**

判斷文件是否存在,如果不存在,則拷貝

@param fileName 文件的名字

*/

- (void)isExistFileWithName:(NSString *)fileName {

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *filePath = [[self getDocumentsPath] stringByAppendingPathComponent:fileName];

if(![fileManager fileExistsAtPath:filePath]) { //如果不存在

NSLog(@"xxx.txt is not exist");

NSString *nameStr = [NSString stringWithFormat:@"/%@",fileName];

NSString *dataPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:nameStr];//獲取程序包中相應(yīng)文件的路徑

NSError *error;

if ([fileManager copyItemAtPath:dataPath toPath:filePath error:&error]) { //拷貝

[self remindMessage:CopyFileSuccess];

} else {

[self remindMessage:CopyFileFail];

}

}

}

/**

計(jì)算文件大小

@param filePath 文件路徑

@return 返回文件大小

*/

- (unsigned long long)fileSizeAtPath:(NSString *)filePath {

NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isExist = [fileManager fileExistsAtPath:filePath];

if (isExist) {

unsigned long long fileSize = [[fileManager attributesOfItemAtPath:filePath error:nil] fileSize];

return fileSize;

} else {

[self remindMessage:NoFileExist];

return 0;

}

}

/**

計(jì)算整個(gè)文件夾中所有文件大小

@param folderPath 文件夾路徑

@return 返回文件夾中所有文件大小

*/

- (unsigned long long)folderSizeAtPath:(NSString*)folderPath {

NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isExist = [fileManager fileExistsAtPath:folderPath];

if (isExist) {

NSEnumerator *childFileEnumerator = [[fileManager subpathsAtPath:folderPath] objectEnumerator];

unsigned long long folderSize = 0;

NSString *fileName = @"";

while ((fileName = [childFileEnumerator nextObject]) != nil) {

NSString *fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];

folderSize += [self fileSizeAtPath:fileAbsolutePath];

}

return folderSize / (1024.0 * 1024.0);

} else {

[self remindMessage:NoFileExist];

return 0;

}

}

/**

刪除文件

@param name 需要?jiǎng)h除的文件名字

*/

- (void)deleteFileWithName:(NSString *)name {

NSString *documentsPath =[self getDocumentsPath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *iOSPath = [documentsPath stringByAppendingPathComponent:name];

BOOL isSuccess = [fileManager removeItemAtPath:iOSPath error:nil];

if (isSuccess) {

[self remindMessage:DeleteFileSuccess];

} else {

[self remindMessage:DeleteFileFail];

}

}

/**

移動(dòng)文件

@param name 需要移動(dòng)的文件名字

*/

- (void)moveFileWithName:(NSString *)name {

NSString *documentsPath =[self getDocumentsPath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *filePath = [documentsPath stringByAppendingPathComponent:name];

NSString *moveToPath = [documentsPath stringByAppendingPathComponent:name];

BOOL isSuccess = [fileManager moveItemAtPath:filePath toPath:moveToPath error:nil];

if (isSuccess) {

[self remindMessage:MoveFileSuccess];

} else {

[self remindMessage:MoveFileFail];

}

}

/**

文件重命名

@param name1 需要重命名的文件名字

@param name2 作為重命名的文件名字

*/

- (void)renameFileName:(NSString *)name1 willChangeFileName:(NSString *)name2 {

//通過移動(dòng)該文件對(duì)文件重命名

NSString *documentsPath = [self getDocumentsPath];

NSFileManager *fileManager = [NSFileManager defaultManager];

NSString *filePath = [documentsPath stringByAppendingPathComponent:name1];

NSString *moveToPath = [documentsPath stringByAppendingPathComponent:name2];

BOOL isSuccess = [fileManager moveItemAtPath:filePath toPath:moveToPath error:nil];

if (isSuccess) {

[self remindMessage:RenameFileSuccess];

} else {

[self remindMessage:RenameFileFail];

}

}


38. OC中創(chuàng)建線程的方法是什么抹凳?如果在主線程中執(zhí)行代碼,方法是什么黔攒?如果想延時(shí)執(zhí)行代碼觉阅,方法又是什么伤溉?

方法一:

NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(mutableThread) object:nil];

方法二:

[NSThread detachNewThreadSelector:@selector(mutableThread) toTarget:self withObject:nil];

方法三:

[self performSelectorInBackground:@selector(mutableThread) withObject:nil];

方法四:多線程blog創(chuàng)建

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

//會(huì)開啟一個(gè)多線程

[operationQueue addOperationWithBlock:^{

for(int i = 0; i < 50 ;i++)

{

NSLog(@"多線程:%d",i);

}

}];

方法五:

//相當(dāng)于是一個(gè)線程池

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

operationQueue.maxConcurrentOperationCount = 1;//設(shè)置并發(fā)數(shù)

//創(chuàng)建線程

NSInvocationOperation *opertion1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread1) object:nil];

//設(shè)置線程的優(yōu)先級(jí)

[opertion1 setQueuePriority:NSOperationQueuePriorityVeryLow];

//創(chuàng)建另一個(gè)線程

NSInvocationOperation *opertion2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(thread2) object:nil];

[opertion2 setQueuePriority:NSOperationQueuePriorityHigh];

//NSOperation就是一個(gè)操作單元,用來執(zhí)行方法,是一個(gè)抽象類,必須子類化或者使用系統(tǒng)創(chuàng)建好的子類(NSInvocationOperation or NSBlockOperation)

// //NSOperation是最小的操作單元;只能夠執(zhí)行一次;

// //NSInvocationOperation第一步:創(chuàng)建

NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(banZhuanPlus) object:nil];

// //第二步:(不設(shè)置的話不添加到隊(duì)列)在主線程中執(zhí)行

// [invocation start];

//NSBlockOperation第一步:創(chuàng)建

NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{

[self banZhuanPlus];

}];

// //第二步:執(zhí)行(在主線程中執(zhí)行)

// [block start];//如果添加到隊(duì)列就不要start了琢蛤,如果不添加沸毁,當(dāng)前線程就是在主線程中執(zhí)行,如果添加嘲碧,就不是在主線程了

// 這個(gè)隊(duì)列會(huì)自動(dòng)幫咱們創(chuàng)建一個(gè)輔助的線程,這個(gè)時(shí)候當(dāng)前線程就不是主線程了

//這個(gè)隊(duì)列里面只能夠添加NSOperation以及子類的對(duì)象;

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue setMaxConcurrentOperationCount:2];//設(shè)置最大并行數(shù);

[queue addOperation:block];//只要把操作隊(duì)列添加到隊(duì)列中就會(huì)執(zhí)行;

[queue addOperation:invocation];

方法六:

dispatch_queue_t queue = dispatch_queue_create("test",NULL);

dispatch_async(queue,^{

for(int i=0;i<50;i++)

{

NSLog(@"多線程:%d",i);

});

創(chuàng)建線程的方法:

- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]

- [self performSelectorInBackground:nil withObject:nil];

- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];

- dispatch_async(dispatch_get_global_queue(0, 0), ^{});

- dispatch_sync(dispatch_get_global_queue(0, 0), ^{});

- [[NSOperationQueue new] addOperation:nil];

主線程中執(zhí)行代碼的方法:

-[self performSelectorOnMainThread:@selector() withObject:nil waitUntilDone:NO];

- dispatch_async(dispatch_get_main_queue(), ^{});//還有異步

-[[NSOperationQueue mainQueue] addOperationWithBlock:^{

// UI更新代碼

}];

延遲執(zhí)行代碼

NSTimer啟動(dòng)定時(shí)器

sleep(2)? 睡兩秒鐘

+ (void)sleepUntilDate:(NSDate *)date;

+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];

[NSThread sleepForTimeInterval:1.0f];


1.performSelector方法

[self performSelector:@selector(delayMethod) withObject:nil afterDelay:1.0f];

此方式要求必須在主線程中執(zhí)行草戈,否則無效星持。

是一種非阻塞的執(zhí)行方式酒来,

暫時(shí)未找到取消執(zhí)行的方法噩翠。

2.定時(shí)器:NSTimer

[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];

此方式要求必須在主線程中執(zhí)行惕它,否則無效缤沦。

是一種非阻塞的執(zhí)行方式恕汇,

可以通過NSTimer類的- (void)invalidate;取消執(zhí)行。

3. sleep方式

[NSThread sleepForTimeInterval:1.0f]; [self delayMethod];

此方式在主線程和子線程中均可執(zhí)行。

是一種阻塞的執(zhí)行方式纲缓,建方放到子線程中纫塌,以免卡住界面

沒有找到取消執(zhí)行的方法诊县。

GCD延遲執(zhí)行

double delayInSeconds = 1.0;

__block ViewController* bself = self;

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

[bself test1]; });

此方式在可以在參數(shù)中選擇執(zhí)行的線程。

是一種非阻塞的執(zhí)行方式措左,

沒有找到取消執(zhí)行的方法。

39. iOS中有哪些多線程方案避除?

常用的有三種: NSThread NSOperationQueue GCD怎披。

1、NSThread 是這三種范式里面相對(duì)輕量級(jí)的瓶摆,但也是使用起來最負(fù)責(zé)的凉逛,

你需要自己管理thread的生命周期,線程之間的同步群井。線程共享同一應(yīng)用程序的部分內(nèi)存空間状飞,

它們擁有對(duì)數(shù)據(jù)相同的訪問權(quán)限。你得協(xié)調(diào)多個(gè)線程對(duì)同一數(shù)據(jù)的訪問,

一般做法是在訪問之前加鎖诬辈,這會(huì)導(dǎo)致一定的性能開銷酵使。

2、NSOperationQueue 以面向?qū)ο蟮姆绞椒庋b了用戶需要執(zhí)行的操作焙糟,

我們只要聚焦于我們需要做的事情口渔,而不必太操心線程的管理,同步等事情穿撮,

因?yàn)镹SOperation已經(jīng)為我們封裝了這些事情缺脉。

NSOperation 是一個(gè)抽象基類,我們必須使用它的子類悦穿。

3攻礼、 GCD: iOS4 才開始支持,它提供了一些新的特性栗柒,以及運(yùn)行庫來支持多核并行編程秘蛔,

它的關(guān)注點(diǎn)更高:如何在多個(gè)cpu上提升效率。

總結(jié):

- NSThread是早期的多線程解決方案傍衡,實(shí)際上是把C語言的PThread線程管理代碼封裝成OC代碼深员。

- GCD是取代NSThread的多線程技術(shù),C語法+block蛙埂。功能強(qiáng)大倦畅。

- NSOperationQueue是把GCD封裝為OC語法,額外比GCD增加了幾項(xiàng)新功能绣的。

* 最大線程并發(fā)數(shù)

* 取消隊(duì)列中的任務(wù)

* 暫停隊(duì)列中的任務(wù)

* 可以調(diào)整隊(duì)列中的任務(wù)執(zhí)行順序叠赐,通過優(yōu)先級(jí)

* 線程依賴

* NSOperationQueue支持KVO。 這就意味著你可以觀察任務(wù)的狀態(tài)屬性屡江。

但是NSOperationQueue的執(zhí)行效率沒有GCD高芭概,所以一半情況下,我們使用GCD來完成多線程操作惩嘉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末罢洲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子文黎,更是在濱河造成了極大的恐慌惹苗,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耸峭,死亡現(xiàn)場(chǎng)離奇詭異桩蓉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)劳闹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門院究,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洽瞬,“玉大人,你說我怎么就攤上這事业汰』锴裕” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蔬胯,是天一觀的道長(zhǎng)对供。 經(jīng)常有香客問我,道長(zhǎng)氛濒,這世上最難降的妖魔是什么产场? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮舞竿,結(jié)果婚禮上京景,老公的妹妹穿的比我還像新娘。我一直安慰自己骗奖,他們只是感情好确徙,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著执桌,像睡著了一般鄙皇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仰挣,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天伴逸,我揣著相機(jī)與錄音,去河邊找鬼膘壶。 笑死错蝴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的颓芭。 我是一名探鬼主播顷锰,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亡问!你這毒婦竟也來了官紫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤玛界,失蹤者是張志新(化名)和其女友劉穎万矾,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慎框,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年后添,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笨枯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖馅精,靈堂內(nèi)的尸體忽然破棺而出严嗜,到底是詐尸還是另有隱情,我是刑警寧澤洲敢,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布漫玄,位于F島的核電站,受9級(jí)特大地震影響压彭,放射性物質(zhì)發(fā)生泄漏睦优。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一壮不、第九天 我趴在偏房一處隱蔽的房頂上張望汗盘。 院中可真熱鬧,春花似錦询一、人聲如沸隐孽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菱阵。三九已至,卻和暖如春缩功,著一層夾襖步出監(jiān)牢的瞬間晴及,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工掂之, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抗俄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓世舰,卻偏偏與公主長(zhǎng)得像动雹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子跟压,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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