李洪強經典面試題21

怎麼設置緩存數據的大小和緩存時間?java

AFN是怎麼工做的,運行時的字典轉模型怎麼作?react

UITableViewCell怎麼優化?android

UITableViewCell有個NSString *reuseIdentifier屬性,能夠在初始化UITableViewCell的時候傳入一個特定的字符串標識來設置reuseIdentifier(通常用UITableViewCell的類名)。當UITableView要求dataSource返回UITableViewCell時,先 經過一個字符串標識到對象池中查找對應類型的UITableViewCell對象,若是有,就重用,若是沒有,就傳入這個字符串標識來初始化⼀一個UITableViewCell對象。ios

 

SVN和Git的比較程序員

1. Git是分佈式的,SVN是集中式的,好處是跟其餘同事不會有太多的衝突,本身寫的代碼放在本身電腦上,一段時間後再提交、合併,也能夠不用聯網在本地提交;web

2. Git下載下來後,在本地沒必要聯網就能夠看到全部的log,很方便學習,SVN卻須要聯網;面試

3. Git鼓勵分Branch,而SVN,說實話,我用Branch的次數還挺少的,SVN自帶的Branch merge我還真沒用過,有merge時用的是Beyond Compare工具合併後再Commit的;數據庫

4. Tortoise也有出Git版本,真是好東西;json

5. SVN在Commit前,咱們都建議是先Update一下,跟本地的代碼編譯沒問題,並確保開發的功能正常後再提交,這樣其實挺麻煩的,有好幾回同事沒有先Updata,就
Commit了,發生了一些錯誤,耽誤了你們時間,Git可能這種狀況會少些。設計模式

還問了一個loadView的使用場景

 

loadView在每一次使用self.view這個property,而且self.view爲nil的時候被調用,用以產生一個有效的 self.view。這個接口本來是爲了讓咱們自定義view用的。在不被subclass實現的狀況下,也就是[super loadView]的效果,應該就是產生了一個有效的view,也就是一個空白的view。

在上面這種狀況下,loadView被實現爲空(只有一條打印語句),並且咱們沒有經過XIB初始化ViewController,因此在 viewDidLoad被執行時,self.view是爲nil的。因此在執行[self.view addSubView:customButton]時,loadView被調用,用來產生一個有效的view,使得self.view再也不爲nil。罷 特,咱們錯了(-_-!)。咱們的loadView什麼也沒有作,因而就出現了上面的情形,不斷的調用一個什麼都不作的loadView....

固然,咱們只要在loadView中增長一句[super loadView]就沒有問題了。但這並非Cocoa的設計者所指望的。

loadView僅僅應該在開發者但願自行經過編碼而不是Interface Builder定製view的時候被實現,並且不該該在其中調用[super loadView],你的loadView中應該有self.view = ...這樣的行爲。

若是僅僅是想要在當前view上增長一些UIButton或是UILabel,應該在viewDidLoad裏去作,此時不要實現本身的loadView。

 

14. 淺複製和深複製的區別?

答案:淺層複製:只複製指向對象的指針,而不復制引用對象自己。
深層複製:複製引用對象自己。
意思就是說我有個A對象,複製一份後獲得A_copy對象後,對於淺複製來講,A和A_copy指向的是同一個內存資源,複製的只不過是是一個指針,對象自己資源
仍是隻有一份,那若是咱們對A_copy執行了修改操做,那麼發現A引用的對象一樣被修改,這其實違背了咱們複製拷貝的一個思想。深複製就好理解了,內存中存在了
兩份獨立對象自己。

 

json解析的用法,用框架的用法簡單介紹:

底層原理遍歷字符串中的字符,最終根據格式規定的特殊字符,好比{}號,[]號, : 號 等進行區分,

 {}號是一個字典的開始,[]號是一個數組的開始, : 號是字典的鍵和值的分水嶺,最終乃是將json數據轉化爲字典,

字典中值多是字典,數組,或字符串而已。

18. 代理的做用?


答案:代理的目的是改變或傳遞控制鏈。容許一個類在某些特定時刻通知到其餘類,而不須要獲取到那些類的指針。能夠減小框架複雜度。
另一點,代理能夠理解爲java中的回調監聽機制的一種相似。

19. 什麼是推送消息?

推送則是服務器端主動push。

 

38. 堆和棧的區別 

管理方式:對於棧來說,是由編譯器自動管理,無需咱們手工控制;對於堆來講,釋放工做由程序員控制,容易產生memory leak。 

申請大小: 

棧: 在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就肯定的常數),若是申請的空間超過棧的剩餘空間時,將提示overflow。因 此,能從棧得到的空間較小。 

堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是因爲系統是用鏈表來存儲的空閒內存地址的,天然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。因而可知,堆得到的空間比較靈活,也比較大。 

碎片問題:對於堆來說,頻繁的new/delete勢必會形成內存空間的不連續,從而形成大量的碎片,使程序效率下降。對於棧來說,則不會存在這個問題,由於棧是先進後出的隊列,他們是如此的一一對應,以致於永遠都不可能有一個內存塊從棧中間彈出 

分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,好比局部變量的分配。動態分配由alloca函數進行分配,可是棧的動態分配和堆是不一樣的,他的動態分配是由編譯器進行釋放,無需咱們手工實現。 

分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的。

 

 

1. SEL和IMP

最先我把SEL理解爲函數指針,如今看來,不是這樣的。

1.1 SEL類型

SEL類型的變量,能夠經過@selector(方法名)來取得,固然了,Objective C的方法名,咱們也知道了,多麼噁心(比Java廢話還多)。

而它真正獲得的,只要方法名同樣,它的值就是同樣的,無論這個方法定義於哪一個類,是否是實例方法【再說了,@selector的時候,除了方法名也沒有什麼類啊,對象啊什麼事情】。

因此我如今把它理解爲「方法名的某種映射結果」。(從C++程序員的眼光看,我以爲沒啥能夠對應的,既不是函數指針,也不是函數指針類型,像是函數指針類型的名字吧。)

1.2 IMP類型

這個纔是函數指針,IMP能夠從 對象 & SEL的方法獲得:

IMP imp = [self methodForSelector:selector];

這是IMP的定義:

typedef id (*IMP)(id, SEL,  );

另外注意NSObject裏面的這兩個方法:

- (IMP)methodForSelector:(SEL)aSelector;

+ (IMP)instanceMethodForSelector:(SEL)aSelector;

1. - (void)makeObjectsPerformSelector:(SEL)aSelector;  

2. - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument;  

 

這是 NSArray和NSSet的兩個方法,相信你們不多用,它相似於 for循環,但有效率高於for循環

makeObjectsPerformSelector:相似於NSNotifation機制,併發的執行同一件事,不能像for循環那樣區別對待

因此參數 argument 必須是非基本類型 ,若是要是用基本類型 請轉換程 NSNumber 或者NSValue。

 

用法:若是一個數組objArr中存儲了一組有hide屬性的對象,須要將數組裏全部對象的hide所有賦值爲真,就能夠這麼寫:

 

1. [objArr makeObjectsPerformSelector:@selector(setHidden:) withObject:@YES];  

 

不用再去for循環調用隱藏了

簡歷最好上午9點或者下午2點投

工做經驗寫在前面

技能寫在後面

 

怎麼用post  get

三、GET請求,將參數直接寫在訪問路徑上。操做簡單,不過容易被外界看到,安全性不高,地址最多255字節;

四、POST請求,將參數放到body裏面。POST請求操做相對複雜,須要將參數和地址分開,不過安全性高,參數放在body裏面,不易被捕獲。

Static

const就是隻讀的意思,只在聲明中使用; 

static通常有2個做用,規定做用域和存儲方式.對於局部變量,static規定其爲靜態存儲方式,每次調用的初始值爲上一次調用的值,調用結束後存儲空間不釋放; 

對於全局變量,若是以文件劃分做用域的話,此變量只在當前文件可見;對於static函數也是在當前模塊內函數可見. 

static const 應該就是上面二者的合集. 

下面分別說明: 

全局: 

const,只讀的全局變量,其值不可修改. 

static,規定此全局變量只在當前模塊(文件)中可見. 

static const,既是隻讀的,又是隻在當前模塊中可見的. 

文件: 

文件指針可看成一個變量來看,與上面所說相似. 

函數: 

const,返回只讀變量的函數. 

static,規定此函數只在當前模塊可見. 

類: 

const,通常不修飾類,(在VC6.0中試了一下,修飾類沒啥做用) 

AFNetworking 和 ASI區別使用

1、底層實現

  一、AFN的底層實現基於OC的NSURLConnection和NSURLSession 

  二、ASI的底層實現基於純C語言的CFNetwork框架 
  三、由於NSURLConnection和NSURLSession是在CFNetwork之上的一層封裝,所以ASI的運行性能高於AFN

2、對服務器返回的數據處理

一、ASI沒有直接提供對服務器數據處理的方式,直接返回的是NSData/NSString 

二、AFN提供了多種對服務器數據處理的方式 
   (1)JSON處理-直接返回NSDictionary或者NSArray 
   (2)XML處理-返回的是xml類型數據,需對其進行解析 
   (3)其餘類型數據處理

3、監聽請求過程 

一、AFN提供了success和failure兩個block來監聽請求的過程(只能監聽成功和失敗) 
  * success : 請求成功後調用 
  * failure : 請求失敗後調用 
  二、ASI提供了3套方案,每一套方案都能監聽請求的完整過程 
  (監聽請求開始、接收到響應頭信息、接受到具體數據、接受完畢、請求失敗) 
  * 成爲代理,遵照協議,實現協議中的代理方法 
  * 成爲代理,不遵照協議,自定義代理方法 
  * 設置block

4、在文件下載和文件上傳的使用難易度  

一、AFN 
  *不容易實現監聽下載進度和上傳進度 
  *不容易實現斷點續傳 
  *通常只用來下載不大的文件 
  二、ASI 
  *很是容易實現下載和上傳 
  *很是容易監聽下載進度和上傳進度 
  *很是容易實現斷點續傳 
  *下載大文件或小文件都可 
  三、實現下載上傳推薦使用ASI

5、網絡監控 

一、AFN本身封裝了網絡監控類,易使用 
  二、ASI使用的是Reachability,由於使用CocoaPods下載ASI時,會同步下載Reachability,但Reachability做爲網絡監控使用較爲複雜(相對於AFN的網絡監控類來講) 
  三、推薦使用AFN作網絡監控-AFNetworkReachabilityManager

6、ASI提供的其餘實用功能 

一、控制信號旁邊的圈圈要不要在請求過程當中轉 
  二、能夠輕鬆地設置請求之間的依賴:每個請求都是一個NSOperation對象 
  三、能夠統一管理全部請求(還專門提供了一個叫作ASINetworkQueue來管理全部的請求對象) 
  * 暫停/恢復/取消全部的請求 
  * 監聽整個隊列中全部請求的下載進度和上傳進度

數據庫原理    

NSUserDefaults,用於存儲配置信息

NSUserDefaults被設計用來存儲設備和應用的配置信息,它經過一個工廠方法返回默認的、也是最經常使用到的實例對象

NSUserDefaults把配置信息以字典的形式組織起來,支持字典的項包括:字符串或者是數組,除此以外還支持數字等基本格式。

NSUserDefaults的全部數據都放在內存裏,所以操做速度很快,並還提供一個歸檔方法:+ (void)synchronize。

    

SQLite,用於存儲查詢需求較多的數據

SQLite擅長處理的數據類型其實與NSUserDefaults差很少,也是基礎類型的小數據,只是從組織形式上不一樣。開發者能夠以關係型數據庫的方

式組織數據,使用SQL DML來管理數據。 

通常來講應用中的格式化的文本類數據能夠存放在數據庫中,尤爲是相似聊天記錄、Timeline等這些具備條件查詢和排序需求的數據。

    

CoreData,用於規劃應用中的對象

官方給出的定義是,一個支持持久化的,對象圖和生命週期的自動化管理方案。嚴格意義上說CoreData是一個管理方案,他的持久化能夠經過

SQLite、XML或二進制文件儲存。如官方定義所說,CoreData的做用遠遠不止儲存數據這麼簡單,它能夠把整個應用中的對象建模並進行自動化的

管理。

    

使用基本對象類型定製的個性化緩存方案(plist)

 

Readwrite能使用場景?

assign:指定setter方法用簡單的賦值,這是默認操做。你能夠對標量類型(如int)使用這個屬性。你能夠想象一個float,它不是一個對象,因此它不能retain、copy。

assign:簡單賦值,不更改索引計數(Reference Counting).使用assign: 對基礎數據類型 (NSInteger)和C數據類型(int, float, double, char,等)

retain:指定retain應該在後面的對象上調用,前一個值發送一條release消息。你能夠想象一個NSString實例,它是一個對象,並且你可能想要retain它。

retain:釋放舊的對象,將舊對象的值賦予輸入對象,再提升輸入對象的索引計數爲1 ,使用retain: 對其餘NSObject和其子類 ,retain,是說明該屬性在賦值的時候,先release以前的值,而後再賦新值給屬性,引用再加1。

copy:指定應該使用對象的副本(深度複製),前一個值發送一條release消息。基本上像retain,可是沒有增長引用計數,是分配一塊新的內存來放置它。copy是建立一個新對象,retain是建立一個指針,引用對象計數加1。copy: 創建一個索引計數爲1的對象,而後釋放舊對象,copy是建立一個新對象,retain是建立一個指針,引用對象計數加1。

readonly:將只生成getter方法而不生成setter方法(getter方法沒有get前綴)。

readwrite:默認屬性,將生成不帶額外參數的getter和setter方法(setter方法只有一個參數)。

atomic:對於對象的默認屬性,就是setter/getter生成的方法是一個原子操做。若是有多個線程同時調用setter的話,不會出現某一個線程執行setter所有語句以前,另外一個線程開始執行setter的狀況,相關於方法頭尾加了鎖同樣。

nonatomic:不保證setter/getter的原子性,多線程狀況下數據可能會有問題。nonatomic,非原子性訪問,不加同步,多線程併發訪問會提升性能。先釋放原先變量,再將新變量      retain而後賦值;

Mvc使用

MVC 約定, Model 

不容許與View 打交道。 Model 是管理數據的, 當Model中的數據發生變化時,與之對應的視圖應更新。 這就須要一種機制來支持。爲此 

iOS 框架提供了兩種支持機制: Notification 和KVO (Key-Value Observing)。

  KVO 可簡單理解爲,爲你所關注的 Key 對象註冊一個監聽器。 當有數據發生變化時,就會發出廣播給全部的監聽器。

      MVC 也約定, View 不容許直接引用Modal, 它只能被Controller 所控制。 Controller 控制 

View 顯示什麼數據。咱們知道,View 所要顯示的數據是來源於 Modal, View 上產生的事件 ( 好比 Touch事件)須要通知 

Controller。 既然MVC 不容許直接打交道,就須要提供一種機制。 

  不錯, iOS 確實提供了一種機制, 名曰: Delegate。 Delegate 這個詞, 

有人將它譯爲「委託」,也有人將它譯爲「代理」。名稱上的差別沒有什麼,重要的是如何理解 Delegate。 

Delegate設計模式的引入,就是爲了解決UIView與Controller鬆耦合互動問題。

UserDefault:輕量級的本地數據存數方式,用於保存,恢復應用程序相關的偏好設置,配置數據等

 

 

測試打包

1.登陸apple的開發者主頁:developer.apple.com

2.選擇Ad Hoc生成一個ios_distribution.cer: 讓電腦具有打包程序的能力

3.利用用戶設備的UDID註冊設備

4.新建一個App ID : 方便打包哪一個程序

5.選擇Ad Hoc利用ios_distribution.cer + 設備UDID + App ID --> 描述文件

(描述文件的做用:

1> 能知道在哪臺電腦上, 爲哪臺設備打包哪一個程序

2> 哪臺設備須要安裝打包哪一個程序)

6.最終產生了3個文件

1> CertificateSigningRequest.certSigningRequest

* 包含了電腦的信息

* 發送給蘋果服務器, 蘋果服務器根據文件信息來生成一個電腦證書

* 生成的證書就可讓對應的電腦具有某個特殊的能力

2> ios_distribution.cer

* 打包證書

* 安裝這個證書後, 電腦就具有打包程序的能力

3> mj_iphone5_news.mobileprovision

* 裏面包含了3個信息:ios_distribution.cer + 設備UDID + App ID

7.安裝證書和描述文件

1> ios_distribution.cer

2> mj_iphone5_news.mobileprovision

8.項目Scheme右邊的設備選擇iOS Device

9.點擊Xcode的菜單

Product --> Archive --> Distribute --> ....Ad Hoc... --> 選擇對應的描述文件

10.生成一個ipa文件,發給測試人員和客戶

* ipa本質是zip

* android的安裝包是APK格式,本質也是zip

7.  5.6.7.8適配  區別

 

 

 

11  什麼地圖   集成

如何快速集成百度地圖:

註冊百度開發者賬號=》建立應用=》下載SDK=》集成開發=》測試應用=》發佈應用

一、註冊百度開發者帳號

百度帳號註冊地址:https://passport.baidu.com/v2/?reg&regType=1&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F,若是你已經有百度帳號能夠跳過這步。

登陸後,進入開發者中心,註冊成爲百度開發者,http://developer.baidu.com/user/info?u=http://lbsyun.baidu.com/apiconsole/key?from=developer,填寫好我的信息,提交。

二、建立新應用

在使用百度地圖SDK以前須要先獲取百度地圖移動版開發密鑰。

百度地圖iOS SDK開發密鑰的申請地址爲:http://lbsyun.baidu.com/apiconsole/key。

點擊以上網址進入API控制檯,選擇建立應用,填寫應用信息:

三、下載IOS SDK百度地圖iOS SDK的下載地址:http://developer.baidu.com/map/sdkiosdev-download.htm

進入後能夠下載所有功能,也能夠根據本身須要選擇模塊選擇下載:

 1)新建一個工程

2)添加百度SDK和靜態庫

解壓下載後的iOS SDK壓縮包將壓縮包內的inc文件夾和mapapi.bundle文件拷貝到工程目錄下。

接下來根據模擬器和真機兩種環境選擇使用靜態庫文件:

若是用的真機,就導入iphoneos文件夾內的libbaidumapapi.a文件到工程,若是用的模擬器,就導入iphonesimulator文件夾內的libbaidumapapi.a文件到工程。 

引入以下圖所示的framework:

而後照着API一步步走唄

單元測試:

測試軟件中局部某個業務的正確性

 

 

 

AFNetworking實際上使用了兩個獨立的緩存機制:

AFImagecache:一個提供圖片內存緩存的類,繼承自NSCache。

NSURLCache:NSURLConnection’s默認的URL緩存機制,用於存儲NSURLResponse對象:一個默認緩存在內存,經過配置能夠緩存到磁盤的類。

AFImageCache是如何工做的

AFImageCache是UIImageView+AFNetworking分類的一部分。它繼承自NSCache,經過一個URL字符串做爲它的key(從NSURLRequest中獲取)來存儲UIImage對象。

NSURLCache如何工做

默認是能夠的,但最好仍是手動配置一下

既然AFNetworking使用NSURLConnection,它利用了原生的緩存機制NSURLCache。NSURLCache緩存了從服務器返回的NSURLResponse對象。

NSURLCache的shareCache方法默認是可使用的,緩存獲取的內容。不幸的是,它的默認配置只是緩存在內存並無寫到硬盤。爲了解決這個問題,你能夠聲明一個 sharedCache,

 

怎麼設置緩存數據的大小和緩存時間,

 爲了提升程序的響應速度,能夠考慮使用緩存(內存緩存\硬盤緩存)

   

  第一次請求數據時,內存緩存中沒有數據,硬盤緩存中沒有數據。

緩存數據的過程

   

當服務器返回數據時,須要作如下步驟

(1)使用服務器的數據(好比解析、顯示)

(2)將服務器的數據緩存到硬盤(沙盒)

此時緩存的狀況是:內存緩存中有數據,硬盤緩存中有數據。

再次請求數據分爲兩種狀況:

(1)若是程序並無被關閉,一直在運行

  那麼此時內存緩存中有數據,硬盤緩存中有數據。若是此時再次請求數據,直接使用內存緩存中的數據便可

(2)若是程序從新啓動

  那麼此時內存緩存已經消失,沒有數據,硬盤緩存依舊存在,還有數據。若是此時再次請求數據,須要讀取內存中緩存的數據。

提示:從硬盤緩存中讀取數據後,內存緩存中又有數據了

 

3、緩存的實現

1.說明:

因爲GET請求通常用來查詢數據,POST請求通常是發大量數據給服務器處理(變更性比較大)

所以通常只對GET請求進行緩存,而不對POST請求進行緩存

  在iOS中,可使用NSURLCache類緩存數據

  iOS 5以前:只支持內存緩存。從iOS 5開始:同時支持內存緩存和硬盤緩存

 

2.NSURLCache

iOS中得緩存技術用到了NSURLCache類。

緩存原理:一個NSURLRequest對應一個NSCachedURLResponse

緩存技術:把緩存的數據都保存到數據庫中。

 

3.NSURLCache的常見用法

(1)得到全局緩存對象(不必手動建立)NSURLCache *cache = [NSURLCache sharedURLCache]; 

(2)設置內存緩存的最大容量(字節爲單位,默認爲512KB)- (void)setMemoryCapacity:(NSUInteger)memoryCapacity;

(3)設置硬盤緩存的最大容量(字節爲單位,默認爲10M)- (void)setDiskCapacity:(NSUInteger)diskCapacity;

(4)硬盤緩存的位置:沙盒/Library/Caches

(5)取得某個請求的緩存- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; 

(6)清除某個請求的緩存- (void)removeCachedResponseForRequest:(NSURLRequest *)request;

(7)清除全部的緩存- (void)removeAllCachedResponses;

 

4.緩存GET請求

  要想對某個GET請求進行數據緩存,很是簡單

  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

  // 設置緩存策略

  request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

  只要設置了緩存策略,系統會自動利用NSURLCache進行數據緩存

 

5.iOS對NSURLRequest提供了7種緩存策略:(實際上能用的只有4種)

NSURLRequestUseProtocolCachePolicy // 默認的緩存策略(取決於協議)

NSURLRequestReloadIgnoringLocalCacheData // 忽略緩存,從新請求

NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未實現

NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略緩存,從新請求

NSURLRequestReturnCacheDataElseLoad// 有緩存就用緩存,沒有緩存就從新請求

NSURLRequestReturnCacheDataDontLoad// 有緩存就用緩存,沒有緩存就不發請求,當作請求出錯處理(用於離線模式)

NSURLRequestReloadRevalidatingCacheData // 未實現

 

6.緩存的注意事項

緩存的設置須要根據具體的狀況考慮,若是請求某個URL的返回數據:

  (1)常常更新:不能用緩存!好比股票、彩票數據

  (2)一成不變:果斷用緩存

  (3)偶爾更新:能夠按期更改緩存策略 或者 清除緩存

提示:若是大量使用緩存,會越積越大,建議按期清除緩存

 

/*--------------------------------------- 卡住主線程------------------------------------------*/

重點:1.線程進程區別(面試)! 2.串行執行!

{

    1. 問題演示 :

    爲何在執行打印輸出(執行耗時代碼)的時候, UITextView 不能滾動? 按鈕不能點擊?

 

    由於在同一條線程中,代碼按順序執行!因此在執行打印輸出(執行耗時代碼)的時候,卡住了主線程!

 

    如何解決這個問題? NSThread類開啓新線程!

 

    // 開啓一條新的線程: NSThread

    // 1.建立一條線程;

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

    // 2.啓動線程; 調用start方法,告訴CPU線程準備就緒;線程被 CPU 調度以後會自動執行@selector()中的方法;

    [thread start];

 

    2. 線程

 

    應用程序中的代碼是由線程來執行的!

    一個進程至少包含一條線程!

    在一個應用程序啓動以後,會默認開啓一條線程 ----> 主線程!

    主線程以外的線程---->子線程

 

    問題:線程是如何執行應用程序中的代碼的?

 

    串行執行:

    在線程中的代碼是按順序執行的!同一時間內,只能有一個代碼塊執行!

 

 

    3. 進程

 

    就是在系統中'正在運行'的應用程序!

 

    進程爲應用程序開闢獨立的內存空間;

 

    // 這塊內存空間是獨立的,受保護的!進程和進程之間是互不干擾的!

}

/*-------------------------  多線程實現原理 ------------------------*/

重點:併發執行!

{

    1. 問題又來了! 爲何開啓一條新線程以後就能解決卡住主線程這個問題了呢?

 

    答: 由於線程和線程之間是併發執行(同時執行)!

 

    2.多線程

 

    進程是由許多條線程組成的!

    一個進程能夠包含不少條線程,每條線程均可以執行不一樣的代碼!

 

    併發執行(同時執行):

    線程和線程之間是同時執行的!  ----> 提升程序的運行效率!

 

 

    爲何多條線程之間能夠併發(同時)執行呢?

 

    線程是由 CPU 來執行的,同一時間只能有一條線程被執行!CPU在多條線程之間快速的切換!

    因爲 CPU 的執行速度很是快!就給咱們形成了多條線程併發執行的'假象'  ------- 多線程實現原理!

 

 

    既然多線程這麼爽, 線程是否是越多越好呢?

 

    <1> 開啓線程須要消耗必定的內存(默認狀況下,線程佔用 512KB 的棧區空間);

    <2> 會使應用程序增長不少代碼!代碼變多以後,程序複雜性就會提升!

    <3> CPU 在多條線程之間來回切換!線程越多, CPU就越累!

 

    建議: 在移動應用的開發中; 通常只開3~5條線程!

}

/*-------------------  UI 線程-----------------*/

重點:用戶體驗(面試)!

{

    在 iOS 開發中,多線程開發的知識點:// 只有知道了這些知識點,你們才能進行多線程的開發!

 

    1. 主線程又稱爲 UI 線程! 主線程的做用: // 有關UI操做,建議都放在主線程中執行!

 

    <1> 更新UI/ 刷新UI界面;

    <2> 處理 UI 事件(點擊/拖拽/滾動等)

 

    2. 耗時操做會卡住主線程!,影響 UI 操做的流暢度!給用戶一種'卡頓'的壞體驗!

 

    注意點:別將耗時操做放在主線程中執行!

}

/*----------------------------iOS中多線程實現方案 1.pthread----------------------*/

重要知識點:

在 C 語言中的 void * 就等同於 OC 中的 id;

Ref/ _t

{

    添加 pthread.h

    

    #import <pthread.h>

 

    看一遍代碼!有時間看,沒時間就別看!

}

/*-------------------------------------- 橋接 (__bridge) ------------------------------------*/

重點:爲何要使用橋接?你是怎麼進行混合開發的?

{

    橋接 (__bridge) :C 和 OC 之間傳遞數據的時候須要使用橋接! why?爲何呢?

    

    1.內存管理:

        在 OC 中,若是是在 ARC環境下開發,編譯器在編譯的時候會根據代碼結構,自動爲 OC 代碼添加 retain/release/autorelease等.   ----->自動內存管理(ARC)的原理!

    

        可是, ARC只負責 OC 部分的內存管理!不會負責 C 語言部分代碼的內存管理!

        也就是說!即便是在 ARC 的開發環境中!若是使用的 C 語言代碼出現了 retain/copy/new/create等字樣呢!咱們都須要手動爲其添加 release 操做!不然會出現內存泄露!

    

        在混合開發時(C 和 OC 代碼混合),C 和 OC 之間傳遞數據須要使用 __bridge 橋接,目的就是爲了告訴編譯器如何管理內存

 

        在 MRC中不須要使用橋接! 由於都須要手動進行內存管理!

    

    2.數據類型轉換:

    

        Foundation 和 Core Foundation框架的數據類型能夠互相轉換的

        Foundation :  OC

        Core Foundation : C語言

    

        NSString *str = @"123"; // Foundation

        CFStringRef str2 = (__bridge CFStringRef)str; // Core Foundation

        NSString *str3 = (__bridge NSString *)str2;

            CFArrayRef ---- NSArray

            CFDictionaryRef ---- NSDictionary

            CFNumberRef ---- NSNumber

 

        Core Foundation中手動建立的數據類型,都須要手動釋放

 

        CGPathRef path = CGPathCreateMutable();

        CGPathRetain(path);

 

        CGPathRelease(path);

        CGPathRelease(path);

 

    3.橋接的添加:

        利用 Xcode 提示自動添加! --簡單/方便/快速

/**

 凡是函數名中帶有create\copy\new\retain等字眼, 都應該在不須要使用這個數據的時候進行release

 GCD的數據類型在ARC環境下不須要再作release

 CF(Core Foundation)的數據類型在ARC\MRC環境下都須要再作release

 */

}

/*----------- iOS中多線程實現方案2.NSThread - 1基本使用 -------------*/

重點:1.三種建立線程! 2.經常使用方法!

{

    1.NSThread: 一個 NSThread 就表明一個線程對象!

    // OC語言 / 使用面向對象 / 須要手動管理線程生命週期(建立/銷燬等)

    

    2.三種多線程實現方案:

    

    1> 先建立,後啓動

    // 建立

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

    // 啓動

    [thread start];

    

    2> 建立完自動啓動

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

    

    3> 隱式建立(自動啓動)

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

    

    3.經常使用方法:

     名字/得到主線程/得到當前線程/阻塞線程/退出線程

    // 不經常使用: 棧區大小/優先級

    1> 得到當前線程

    + (NSThread *)currentThread;

    

    2> 得到主線程

    + (NSThread *)mainThread;

    

    3> 睡眠(暫停)線程

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

    + (void)sleepForTimeInterval:(NSTimeInterval)ti;

    

    4> 設置線程的名字

    - (void)setName:(NSString *)n;

    - (NSString *)name;

}

/*------------- iOS中多線程實現方案2.NSThread - 2線程狀態 --------------*/

重點:1. "Crash, P0級別 Bug(面試)!" 2.理解線程狀態!

{

    // 建立線程

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

    // 啓動線程

    [thread start];

 

    線程池:存放線程的池子! 分爲:

 

    可調度線程池: CPU 只會調度可調度線程池中的線程! 下面藍色狀態都位於可調度線程池中! '就緒' ,'運行'!

 

    不可調度線程池: 下面紅色狀態都位於不可調度線程池中! "新建" ,"阻塞" ,"死亡"!

 

    線程狀態:

 

                 start            CPU調度當前線程           運行結束/強制退出(exit)

        "新建" ---------->'就緒' -----------------> '運行' -----------------------> "死亡";

 

                CPU 調度其餘線程           CPU調度當前線程

        '運行' ------------------> '就緒'-----------------> '運行'

 

                調用 sleep/等待互斥鎖            sleep時間到/獲得互斥鎖

        '運行' -----------------------> "阻塞"-----------------------> '就緒';

 

    線程運行結束或者強制退出(exit)就進入 "死亡" 狀態;

    

    "注意:一旦線程中止(死亡),就不能夠再次開啓任務!程序會掛掉: Crash!

    

    面試語句: 平時開發中,要特別關注 Crash! :"PO"級別的 "Bug";

 

}

/*----------- iOS中多線程實現方案2.NSThread - 3資源共享 ---------------*/

重點:1.線程同步技術! 2.理解資源共享

{

    當多條線程訪問同一塊資源的時候,就會出現數據錯亂和數據安全的問題!

    

    1.ATM機取錢; 賣票;

    

    2.解決方案:互斥鎖 @synchronized(鎖對象self){  /*須要鎖住的代碼,越少越好!*/ }   ------- 廁所加鎖!

    

    注意:鎖定一份代碼只用一把鎖,用多把鎖是無效的!

    

    優勢:能有效防止因多線程搶奪資源而引發的數據安全問題!

    缺點:須要消耗大量的CPU資源!

    

    結論:儘可能少加鎖!互斥鎖的使用前提是多條線程搶奪同一塊資源!

    

    3.添加互斥鎖技巧: [[NSUserDefaults standardUserDefaults] synchronize];

    

    4.線程同步技術:  ----- 互斥鎖使用了線程同步技術!

    

    多條線程在同一條線上按順序執行任務!

    

    5.線程安全:保證多條線程進行讀寫操做,都可以獲得正確的結果!

    

    用 '鎖' 來實現線程安全!

}

/*----------------- 原子屬性和非原子屬性 -------------------*/

重點:1.面試問題: 爲何要在主線程更新UI? 2.原子和非原子屬性選擇!

{

    1.原子屬性和非原子屬性:

    

    OC在定義屬性時有 atomic 和 nonatomic 兩種選擇!

    

    atomic(默認屬性): 原子屬性,自動爲setter 方法加鎖!線程安全的,須要消耗大量的 CPU 資源!

    

    nonatomic: 非原子屬性,不會爲 setter 方法加鎖!非線程安全的,適合內存小的移動設備!

    

    咱們在聲明屬性的時候該如何選擇?

    

    面試問題: 爲何要在主線程更新UI?

    

    由於UIKit 框架都不是線程安全的!爲了獲得更好的用戶體驗,UIKit框架犧牲了線程安全;

    

    因此咱們要在主線程更新UI;

    

    2.iOS 開發建議:

    <1> 全部屬性都聲明爲 nonatomic!

    <2> 儘可能避免多線程搶奪同一塊資源!

    <3> 儘可能將加鎖,資源搶奪等業務邏輯交給服務器端處理,減少移動客戶端的壓力!

}

/*--------- iOS中多線程實現方案2.NSThread - 4線程間通訊 ---------------*/

1.下載圖片? 更新 UI?

{

    1.後臺線程(子線程)下載圖片;

    

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

    

    2.主線程更新 UI.

    

    線程間通訊經常使用方法:

    

    // 最後一個參數:是否等待調用方法執行結束!

    <1>[self performSelectorOnMainThread:@selector(setImageWithImage:) withObject:nil waitUntilDone:YES];

    

    <2>[self performSelector:@selector(setImageWithImage:) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];

}

/*------------------------------------------- 知識點補充 --------------------------------------*/

1.項目支持ARC

{

    -fobjc-arc :就可讓舊項目支持arc

    -fno-objc-arc :讓原來支持arc的不使用arc

}

 

/*----------------- GCD使用 1.隊列和任務-------------------*/

重點:1."串行隊列"? "併發隊列"? 2.block?

{

    1.GCD(Grand Central Dispatch) ---- '牛逼的中樞調度器'!

    // C語言框架 / 自動管理線程的生命週期(建立/釋放)

    

    推出GCD的目的:取代NSThread!

    

    爲"多核"的"並行"運算提出的解決方案!

    

    優勢:

    <1> GCD 可以自動利用更多的CPU的核數(雙核/四核)!

    <2> GCD 會自動管理線程的生命週期.

    

    程序員只須要告訴 GCD 想要執行的任務(代碼)!

    

    2.GCD中的兩個核心概念:

    

    "任務":

        想要作的事情/執行什麼操做.

        GCD 中的任務定義在block中.

        void (^myBlock)() = ^{

            // 想要作的事情/任務

        }

    

    "隊列":

        用來'存放'任務!

    

    隊列 != 線程!

    隊列中存放的任務最後都要由線程來執行!

    

    隊列的原則:先進先出,後進後出(FIFO/ First In First Out)!

 

    隊列的類型:

    <1> '串行'隊列:(Serial Dispatch Queue)

    存放按順序執行的任務!(一個任務執行完畢,再執行下一個任務)!

    

    // 建立一個串行隊列

    dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);

    

    <2> '併發'隊列:(Concurrent Dispatch Queue)

    存放想要同時(併發)執行的任務!

    

    // 建立一個併發隊列

    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);

    

    注意兩個很是經常使用的特殊隊列:

    

    <1> 主隊列: // UI 操做放在主隊列中執行!

        跟主線程相關聯的隊列!

        主隊列是 GCD 自帶的一種特殊的串行隊列!

        主隊列中的任務都會在主線程中執行!

    //獲取主隊列

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    

    <2> 全局併發隊列: // 通常狀況下,併發任務均可以放在全局併發隊列中!

    //獲取全局併發隊列

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

}

/*----------------  GCD使用 2.執行任務 ---------------------*/

重點:1."同步"函數!"異步"函數! 2.容易混淆的四個概念: '串行' ,'併發' ,"同步" ,"異步"之間的區別?

{

    問題:串行隊列中的任務一定按順序執行嗎?併發隊列中的任務一定同時執行嗎?

    

    GCD中有兩個用來執行任務的函數:

    

    '同步'執行任務:

    dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

    

    '異步'執行任務:

    dispatch_async(dispatch_queue_t queue, <#^(void)block#>)

 

    // <#dispatch_queue_t queue#> :隊列

    // <#^(void)block#>:任務

    

    "同步"和"異步"的區別:

    "同步": 只能在'當前'線程中執行任務,不具有開啓新線程的能力.

    "異步": 能夠在'新'的線程中執行任務,具有開啓新線程的能力.

    

    GCD 使用有兩個步驟:

    <1> 將任務添加到隊列中;

    <2> 選擇同步仍是異步的方式執行任務.

    

    注意:四個容易混淆的術語:

    '串行' ,'併發' ,"同步" ,"異步".

}

/*-----------------  GCD使用 3.各類隊列的執行效果 ------------------*/

重點:1.掌握兩個經常使用的組合!

{

    常見的組合(掌握)

    1> dispatch_async + 全局併發隊列 (能夠開啓多條線程)

    2> dispatch_async + 本身建立的串行隊列 (開啓一條線程)

    

    只有'異步'執行"併發"隊列,才能夠開啓多條線程.

    

    注意:

    在主線程中同步執行主隊列中的任務,會形成'主線程'和'主隊列'相互等待,卡住主線程!

}

/*-----------------  GCD使用 4.線程間通訊 --------------------*/

重點:1.從子線程回到主線程(經典用法)! 2.兩個注意點.

{

    1.經典用法(子線程下載(耗時操做),主線程刷新UI):

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        

                       // 執行耗時的異步操做...

        

                       dispatch_async(dispatch_get_main_queue(), ^{

                           

                           // 回到主線程,執行UI刷新操做

                           

                       });

                   });

 

    2.注意:

    <1> 須要設置按鈕的image,建議先把按鈕類型改成custom,才能保證設置成功

    <2> 屬性名不能以new開頭

}

/*---------------------  GCD使用 5.延時執行  --------------------------*/

重點:1.iOS常見的兩種延時執行方式

{

    iOS中的延時執行方式:

    // 定製好延時任務後,不會阻塞當前線程.

    

    <1> 調用 NSObject 方法:

    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    // 2秒後再調用self的run方法

    

    <2> GCD 函數實現延時執行:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 2秒後執行這裏的代碼... 在哪一個線程執行,跟隊列類型有關

    });

    

    注意:

    不要使用sleep,會阻塞當前線程.

}

/*------------------  GCD使用 6.隊列組   ----------------*/

重點:1.瞭解隊列組的使用方法.

{

    項目需求:

    首先:分別異步執行兩個耗時操做;

    其次:等兩次耗時操做都執行完畢後,再回到主線程執行操做.

    

    使用隊列組(dispatch_group_t)快速,高效的實現上述需求.

    

    dispatch_group_t group = dispatch_group_create(); // 隊列組

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 全局併發隊列

    

    dispatch_group_async(group, queue, ^{         // 異步執行操做1

        // longTime1

    });

    

    dispatch_group_async(group, queue, ^{         // 異步執行操做2

        // longTime2

    });

    

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{     // 在主線程刷新數據

        // reload Data

    });

}

 

/*------------------------  GCD使用 7.一次性代碼 ------------------------*/

重點:1.掌握一次性代碼的實現.

{

    一次性代碼:

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        // 只執行一次的代碼(這裏面默認是線程安全的).

    });

}

/*-----------  補充: 單例設計模式 ------------*/

重點:1.掌握單例!

{

    1.單例簡介:

    

    做用:

        保證程序在運行過程當中,一個類只有一個實例對象.這個實例對象容易被外界訪問!

        控制實例對象個數(只有一個),節約系統資源.

    

    使用場合:

        在整個應用程序中,共享一份資源(這份資源只須要建立初始化一次).

    

    舉例:

        打印機/視圖窗口/一些網絡工具類等等

    

    // 懶漢式: 用到的時候再加載.

    // 餓漢式: 只要程序運行就加載. // 不須要掌握,也不要這麼寫!

    // 掌握懶漢式.

    

    2.單例實現:(兩種方式:互斥鎖(@synchronized(self))和一次性代碼(dispatch_once));

    

    2.1互斥鎖 @synchronized(self):

    

    <1>.在 .m 文件中保留一個全局的 static 的實例.

    

        static id _instance;

    

    <2>.重寫若干方法(allocWithZone:和 copyWithZone:)並提供一個類方法讓外界訪問惟一的實例.

    

    //(1)重寫 allocWithZone:方法,在這裏建立惟一的實例(注意線程安全). //alloc 內部都會調用這個方法.

        +(instancetype)allocWithZone:(struct _NSZone *)zone {

            if (_instance == nil) { // 防止頻繁加鎖

                @synchronized(self) {

                    if (_instance == nil) { // 防止建立屢次

                        _instance = [super allocWithZone:zone];

                    }

                }

            }

            return _instance;

        }

    

    //(2)重寫 copyWithZone:方法.

        +(id)copyWithZone:(struct _NSZone *)zone

        {

            return _instance;

        }

    //(3)提供1個類方法讓外界訪問惟一的實例

        +(instancetype)shareSingleton

        {

            if (!_instance) { // 防止頻繁加鎖

                @synchronized(self){

                    if (!_instance) { // 防止建立屢次

                        _instance = [[self alloc] init];

                    }

                }

            }

            return _instance;

        }

    

    2.2 一次性代碼(dispatch_once):

    <1>.在 .m 文件中保留一個全局的 static 的實例.

    

    static id _instance;

    

    <2>.重寫若干方法(allocWithZone:和 copyWithZone:)並提供一個類方法讓外界訪問惟一的實例.

    

    //(1)重寫 allocWithZone:方法,在這裏建立惟一的實例(注意線程安全).

    + (id)allocWithZone:(struct _NSZone *)zone

    {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            _instace = [super allocWithZone:zone];

        });

        return _instace;

    }

    

    //(2)重寫 copyWithZone:方法.

    +(id)copyWithZone:(struct _NSZone *)zone

    {

        return _instance;

    }

    //(3)提供1個類方法讓外界訪問惟一的實例

    + (instancetype)shareSingleton

    {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            _instace = [[self alloc] init];

        });

        return _instace;

    }

 

    注意:在 ARC 和 MRC 中單例的實現方式略有不一樣. MRC 下單例的實現比 ARC 多了幾個內存管理的方法:

    

    MRC 中增長以下方法的實現:

    - (instancetype)retain { return self; }

    - (NSUInteger)retainCount { return 1; }

    - (oneway void)release {}

    - (instancetype)autorelease { return self; }

    

    3.判斷當前環境(ARC/MRC)

    

#if __has_feature(objc_arc)

    // ARC

#else

    // MRC

#endif

    

    4.注意兩個方法:

    // 面試問題:兩個方法的區別?

    <1> +(void)load;

    // 當類加載到OC運行時環境(內存)中的時候,就會調用一次(一個類只會加載一次).

    // 程序一啓動就會調用.

    // 程序運行過程當中,只會調用1次.

    <2> +(void)initialize;

    // 當第一次使用這個類的時候(好比調用了類的某個方法)纔會調用.

    // 並不是程序一啓動就會調用.

}

 

/*-----------  NSOperation使用 1.簡介  ---------------*/

重點:理解操做 NSOperation 和操做隊列 NSOperationQueue!

{

    1.NSOperation(操做)簡介:

    

    NSOperation: // 本質是對 GCD 的封裝, OC 語言.

    

    NSOperation 和 GCD 的比較:

    

    GCD使用場合:

    一些簡單的需求,簡單的多線程操做. //簡單高效

    

    NSOperation使用場合:

    各個操做之間有依賴關係,操做須要取消/暫停;須要限制同時執行的線程數量,讓線程在某時刻中止/繼續等.

    

    配合使用 NSOperation和 NSOperationQueue 也能夠實現多線程.

 

    2.NSOperation使用:

    

    NSOperation: 抽象類,不能直接使用,須要使用其子類.

    

    抽象類:定義子類共有的屬性和方法.// CAAnimation/CAPropertyAnimation...

    

    兩個經常使用子類: NSInvocationOperation(調用) 和 NSBlockOperation(塊);

    

                二者沒有本質區別,後者使用 Block 的形式組織代碼,使用相對方便.

    

    自定義子類繼承自 NSOperation,實現內部相應的方法. // 高級用法

}

/*-----------------  NSOperation使用 2.NSOperation ----------------------*/

重點:1.NSBlockOperation, NSInvocationOperation的簡單使用.

{

    1. 建立 NSInvocationOperation 對象

    

    // 建立 NSInvocationOperation

    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(longTimeOperation:) object:@"op1"];

    

    //默認狀況下,調用 start 方法以後,不會開啓新線程,只會在當前線程執行操做.

    [op1 start];

    

    注意:只有將 NSOperation 放到一個 NSOperationQueue 中,纔會異步執行操做.

    

    2. 建立 NSBlockOperation 對象

    

    // 建立 NSBlockOperation

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"下載圖片1---%@",[NSThread currentThread]);

    }];

    // 添加更多操做

    [op2 addExecutionBlock:^{

        NSLog(@"下載圖片2---%@",[NSThread currentThread]);

    }];

    [op2 addExecutionBlock:^{

        NSLog(@"下載圖片3---%@",[NSThread currentThread]);

    }];

    

    // 只要 NSBlockOperation 中封裝的操做數 > 1, 調用start方法以後就會開啓多條線程併發執行

    // 若是 NSBlockOperation 中封裝的操做數 == 1,調用 start 方法以後,不會開啓新線程,只會在當前線程執行操做

    [op2 start];

    

    注意: 只要 NSBlockOperation 中封裝的操做數 > 1,就會異步執行這些操做.(將操做添加到 NSOperationQueue中或者直接調用 start方法都會開啓多條線程異步執行).

}

/*----------------- NSOperation使用 3.NSOperationQueue ------------------*/

重點:將操做添加到隊列中;

{

    NSOperation 能夠調用 start 方法來執行任務,但默認是同步執行的.

    將 NSOperation 添加到 NSOperationQueue(操做隊列) 中,系統會自動異步執行NSOperationQueue中的操做.

    

    1.NSOperationQueue(操做隊列):

    

    <1> 主隊列

    [NSOperationQueue mainQueue] //獲取主隊列

    添加到"主隊列"中的操做,都會放在主線程執行!

    

    <2>非主隊列

    [[NSOperationQueue alloc] init]; //建立非主隊列

    添加到"非主隊列"中得操做,都會放在子線程中執行.

    

    2.使用: 添加操做到操做隊列中.

    

    // 建立 NSInvocationOperation 操做

    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(longTimeOperation:) object:@"op1"];

    

    // 建立 NSBlockOperation 操做

    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"下載圖片1---%@",[NSThread currentThread]);

    }];

 

    // 1.建立一個 NSOperationQueue

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

    

    // 2.將操做添加到隊列中.

    [queue addOperation:op1];

    [queue addOperation:op2];

    

    注意:另一種添加操做到隊列中的方法: block

    [queue addOperationWithBlock:^{

        NSLog(@"下載圖片5---%@",[NSThread currentThread]);

    }];

    

    推薦使用: block // 簡單. 本身哪一個使用熟練就用哪一個.

    

    注意:隊列中任務的執行是無序的.

    

    問題:是否可讓隊列中的操做有序執行?

}

/*----------------- NSOperation使用 4.常見用法1 ----------------------*/

重點:1.設置操做依賴. 2.設置最大併發數.

{

    回答上問題: 能,設置操做依賴.

    

    1.NSOperation設置操做依賴: // 執行順序: op1,op2,op3;

    

    // 操做op3依賴於操做op2;

    [op3 addDependency:op2];

    // 操做op2依賴於操做op1;

    [op2 addDependency:op1];

    

    注意:不能相互依賴.

    

    2.NSOperationQueue設置最大併發數.

    

    併發數:同時開啓的線程數.

    

    // 建立操做隊列

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

    // 設置操做隊列的最大併發數

    queue.maxConcurrentOperationCount = 3;

    [queue setMaxConcurrentOperationCount:3];

}

/*-------------------- NSOperation使用 5.常見用法2 -----------------------*/

重點:1.隊列的取消/暫停/恢復  2.線程間通訊. 注意問題:爲毛要取消恢復隊列? 在何時用?

{

    1.NSOperationQueue 的取消/暫停/恢復

    

    // 取消操做 op1. 取消單個操做.

    [op1 cancel];

    // 取消全部操做,不會再次恢復

    [queue cancelAllOperations];

    // 暫停全部操做;注意,已經開始的操做不會暫停.

    [queue setSuspended:YES];

    // 從新開始全部操做

    [queue setSuspended:NO];

    

    問:爲毛要取消恢復隊列? 在何時用?

    

    答:1.爲了內存管理,處理內存警告; 2.爲了用戶體驗,保證滾動流暢.

    

    // 接收到內存警告的時候果斷取消隊列中的全部操做

    - (void)didReceiveMemoryWarning

    {

        [super didReceiveMemoryWarning];

        

        //    [queue cancelAllOperations]; // 取消隊列中的全部任務(不可恢復)

    }

    // 開始滾動的時候暫停隊列中的任務.

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

    {

        //    [queue setSuspended:YES]; // 暫停隊列中的全部任務

    }

    // 滾動結束的時候恢復隊列中的任務.

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

    {

        //    [queue setSuspended:NO]; // 恢復隊列中的全部任務

    }

 

    2.線程間通訊  // 子線程下載圖片,主線程設置圖片.

    

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

    

    [queue addOperationWithBlock:^{

        // 1.異步下載圖片

        NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];

        NSData *data = [NSData dataWithContentsOfURL:url];

        UIImage *image = [UIImage imageWithData:data];

        

        // 2.回到主線程,顯示圖片

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            self.imageView.image = image;

        }];

    }];

    

}

/*-------------  NSOperation使用 6.AppleWatch 展現   ----------------*/

{

    // RectiveCocoa

    http://www.devtang.com/blog/2014/02/11/reactivecocoa-introduction/

    

    1.九宮格的繪製;

    2.圖片的異步下載;

    3.主線程顯示圖片;

    4.設置操做依賴(先顯示第一張);

    5.完善功能.

}

 

 

 

/*--------------  NSOperation綜合案例: 1.項目簡介/UI 搭建  -------------*/

重點: 1.搭建 UI 界面; 2.構建數據模型; 3.緩存開發中須要用到的數據模型; 4.注意在懶加載方法中,不要出現點語法.

{

    1. 搭建 UI 界面;

    // 導航控制器 + 表格視圖控制器(根控制器)

    2. 構建數據模型;

    // 注意提供一個字典轉模型的方法.

    // KVC(Key - Value - Code)鍵值編碼使用注意;

    // 注意與 KVO(Key - Value - Observe)鍵值監聽的區別;                 存放

    3. 將數據模型緩存到可變數組中.開發的數據直接來源於這個可變數組.   "apps -------> 數據模型"

    // 懶加載存放數據模型的數組.操做步驟:                                  存放

    (1)將 apps.plist 文件轉換爲數組(數組中存放的是數據字典):       array ---------> 字典模型

    {

        // <1> 得到 apps.plist 文件 的路徑(知道了文件路徑,就可以找到文件):

        {

            // 獲取 apps.plist 文件名的全路徑

            NSString *path =[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];

        }

        // <2> 根據 apps.plist文件 轉換爲數組

        {

            // 根據 apps.plist文件 轉換爲數組; array 中存儲的是數據字典

            NSArray *array = [NSArray arrayWithContentsOfFile:path];

        }

    }

    (2)取出數據字典,將數據字典轉換爲模型,而且將模型存放在apps數組中:

    {

        // 取出數據字典,將數據字典轉換爲模型

        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

            // array: 存放字典模型;

            // obj: 數組中存放的字典;

            // idx: obj在數組中的位置

            

            // <1>取出數組中的數據字典

            NSDictionary *dict = obj;

            // <2>將數據字典轉換爲數據模型

            ITApp *app = [ITApp ITAppWithDictionary:dict];

            

            // 將數據模型添加到臨時可變數組中.

            [appsArray addObject:app];

        }];

        

        // 將數據模型存放在 apps 中

        _apps = appsArray;

    }

    // 注意,取出數組中的字典的兩種方式:<1> for 循環; <2>Block.推薦使用第二種.效率更高.

#pragma 懶加載

    // 存放數據模型的數組

    -(NSMutableArray *)apps

    {

            if (!_apps) {

                _apps = [NSMutableArray array];

                

                // 獲取 apps.plist 文件名的全路徑

                NSString *path =[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil];

                

                // 根據 apps.plist文件 轉換爲數組; array 中存儲的是數據字典

                NSArray *array = [NSArray arrayWithContentsOfFile:path];

                // NSLog(@"%@",array);

                

                // 定義一個臨時的可變數組來存放數據模型;

                NSMutableArray *appsArray = [NSMutableArray array];

                

                // 拿出數據字典,將數據字典轉換爲模型,而且將模型存放在apps數組中:

                

                //兩種方法:

                

                //<1> for 循環

                //        for (int i = 0 ; i < array.count; i ++) {

                //

                //           // 取出 array 中存放的數據字典

                //            NSDictionary *dict = array[i];

                //

                //            // 將數據字典轉換爲數據模型

                //            ITApp *app = [ITApp ITAppWithDictionary:dict];

                //

                //            // 將數據模型添加到臨時可變數組中.

                //            [appsArray addObject:app];

                //        };

                //

                

                // <2> Block

                [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

                    // obj:數組中存放的字典; idx: obj在數組中的位置

                    NSDictionary *dict = obj;

                    // 將數據字典轉換爲數據模型

                    ITApp *app = [ITApp ITAppWithDictionary:dict];

                    

                    // 將數據模型添加到臨時可變數組中.

                    [appsArray addObject:app];

                }];

                

                // 將數據模型存放在 apps 中

                _apps = appsArray;

            }

            return _apps;

        }

    4. 注意點: UITableView的數據源方法:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    {

        必須返回一個 :UITableViewCell;

       // 調試的時候,用下面這種方式寫,不會報錯.

        return [UITableViewCell new];

    }

}

 

/*--------------  NSOperation綜合案例: 2.下載圖片/技術選擇  ----------------*/

重點: 1.分析項目需求; 2.處理內存警告,優化用戶體驗; 3.技術選擇,技術點實施,代碼編寫. 4.Bug?

{

    項目需求:

    <1> 下載圖片;

    分析:

        1>.子線程下載圖片,主線程顯示圖片.

        2>.開啓子線程有三種技術方案可供選擇: (1)NSThread ,(2)GCD ,(3)NSOperation 配合 NSOperationQueue使用.

    

    <2> 內存警告處理;

    分析:

        接收到內存警告的時候,中止一切下載操做,防止閃退.

    

    <3> 用戶體驗;

    分析:

        在與用戶作 UI 交互的時候,最好暫停圖片的下載;用戶滾動結束以後,再繼續下載圖片.

    

    1. 技術選擇 : (3)NSOperation 配合 NSOperationQueue使用.

    2. 技術點實施:

    // <1>用戶開始滾動的時候,暫停下載操做;中止滾動以後,恢復下載操做.

    {

#pragma UIScrollViewDelegate

        // 開始滾動的時候調用

        - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView

        {

            NSLog(@"暫停下載---");

            // 暫停全部下載操做

            [self.queue setSuspended:YES];

        }

        // 滾動結束的時候調用

        - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

        {

            NSLog(@"恢復下載---");

            // 恢復全部下載操做

            [self.queue setSuspended:NO];

        }

 

    }

    // <2>接收到內存警告的時候,取消一切操做.

    {

        // 接收到內存警告的時候調用

        -(void)didReceiveMemoryWarning

        {

            [super didReceiveMemoryWarning];

            // 取消一切下載操做

            [self.queue cancelAllOperations];

            

        }

    }

    // <3>將下載圖片的操做封裝在 NSBlockOperation.最後將操做放在併發隊列中.自動執行!

    {

        __weak typeof(self) wself = self;

        // 定義下載操做

        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

            // 下載網絡圖片

            UIImage *webImage = [wself downloadWebImage:app.icon];

            //回到主線程顯示圖片

            [[NSOperationQueue mainQueue] addOperationWithBlock:^{

                // 顯示圖片

                cell.imageView.image = webImage;

            }];

        }];

        

        // 將下載操做放在併發隊列中.自動開啓下載.

        [self.queue addOperation:op];

 

    }

    

    3.運行程序以後,發現 3 個Bug:

    <1> 程序運行以後,圖片不會直接顯示出來.須要刷新以後才能顯示(滾動/點擊都會重繪 UI).

    <2> 圖片錯位問題.

    <3> 用戶體驗方面的Bug:只要滾動,圖片就會重複下載.即便已經下載好的圖片,也會從新下載.(耗費流量,體驗巨差,巨耗電).

}

 

/*--------------  NSOperation綜合案例: 3.Bug 解決  -----------------*/

重點: 1.分析Bug產生的緣由並解決Bug. 2.知識點回顧:如何防止一個url對應的圖片重複下載?

{

    Bug 產生的緣由分析:

    

    <1> "程序運行以後,圖片不會直接顯示出來.須要刷新以後才能顯示(滾動/點擊都會重繪 UI)."

    

        Bug產生緣由: UITableViewCell 中剛開始沒有設置顯示圖片的 Frame,也就是說沒有 Frame.圖片下載完後,點擊/刷新以後,就會從新繪製 UITableViewCell,這樣就會顯示圖片了.

    

        解決 Bug :下載以前最好先把圖片的 Frame 繪製出來.好比,能夠添加一張佔位圖片.

    {

        // 設置佔位圖片.

        cell.imageView.image = [UIImage imageNamed:@"placeholder"];

    }

    

    <2> "圖片錯位問題."

    

        Bug產生緣由: UITableViewCell 的重用以及網絡下載延時產生的.

    

        解決 Bug :讓數據控制視圖(視圖根據數據來顯示),設置一個圖片緩存,cell 中的圖片來源於這個圖片緩存.

    {

        <1>.設置圖片緩存.

        定義一個字典作爲圖片緩存,保存下載好的圖片(以圖片的 url 做爲 key 值;如下載好的圖片爲 Value).

        // 能夠選擇 NSCache 代替字典做爲緩存機制.

        // NSCache在內存緊張的時候,會自動釋放一些資源(自動銷燬圖片,咱們沒法控制).

        // 若是使用字典,在接收到內存警告以後,須要手動釋放資源.

        

        <2>.從緩存中取出 cell 對應的圖片.

        cell 設置圖片:根據 cell 的 app.icon(url)從字典中取出對應的圖片.

        

        <3>.圖片下載完畢以後刷新所在行的數據.

        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

        

        注意: 不必刷新整個表格視圖,刷新所在行的數據就能夠了.

    }

    

    <3> "用戶體驗方面的Bug:只要滾動,圖片就會重複下載.即便已經下載好的圖片,也會從新下載.(耗費流量,體驗巨差,巨耗電)."

    

        Bug產生緣由: 只要上下滾動,就會調用 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 這個方法.這個方法頻繁調用致使下載操做一直建立.

    

        解決 Bug :每次建立下載操做以前,最好先檢查一下載操做是否存在,對於已經存在的操做,不要重複下載.

    {

        <1>.設置操做緩存.

        定義一個字典做爲下載操做緩存,保存已經建立好的下載操做(一樣,以圖片的 url 爲 key值;以操做Operation爲 Value).

        

        <2>.從緩存中取出操做比較.

        每次從新建立下載操做以前,首先從下載操做緩存之中查看操做是否已經存在.若是存在,就不要再次建立;若是不存在,建立新的下載操做.

        

        <3>.防止操做緩存愈來愈大

        圖片下載成功以後,就下載操做就沒有存在的意義了,應該及時清除緩存,防止下載操做緩存愈來愈大.

        

    }

 

    補充知識點:NSCache

    {

        // 能夠選擇 NSCache 代替字典做爲緩存機制.

        NSCache類結合了各類自動刪除策略,以確保不會佔用過多的系統內存.若是其它應用須要內存時,系統自動執行這些策略.

        NSCache是線程安全的,咱們能夠在不一樣的線程中添加/刪除和查詢緩存中的對象,而不須要鎖定緩存區域.

    }

    

    2. 問題:如何防止一個url對應的圖片重複下載?

    

        答:cell下載圖片思路 - 無沙盒緩存;

}

/*-------------  NSOperation綜合案例: 4.完善項目-添加沙盒緩存  ----------------*/

重點: 1.處理內存警告.  2.添加沙盒緩存.

{

    1.因爲添加了內存緩存機制(圖片緩存和操做緩存),在接收到內存警告的時候,最好釋放內存緩存.

    

    將圖片緩存在沙盒中,能夠優化用戶體驗.之後每次展現圖片,對於已經下載過的圖片,就不須要從新下載.

    

    2.添加沙盒緩存.

    <1> 認識沙盒:

        默認狀況下,每一個沙盒含有3個文件夾:Documents, Library 和 tmp.

        Documents:蘋果建議將程序中創建的或在程序中瀏覽到的文件數據保存在該目錄下,iTunes備份和恢復的時候會包括此目錄.

        Library:存儲程序的默認設置或其它狀態信息;

        // Library/Caches:存放緩存文件,iTunes不會備份此目錄,此目錄下文件不會在應用退出刪除

        tmp:提供一個即時建立臨時文件的地方.

    <2> 將圖片寫入沙盒:將文件存儲在 Library/Caches路徑下

        1>.獲取 ~/Caches路徑.

    {

        // 獲得 Caches 路徑

        NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;

    }

        2>.拼接文件路徑.

    {

        // 拼接文件路徑( path + app.icon.lastPathComponent ),以url 中圖片的名做爲名字.

        NSString *file = [path stringByAppendingPathComponent:app.icon.lastPathComponent];

    }

        3>.將 image 轉換爲 二進制數據. //沙盒中不能直接存取圖片數據.

    {

        // 將 UIImage 圖片轉換爲二進制數據

        NSData *data = UIImagePNGRepresentation(webImage);

    }

        4>.將圖片的二進制數據存入沙盒.

    {

        // 將圖片的二進制數據存入沙盒,路徑爲:file

        [data writeToFile:file atomically:YES];

    }

    <3> 從沙盒中獲取圖片:

        1>.獲取 ~/Caches路徑.

        2>.拼接完整的圖片文件路徑.

        3>.根據完整的圖片文件路徑獲取圖片.

    {

        UIImage *image = [UIImage imageWithContentsOfFile:fileName];

    }

}

/*------------- NSOperation綜合案例: 5.SDWebImage使用 ------------------*/

重點: 1.瞭解 SDWebImage.

{

    1.SDWebImage:

    

    SDWebImage是一個開源的第三方庫,它提供了UIImageView的一個分類,以支持從遠程服務器下載並緩存圖片的功能.

    

    <1> SDWebImageManager

    

    在實際的運用中,咱們並不直接使用SDWebImageDownloader類及SDImageCache類來執行圖片的下載及緩存.

    

    爲了方便用戶的使用,SDWebImage提供了SDWebImageManager對象來管理圖片的下載與緩存.

    

    咱們常常用到的諸如UIImageView+WebCache等控件的分類都是基於SDWebImageManager對象的.

    

    該對象將一個下載器和一個圖片緩存綁定在一塊兒,並對外提供兩個只讀屬性來獲取它們.

    

    <2> UIImageView+WebCache

    

    咱們在使用SDWebImage的時候,使用得最多的是UIImageView+WebCache中的針對UIImageView的擴展方法,這些擴展方法將UIImageView與WebCache集成在一塊兒,來讓UIImageView對象擁有異步下載和緩存遠程圖片的能力.

    

    其中最核心的方法是 -sd_setImageWithURL:placeholderImage:options:progress:completed:,其使用SDWebImageManager單例對象下載並緩存圖片,完成後將圖片賦值給UIImageView對象的image屬性,以使圖片顯示出來.

    

    2.面試題

    

    1> SDWebImage的默認緩存是多長時間?

    * 1個星期

    

    2> SDWebImage的默認最大併發數是多少?

    * 6

    

    3> SDWebImage底層是怎麼實現的?

    * cell下載圖片思路 – 有沙盒緩存

    

    3.SDWebImage經常使用方法:

    

    1> 經常使用方法

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock;

    - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;

    

    2> SDWebImageOptions

    * SDWebImageRetryFailed : 下載失敗後,會自動從新下載

    * SDWebImageLowPriority : 當正在進行UI交互時,自動暫停內部的一些下載操做

    * SDWebImageRetryFailed | SDWebImageLowPriority : 擁有上面2個功能

    

    3> 內存處理:當app接收到內存警告時

    /**

     *  當app接收到內存警告

     */

    - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application

    {

        SDWebImageManager *mgr = [SDWebImageManager sharedManager];

        

        // 1.取消正在下載的操做

        [mgr cancelAll];

        

        // 2.清除內存緩存

        [mgr.imageCache clearMemory];

    }

 

}

 

/*-------------- NSOperation綜合案例: 6.自定義 NSOperation --------------*/

{

    這一塊知識點比較複雜,難以理解.只須要知道如下幾點:

    

    自定義 NSOperation的步驟:

    

    * 重寫 -(void)main 方法,在裏面實現想執行的操做.

    

    重寫 -(void)main 方法注意點:

    

    1> 本身建立自動釋放池(若是異步操做,沒法訪問主線程的自動釋放池).

    

    2> 常常經過 -(BOOL)isCancelled 方法檢測操做是否取消,對取消作出響應.

    

    // 若是真的想搞這塊,還須要掌握'不一樣對象間'通訊的的方法:(通知/代理/Block 等).

    // 詳細見代碼

 

}

相關文章
相關標籤/搜索