隨意總結的面試題

熟悉掌握 OC、瞭解 swift和小程序開發
OC
objective-c是一門面嚮對象語言,在C語言的繼承上面增加面向對象的思想,從一個class開始
特徵:
class
繼承:繼承是指一個新類擁有父類的全部屬性和方法
封裝
多態:不同對象以自己的方式響應相同的消息的能力叫多態。在objective-c中是通過selected的選擇器實現的,在objective-c中,selected有兩個意思,當用在給對象的源碼消息時,用來指方法的名字。它也只那個在源碼編譯後代替方法名的唯一標識符。編譯後的選擇器的類型是SEL有同樣名字的方法,也有同樣的選擇器
Swift:Swift是面向協議的編程,從一個protocol開始
優勢:
1、Swift代碼更好寫
從objective-c遷移過來的API寫法更簡潔,更易於閱讀、維護。類型推斷使代碼更簡潔,去掉了引用頭文件並提供名稱控件。內存自動管理,不需要鍵入分號
2、Swift更安全
- OC的動態性一直是OC的一大優勢,但是也伴隨着安全隱患。語言過於曖昧導致很多不可預測的問題。比如向nil發送消息並不會使程序崩潰,這看起來很安全,但不代表沒有bug,反而這成爲了問題的根源。
- Swift也有nil,但是跟OC不同的概念。在OC中,nil是指向一個不存在對象的指針。在Swift中,nil並不是一個指針,而是代表一個特定類型值不存在。不光是對象,基本類型、結構體、枚舉都可以被設置爲nil。
- Swift是個類型安全的語言,類型安全的語言需要開發者清楚每個變量的類型。如果你需要一個string類型的參數,就絕不能錯誤的講int傳遞給它。正因爲Swift是類型安全的,它在編譯期對代碼進行檢測並指出錯誤,可以早發現代碼中的類型錯誤。
3、Swift更快
- Swift的誕生是爲了替換基於C語言(C,C++,objective-c)。因此,在大多數場景下,Swift的性能必須與那些語言相當。性能也必須是可預測和穩定的,而不能只是短時間的爆發,之後需要冷卻時間的。在擁有現在語言性特性的語言中,速度也快。
- 在OC的速度對比中,蘋果官方生成Swift的速度是OC的1.4倍。由於Swift的類型安全,使得它可以使用類似C++虛函數表的方式解決OC運行時發消息慢的問題,很多函數調用都從層層消息變成了取址調用,在加上更強大的編譯優化。
4、Swift開源
- OC開源了runtime,CF。但是Swift是完全開源的,並且還能提代碼。不光是Swift本身,Swift-class,LLVM、LLDB等都是開源的
5、Swift跨平臺
- Swift除了可以基於蘋果系統和Linux開發還可以進行服務端開發
- Perfect是一組完整,強大的工具箱、軟件框架體系和Web應用服務器,可以在Linux、iOS和macOS(OS X)上使用

2、熟悉掌握 KVC,KVO,MVC、代理協議、單例設計模式
KVC、KVO
KVC:鍵值編碼,iOS開發中可以允許開發中通過key名直接訪問對象的屬性,或者對象的屬性賦值。
KVC的定義都是對NSObject的擴展來實現的,objective-c中有個顯示NSKeyValueCoding類別名,所以對所有繼承NSObject的類型,都能用KVC(一些純Swift結構體是不支持KVC的,因爲沒有繼承NSObject)
取值兩種方式:
1. valueForKey ,valueForKeyPath
2. setValue:forkey ,setValue:forKeyPath
NSKeyValueCoding類別的其他方法:
3. accessInstanceVarialbesDirently:默認返回YES,表示如果沒有找到Set方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,設置成NO就不這樣搜索
4. validateValue:forkey:error:返回布爾值,KVC提供屬性值正確性驗證API,他可以用來檢查set的值是否正確、爲不正確的值做一個替換值或者拒絕這設置新值並返回錯誤原因
5. mutableArrayValueForKey:這是集合操作的API
6. valueForUnderfinedKey:如果Key不存在,且沒有KVC無法搜索到任何和Key有關的字段或者屬性,則會調用這個方法,默認是拋出異常
7. setValue:forUnderfineKey:和上個方法一樣,但是這個方法是設置的
8. setNilValueForkey:如果你在SetValue方法是面對Value傳nil,則會調用這個方法
9. dictionaryWithValuesForKeys:輸入一組key,返回該組key對應的value,在轉字典返回,用於將model轉到字典

KVC相關技術概念以及使用:
KVC取值:

  • KVC要設值,那麼就要對象中對應的key,KVC內部是順序:當調用setValue:forKey,程序優先調用set<key(key是成員變量)>屬性方法,代碼通過setter方完成設置;如果沒有找到set方法,KVC機制會檢查accessInstanceVarialbesDirently有沒有返回YES,該方法默認是返回YES,如果重寫該方法讓它返回NO的話,那麼在這一步KVC執行setValue:forUndefinedKey:方法,不過一般不會這麼做。所以KVC機制會搜索該類裏面有沒有這個成員變量,無論該變量是在類接口處定義,還是在類實現處定義,也無論用了什麼樣的訪問修飾,只在存在以命名的成員變量,KVC都可以對該成員變量賦值。
  • 如果該類既沒有set方法,也沒有_成員變量,KVC機制會搜索_is的成員變量;如果也沒有這兩個方法,KVC機制在會繼續搜索和is的成員變量。再給它們賦值。
  • 如果上面列出的方法或者成員變量都不存在,系統將會執行該對象的setValue:forUndefinedKey:方法,默認拋出異常
  • 簡單說,如果沒有找到Set方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,並進行賦值。如果開發者想讓這個類禁用KVC,accessInstanceVarialbesDirently方法重寫設置成NO即可,這樣的話,如果KVC沒有找到set:屬性時,會直接用setValue:forUndefinedKey:方法

KVC取值
- 當調用valueForKey,首先按get,,is的順序方法查找getter方法,找到的話會直接調用。如果是BOOL或者Int等值類型, 會將其包裝成一個NSNumber對象。

  • 如果上面的getter沒有找到,KVC則會查找countOf,objectInAtIndex或AtIndexes格式的方法。如果countOf方法和另外兩個方法中的一個被找到,那麼就會返回一個可以響應NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子類),調用這個代理集合的方法,或者說給這個代理集合發送屬於NSArray的方法,就會以countOf,objectInAtIndex或AtIndexes這幾個方法組合的形式調用。還有一個可選的get:range:方法。所以你想重新定義KVC的一些功能,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標準命名方法,包括方法簽名。
  • 如果上面的方法沒有找到,那麼會同時查找countOf,enumeratorOf,memberOf格式的方法。如果這三個方法都找到,那麼就返回一個可以響應NSSet所的方法的代理集合,和上面一樣,給這個代理集合發NSSet的消息,就會以countOf,enumeratorOf,memberOf組合的形式調用。
  • 如果還沒有找到,再檢查類方法accessInstanceVariablesDirectly,如果返回YES(默認行爲),那麼和先前的設值一樣,會按_,_is,,is的順序搜索成員變量名,這裏不推薦這麼做,因爲這樣直接訪問實例變量破壞了封裝性,使代碼更脆弱。如果重寫了類方法accessInstanceVariablesDirectly返回NO的話,那麼會直接調用valueForUndefinedKey:方法,默認是拋出異常。

KVC使用keyPath
在開發過程中,一個類的成員變量有可能是自定義類或其他的複雜數據類型,你可以先用KVC獲取該屬性,然後再次用KVC來獲取這個自定義類的屬性,但這樣是比較繁瑣的,對此,KVC提供了一個解決方案,那就是鍵路徑keyPath。

KVC處理異常
KVC中最常見的異常就是不小心使用了錯誤的key,或者在設值中不小心傳遞了nil的值,KVC中有專門的方法來處理這些異常。
KVC處理nil異常,通常情況下,KVC不允許你要在調用setValue:屬性值forKey:(或者keyPath)時對非對象傳遞一個nil的值。很簡單,因爲值類型是不能爲nil的。如果你不小心傳了,KVC會調用setNilValueForKey:方法。這個方法默認是拋出異常,所以一般而言最好還是重寫這個方法。
KVC處理UndefinedKey異常,通常情況下,KVC不允許你要在調用setValue:forKey:(或者keyPath)時對不存在的key進行操作。不然,會報錯forUndefinedKey發生崩潰,重寫forUndefinedKey方法避免崩潰。

KVC處理數值和結構體類型屬性
不是每一個方法都返回對象,但是valueForKey:總是返回一個id對象,如果原本的變量類型是值類型或者結構體,返回值會封裝成NSNumber或者NSValue對象。這兩個類會處理從數字,布爾值到指針和結構體任何類型。然後開以者需要手動轉換成原來的類型。儘管valueForKey:會自動將值類型封裝成對象,但是setValue:forKey:卻不行。你必須手動將值類型轉換成NSNumber或者NSValue類型,才能傳遞過去。因爲傳遞進去和取出來的都是id類型,所以需要開發者自己擔保類型的正確性,運行時Objective-C在發送消息的會檢查類型,如果錯誤會直接拋出異常。

KVC鍵值驗證(Key-Value Validation)
KVC提供了驗證Key對應的Value是否可用的方法:validateValue: forKey: error:
這樣就給了我們一次糾錯的機會。需要指出的是,KVC是不會自動調用鍵值驗證方法的,就是說我們如果想要鍵值驗證則需要手動驗證。但是有些技術,比如CoreData會自動調用。

KVC處理集合
KVC同時還提供了很複雜的函數,主要有下面這些:
簡單集合運算符
簡單集合運算符共有@avg, @count , @max , @min ,@sum5種,都表示什麼直接看下面例子就明白了, 目前還不支持自定義。
對象運算符
比集合運算符稍微複雜,能以數組的方式返回指定的內容,一共有兩種:
@distinctUnionOfObjects
@unionOfObjects
它們的返回值都是NSArray,區別是前者返回的元素都是唯一的,是去重以後的結果;後者返回的元素是全集。
KVC處理字典
當對NSDictionary對象使用KVC時,valueForKey:的表現行爲和objectForKey:一樣。所以使用valueForKeyPath:用來訪問多層嵌套的字典是比較方便的。
KVC裏面還有兩個關於NSDictionary的方法:
dictionaryWithValuesForKeys ,setValuesForKeysWithDictionary

KVC使用
KVC在iOS開發中是絕不可少的利器,這種基於運行時的編程方式極大地提高了靈活性,簡化了代碼,甚至實現很多難以想像的功能。

KVC的使用場景.
1. 動態地取值和設值
2. 用KVC來訪問和修改私有變量
3. Model和字典轉換
4. 修改一些控件的內部屬性 :最常用的就是個性化UITextField中的placeHolderText了
5. 操作集合:KVC的valueForKey:方法作了一些特殊的實現,比如說NSArray和NSSet這樣的容器類就實現了這些方法。所以可以用KVC很方便地操作集合。
6. 用KVC實現高階消息傳遞:當對容器類使用KVC時,valueForKey:將會被傳遞給容器中的每一個對象,而不是容器本身進行操作。結果會被添加進返回的容器中,這樣,開發者可以很方便的操作集合來返回另一個集合。

MVC
概念:MVC最早存在於桌面程序中,M指的是業務數據,V指的是用戶界面,C則指的是控制器。在具體的業務場景中,C作爲一個與M和V之間的協助者,負責獲取輸入的業務數據,然後將處理後的數據輸出到界面上做相應的展示,另外,在數據更新時,C需要及時的提交相應的更新到界面展示,在上述過程中,因爲M和V之間是完全隔離的,所以在業務場景切換時,通常只需要替換相應的C,複用已有的M和V便可以快速搭建新的業務場景。MVC因其複用性,大大提高了開發效率。
優點:給混亂的項目提供了組織方式。通過Controller來掌握全局,同時將view展示和Model的變化分開
缺點:愈發笨重的Controller,隨着業務邏輯的增加,大量的代碼放進Controller,導致Controller越來越臃腫,堆積成千上萬行代碼,後期維護起來費時費力

MVVM
Model層:請求的原始數據
View層:視圖展示,由ViewController來控制
ViewModel層:負責業務處理和數據轉化
在MVVM中,view和ViewCOntroller聯繫在一起,把它們視爲一個組件,view和ViewController都不能直接引用model,而是引用是視圖模型即ViewModel。
viewModel是一個用來放置用戶輸入驗證邏輯、視圖顯示邏輯、網絡請求等業務邏輯的地方,這樣的設計模式,會輕微增加代碼量,但是會減少代碼的複雜性

優點: View可以獨立於Model的變化和修改,一個ViewModel可以綁定到不同的View上,降低耦合,增加重用
缺點: 過於簡單的項目不適用、大型的項目視圖狀態較多時構建和維護成本太大

代理協議
概念:代理模式是一種消息傳遞方式,一個完整的代理模式包括:委託對象、代理對象和協議。

1、首先是委託者的.h文件中的代理指定以及聲明
2、定義一個屬性,注意這裏的修飾詞要用weak
3、在委託者的.m文件中如何調用代理中的方法
4、代理對象這設置代理
5、遵守協議
6、實現協議中規定的方法

單例設計模式
概念: 一個單例類,在整個程序中只有一個實例,並且提供一個類方法供全局調用,在編譯時初始化這個類,然後一直保存在內存中,到程序(APP)退出時由系統自動釋放這部分內存。

優點:
1、在整個程序中只會實例化一次,所以在程序如果出了問題,可以快速的定位問題所在;
2、由於在整個程序中只存在一個對象,節省了系統內存資源,提高了程序的運行效率;
缺點:
 1、不能被繼承,不能有子類;
  2、不易被重寫或擴展(可以使用分類);
  3、同時,由於單例對象只要程序在運行中就會一直佔用系統內存,該對象在閒置時並不能銷燬,在閒置時也消耗了系統內存資源;

熟悉使用 MJRefresh ,AFNetworking ,masonry ,SDWebImage 等第三方框架
述下SDWebImage裏面給UIImageView加載圖片的邏輯:
SDWebImage中爲UIImageView提供了一個UIImageView+WebCache.h的分類,在這個分類中有一個常用的接口sd_setImageWithURL:placeholderImage:,會在真實圖片出現前優先顯示佔位圖,當真是圖片加載出來後在替換佔位圖。
加載過程:
1、首先會在SDWebImageCache中尋找圖片是否有對應的緩存,它會以url作爲數據的索引先在內存中尋找是否有對應的緩存
2、如果緩存未找到就會利用通過MD5處理過的key來繼續在磁盤中查詢對應數據,如果找到了,就會把磁盤中的數據加載到內存中,並將圖片顯示出來
3、如果內存和磁盤緩存中都沒有找到,就會向遠程服務器發送請求,開始下載圖片
4、下載後的圖片會加入緩存中,並寫入磁盤
5、整個獲取圖片的過程都是在子線程中執行的,獲取圖片後回到主線程將圖片顯示出來
SDWebImage原理:
調用類別的方法:
1、從內存(字典)中找圖片(這個圖片在本次使用程序的過程中已經被加載過),找到直接使用。
2、從沙盒中找(這個圖片在之前使用程序的過程中被加載過),找到使用,緩存到內存中
3、從網絡獲取,下載圖片後緩存到內存中,並寫入磁盤內存

AFNetworking 底層原理分析
AFNetWorking是一個適用於iOS和Mac OS X兩個平臺的網絡庫,是基於Foundation URL Loading System上進行了一套封裝,並且提供了豐富且優美的API接口給開發者使用。
URL Loading System是一系列用來訪問通過URL來定位的資源的類和協議
AFNetWorking主要分爲四個模塊:
1.處理請求和回覆得到序列化模塊:Serialization
2.網絡安全模塊:Security
3.網絡監測模塊:Reachability
4.處理通訊的會話模塊:NSURLSession

AFNetWorking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類:
1、AFHTTPRequestOperation:內部封裝的是NSURLConnection,負責發送網絡請求,使用最多的一個類。(3.0廢棄)
2、AFHTTPSessionManager:內部封裝的是NSURLSession,負責發送網絡請求,使用最多的一個類
3、AFNEtWorkReachabilityManager:實時監測網絡狀態的工具類,當前網絡環境發生改變之後,這個工具類就可以檢測到。
4、AFURLRequestSerialization:序列化工具類,基類,上傳的數據轉換成JSON格式。
(AFJSONRequestSerializer).使用不多。
5、AFSecurityPolicy:網絡安全工具類,主要針對HTTPS服務
6、AFURLResponseSerialization:反序列化工具類,使用比較多
7、AFJSONResponseSerializer:json解析器,默認的解析器
8、AFHTTPResponseSerializer:萬能解析器;JSON和XML之外的數據類型,直接返回二進制數據,對服務器返回的數據不做任何處理。
9、AFXMLParserResponseSerializer:XML解析器

熟悉OC的runtime和runloop運行機制和內存管理機制
什麼是runtime?
概念:runtime,運行時,是一套底層的C語言API,是iOS內部核心之一,OC編寫的代碼底層都是基於runtime實現的。其主要的機制是消息轉達機制。
1、消息轉發機制:

  • 動態方法解析

  • 快速轉發

  • 常規轉發(分爲兩步:1.爲方法簽名、2.消息轉發)
    2、消息轉發過程
    objc_msgsend方法:通過對象的isa指針找到對應的class,在class中的方法列表中找到對應的方法,如果找不到繼續在super_class中查找,直到找到對應的方法實現IMP
    3、主要運用

  • 關聯對象(Objective-C Associated Objects)給分類增加屬性

  • 方法魔法(Method Swizzling)方法添加和替換和KVO實現

  • 消息轉發(熱更新)解決Bug(JSPatch)

  • 實現NSCoding的自動歸檔和自動解檔

  • 實現字典和模型的自動轉換(MJExtension)

什麼是runloop?
概念:運行循環,實際上是一個死循環。是事件接收和分發機制的一個實現。一個線程對應一個runloop,作用是保持程序持續運行,處理程序中的各種你事件(觸摸、UI刷新、定時器、selected事件)。通過runloop可以節省CPU資源可以提高程序性能。主程序的runloop默認是開啓的。
runloop和線程的關係?
1、runloop和線程是一一對應的,一個runloop對應一個核心的程序,爲什麼說是核心,是因爲runloop是可以嵌套的,但是核心只有一個,他們的關係保存在一個全局的字典裏面。
2、runloop是用來管理線程的,當線程的runloop被開啓後,線程會執行完任務後進入休眠狀態,有新任務時就會被喚醒執行任務。
3、runloop在第一次獲取時被創建,在線程結束時被銷燬。
4、對於主線程來說,runloop是懶加載的,只有當我們使用的時候纔會創建,所以在子線程用定時器是要注意:確保子線程的runloop被創建,不然定時器不會回調

在開發中如何使用RunLoop?什麼應用場景?
1. 開啓一個常駐線程(讓一個子線程不進入消亡狀態,等待其他線程發來消息,處理其他時間)
2. 在子線程中開啓一個定時器
3. 在子線程中進行一些長期監控
場景:
1. 可以控制定時器在特定模式下執行
2. 可以讓某些事件(行爲,任務)在特定模式下執行
3. 可以添加Observer監聽RunLoop的狀態,比如監聽點擊事件的處理(在所有點擊事件之前做一些事情)

內存管理機制?
OC的內存管理主要有三種方式:MRC(手動管理內存計數)、ARC(自定管理內存計數)、內存池
1、手動內存計數MRC:蹲循內存誰申請,誰管理;誰添加,誰釋放的原則。
2、自動內存計數ARC:有系統自動在編譯階段,在代碼中添加內存管理代碼
3、內存釋放池Release Pool:把需要釋放的內存統一放在一個內存池中,當池子被抽乾後(drain),池子中所以得內存空間也被自動釋放掉。內存池的釋放操作分爲自動和手動的。自動釋放收runloop機制影響。

熟悉Block
block本身是一個對象。
block分爲三種:

  • 全局block(_NSConcretGlobalBlock)=== 存在靜態區
//全局靜態block,不會訪問任何外部變量,執行完就銷燬。
^{
      NSLog(@"Hello World!");
  }();
  • 堆區block(_NSConcretMallocBlock)=== 捕獲外部變量
//保存在堆中的block,當引用計數爲0時會被銷燬。例如按鈕的點擊事件,一直存在,即使執行過,也不銷燬,因爲按鈕還可能被點擊。知道持有按鈕的view被銷燬,它纔會被銷燬。
#import <UIKit/UIKit.h>

typedef void(^ButtonClickBlcok)();

@interface TestView : UIView

@property (nonatomic, copy) ButtonClickBlcok buttonClickBlcok;

@end

#import "TestView.h"

@implementation TestView

- (IBAction)buttonClick:(id)sender {
       if (self.buttonClickBlcok) {
           self.buttonClickBlcok();
       }
 }
@end

  • 棧區block(_NSConcretStackBlock)
//保存在棧中的block,當函數返回時會被銷燬,和第一種的區別就是調用了外部變量。
[UIView animateWithDuration:3 animations:^{
          self.view.backgroundColor = [UIColor redColor];
}];

1、block爲什麼要用copy?
- block在創建的時候默認分配的內存在棧區上,而不是在堆區上。這樣的話其本身的作用域是屬於創建時候的作用域,一旦在創建作用域之外調用就會導致程序的奔潰。所以copy是將其拷貝到堆區內存上。
- block創建在棧區上,而block的代碼種可能會用到本地的一些變量,只有將其拷貝到堆區內存上,才能用這些變量。
-
2、block爲什麼不用retain?
retain這是增加一次引用計數,block的內存還在棧區內存上,並沒有存在堆區內存上,存在棧區上的block可能隨時被系統回收。

3、爲什麼進入block中的對象引用計數需要自動加1?
block執行的是回調,因此block並不知道其中的對象obj創建後會在什麼時候被釋放嗎,爲了不在block使用obj之前對象已經被釋放,block就retain了obj一次。

4、block和函數的關係?
block的使用很像函數指針,不過與函數最大的區別是block可以訪問函數以外、詞法作用域以內的外部變量的值。換句話說,block不僅是實現函數的功能,還能攜帶函數的執行環境。

5、對於block的理解?
block實際上是指向結構體的指針,編譯器會將block的內部代碼生成對應的函數

6、block中self的循環引用?
block默認創建在棧區上,所以對其進行copy操作,將其拷貝到堆區上,便於更好的操作對象。但是執行copy操作之後,block中使用self,比對象會被retain一次(注意:block在堆區上時纔會起到retain作用),就會造成循環引用。

解決方法:
1.MRC環境下使用__block修飾
2.在ARC環境下使用_unsafe_unretain/weak修飾

總結:block可能理解成爲指向結構體的指針,編譯器會把block的代碼默認生成其對應的函數。因爲block創建時是在棧區上的,因此需要使用copy進行修飾,使其copy到堆區上,方便操作其對象。因爲block是回調,我們不知道其對象將會在什麼時候被釋放,所以block會自動將其對象retain一次,使其引用計數加1,避免在使用其對象前就被釋放掉的尷尬場景。需要注意的是,在block內部使用self修飾對象,會使其retain一次,這樣會造成循環引用,解決的方法是MRC下使用__block修飾,ARC下使用__unsafe_unretained或者是__weak修飾。

7、協議跟block的區別?

  • block優勢:
    - block的代碼可讀性更好,因爲block只要實現就可以,而代理需要蹲循協議並且實現協議裏的方法,而兩者還不在一個地方。
    - 代理使用起來比較麻煩,需要聲明協議,聲明代理屬性,蹲守協議,實現協議的方法。而block只需要聲明屬性和實現就可以
    - block是一種輕量級的回調,可以直接訪問上下文,由於block的代碼是內嵌的,運行效率高。block本就是一個對象,實現了匿名函數的功能,所以可以吧block當做一個成員變量、屬性、參考使用,使用起來非常靈活
    • block的劣勢:
      • block的運行成本高,block出棧需要將使用的數據從棧內存中拷貝到堆內存,當然對象的話就是引用計數加1,使用完或者block置nil才銷燬
      • 代理只是保存了一個對象指針(用weak修飾delegate,不會造成循環引用),直接回調,沒有額外的消耗
      • block容易造成循環引用,而且不易察覺。因爲爲了block不被系統回收,都用copy修飾,實行強引用。block對捕獲的變量也都是強引用,所以會造成循環引用。

代理:代理是一種通用的設計模式,很多語言上都有實現,在iOS中,代理作爲一種消息傳遞的方式,使用的非常普遍。代理包括三部分:代理、委託、協議。委託方通過實現協議的方法,接受代理傳遞過來的消息。
協議:用來指定代理雙方可以做什麼,必須做什麼
委託對象:根據指定的協議,指定代理去完成什麼功能,即調用協議中的方法。
代理對象:根據指定的協議,完成委託方需要實現的功能,即實現協議中的方法。

代理屬性使用weak修飾:
- weak:指明該對象並不負責保持delegate這個對象,delegate這個對象的銷燬有外部控制
- strong:該對象強引用delegate,外界不能銷燬delegate對象,會導致循環引用(retain cycles)
- assign:也有weak的功效,但是assign是指賦值,不對引用計數器操作,使用之後如果沒有置nil,可能會產生野指針。

應用範圍:
1.一般回調優先使用block。
2.如果回調的狀態很多,一般對於三個就使用代理
3.如果回調頻繁,次數很多,想tableView,scrollView,每次初始化,滑動,點擊都回調,使用代理

Socket概念
概念:網絡七層由下往上分別爲物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。其中物理層、數據鏈路層和網絡層通常被稱作媒體層,是網絡工程師研究的對象,傳輸層、會話層、表示層和應用層則被稱作主機層,是用戶所面向和關心的內容。

  • http協議對應的應用層
  • tcp / udp 協議應對的是傳輸層
  • IP協議對應的是網絡層

三者本質上沒有可比性。何況HTTP協議是基於TCP連接的,TCP/UDP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要是解決如何包裝數據。我們在傳輸數據時,可以只使用傳輸層(TCP/UDP),但是那樣的話,由於沒有應用層,便無法識別數據內容,如果想要是傳輸的數據有意義,則必須使用應用層協議,應用層協議很多,有HTTP、FTP、TELNET等等,也可以自己定義應用層協議。web使用HTTP作傳輸層協議,以封裝HTTP文本信息,然後使用TCP/IP做傳輸層協議將它發送到網絡上。socket是對TCP/IP協議的封裝,socket本身並不是協議,而是一個接口調用(API),通過socket,我們才能使用TCP/IP協議。

socket又稱之爲“嵌套字”,是系統提供的用於網絡通信的方法,它的實質並不是協議,沒有規定計算機應當怎麼樣傳遞消息,只是給程序員提供了一個發送消息的接口,程序員使用這個接口提供的方法,發送和接受消息。

socket描述了一個IP、端口對。它簡化了程序員的操作,知道對方的IP以及PORT就可以給對方發送消息,有服務器端來處理髮送這些消息。所以,socket一定包含了通信雙方,及客戶端與服務端。

socket的通信過程
每個應用或者服務都有一個端口,比如DNS的端口號爲53,HTTP的端口號爲80。都是對應一個應用或者服務的端口。我們能由DNS請求到查詢信息,是因爲DNS服務器時刻都在監聽53端口,當收到查詢請求以後,就能夠返回我們想要的IP信息,所以,從程序設計上講,應該包含以下步驟:
- 服務端利用socket監聽端口
- 客戶端發送連接
- 服務端返回信息,建立連接,開始通訊
- 客戶端,服務端斷開連接

socket的原理:
- 套接字(socket)是通信的基石,是支持TCP/IP協議的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示,包含進行使用的協議,本地主機的IP地址,本地進程的協議端口,遠程主機的IP地址,遠程的協議端口。
- 應用層通過傳輸層進行數據通信時,TCP會遇到同時爲多個應用程序提供併發服務的問題。多個TCP連接或者多個應用程序進程可能需要通過一個TCP協議端口傳輸數據。爲了區別不同的應用程序和連接,許多計算機操作系統爲應用程序與TCP/IP協議交互提供了套接字(socket)接口。應用層可以和傳輸層通過socket接口,區分來自不同應用程序進程或者網絡連接的通信,實現數據傳輸的併發服務。

socket連接
建立socket連接至少需要一對套接字,其中一個運行於客戶端,稱爲clientSocket,另一個運行於服務器端,稱爲ServerSocket。

套接字之間的連接過程分爲三個步驟
- 服務器監聽:服務器套接字並不定位具體的客戶端套接字,而是出於等待的狀態,實時監控網絡的狀態,等待客戶端的連接請求
- 客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器的套接字。爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器套接字的地址和端口,然後就想服務器端套接字提出請求。
- 連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發送給客戶端,一旦客戶端確認此描述,雙方正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接受其他客戶端套機制的請求。

OSI各層功能:
應用層:
- 與其他計算機進行通信的一個應用,它是對應用程序的通信服務的。例如,一個沒有通信功能的自處理程序就不能執行通信的代碼。從事自處理工作的程序員也不關心OSI的第7層。但是,如果添加了一個傳輸文件的選項,那麼自處理的程序員就需要實現OSI的第7層。如:TELNET、HTTP、FTP、SMTP等待。
- 這個一層的主要功能是定義數據格式及加密。例如,FTP允許你選擇以二進制或者ASCII格式傳輸,如果選擇二進制,那麼發送方和接收方不改變文件的內容。如果選擇ASCII格式,發送方將把文件從發送方的字符集轉換成標準的ASCII後發送數據。在接受方將標準的ASCII轉換成接收方計算機的字符集。如:加密,ASCII等。

會話層
- 它定義瞭如何開始、控制和結束一個會話,包括對多個雙向消息的控制和管理,以便在只完成連續消息的一部分時可以通知應用,從而使表示層看到數據是連續的,在某種情況下,如果表示層收到了所有的數據,則用數據表示表示層。如:RPC、SQL等。

傳輸層
- 這層的功能包括是否選擇差錯恢復協議還是無差錯恢復協議,及在同一主機上對不同應用的數據流的輸入進行復用,還包括對收到的順序不對的數據包的重新排序功能,如:TCP、UDP、SPX。

網絡層
- 這層對端到端的包傳輸進行定義,它定義了能夠標識所有結點的邏輯地址,還定義了路由的實現方式和學習方式。爲了適應最大傳輸單元長度的傳輸介質,網絡層還定義瞭如何將一個包分解成更小的包的分段法。如:IP、IPX等。

數據鏈路層
- 它定義了在單個鏈路上如何傳輸數據,這些協議與被討論的各種介質有關,如:ATM、FDDI等。

物理層
- OSI的物理層規範是有關傳輸介質的特性,這些規範通常參考了其他組織制定的標準。連接頭、幀、幀的使用、電流、編碼及光調劑等都屬於各種物理層規範中的內容。物理層常用多個規範完成對所有細節的定義。如:Rj45、802.3等。

TCP(傳輸控制協議) / UDP(用戶數據協議) 區別?
TCP:是一種面向連接的、可靠的、基於字節流的傳輸層通信協議,是專門爲了在不可靠的網絡中提供一個可靠的端對端字節流而設計的,面向字節流。
UDP:是ISO參考模型中一種無連接的傳輸層協議,提供簡單不可靠的非連接傳輸層服務,面向報文

區別:
1、TCP是面向連接的,可靠性高LUDP是基於非連接的,可靠性底。
2、由於TCP是連接的通信,需要有三次握手、重新確認等連接過程,會有延時,實時性差,同時過程複雜,也使其易於攻擊;UDP沒有建立連接的過程,因而實時性較強,也比較安全。
3、在傳輸相同大小的數據時,TCP首部開銷20字節;UDP首部開銷8字節,TCP報頭比UDP複雜,故實際包含的用戶數據較少。TCP在IP協議的基礎上添加了序號機制、確認機制、超市重傳機制等,保證了傳輸的可靠性,不會出現丟包或者亂序,而UDP有丟包,故TCP開銷大,UDP開銷較小。
4、每條TCP連接只能時點到點的;UDP支持一對一、一對多、多對一、多對多的交互通信

應用場景的選擇
對實時性要求高和高速傳輸的場合下使用UDP;在可靠性要求低,追求效率的情況下使用UDP;需要傳輸大量數據且對可靠性要求高的情況下使用TCP。

多線程
進程:進程指的是系統中正在運行的應用程序。
線程:1.1個進程想要執行任務,必須得有線程
2.一個進程的所有任務都在線程中執行

進程和線程的比較:
1.線程是CPU調用的最小單位
2.進程是CPU分配資源和調度的單位
3.一個程序可以對應多個線程,一個進程中可以有多個線程,但至少要有一個線程
4.同一個進程內的線程共享進程的資源

線程的串行:
1.如果要在一個線程中執行多個任務,那麼只能一個一個的按順序執行這些任務
2.也就是說,在同一時間內,1一個線程只能執行一個任務(也可以認爲線程是進程的1條執行路勁)

多線程 - 並行(同時執行):
1個進程中可以開啓多條線程,每條線程可以並行(同時)執行不同的任務

多線程的原理:
1.同一時間,CPU只能處理1條線程,只有1條線程在工作(執行)
2.多線程併發(同時)執行,其實是CPU快速的在多個線程之間調度(切換)
3.如果CPU調度線程的時間足夠快,就造成了多線程併發執行的假象

常用的多線程:
NSThread : 直接操作線程對象,但是需要手動管理生命週期,而且經常使用這中方式查看當前線程
GCD : 底層使用的是C語言,靈活方便,可以根據系統負荷來增減線程,性能效率更好
NSOperation : NSOperation是對GCD的封裝,使用起來更好理解,將任務封裝爲NSOperation,添加到NSOperationQueue對象中。子類化NSOperation的設計,更具有面向對象的特性。更加適合在複雜的項目中使用。

思考:
如果線程非常多,會發生什麼?
1.CPU會在多線程之間調度,CPU會累死,消耗大量的CPU資源
2.每條線程被調度執行的頻率會降低(線程的執行效率低)
3.建議:一般程序中開3~5條線程就足夠,合理的使用線程

多線程的優缺點:
優點:
1.能適當的提高程序的執行效率
2.能適當提高資源的利用率(CPU、內存利用率)
缺點:
1.創建線程是有開銷的,iOS下主要成本包括:內核數據結構(大約1KB)、棧空間(子線程512KB、主線程1MB,也可以使用-setStackSize:設置,但是必須是4K的倍數,而且是最小的16k),創建線程大約需要90毫秒的創建時間
2.如果大量開啓線程,會降低程序的性能
3.線程越多,CPU在調度線程上的開銷就越大
4.程序設計更加複雜,如線程之間的通信、多線程的數據共享

總結:
進程:正在運行的一個應用程序
線程:進程中真正執行任務的
關係:包含(每個進程至少有一個線程)
串行:針對一個線程中有多個任務->按順序執行
並行:多個線程同時執行
多線程的優缺點:
優點:提高程序性能
缺點:需要開銷 + 程序更加複雜

多線程的安全隱患:
1、資源共享:
- 1塊資源可能會被多個線程共享,也就是多個線程可能會訪問一塊資源
- 比如多個線程訪問同一個對象,同一個變量,同一個文件
- 當多個線程同時訪問一塊資源時,很容易引發數據錯亂和數據安全問題
- 如存錢/取錢,買票賣票

2、解決這中問題:互斥鎖

3、互斥鎖的使用格式:
@synchronize(鎖對象){需要鎖定的代碼}

互斥鎖的優缺點:
優點:能有效防止因多線程搶奪資源造成的數據安全問題
缺點:需要消耗大量的CPU資源
互斥鎖使用前提:
多線程搶奪同一塊資源

加鎖的注意點:
- 加鎖需要消耗大量的CPU資源
- 注意加鎖的位置
- 注意加鎖的前提條件
- 注意加鎖的鎖對象,鎖定1份代碼只能用1把鎖,用多把是無效的

4、相關的專業術語:線程同步 線程異步(並行執行)
- 線程同步:多條線程在同一條線上執行(按順序執行)–> 互斥鎖使用的是線程同步
- 線程異步:多條線程同時執行

線程之間的通信
概念:在1個進程中,線程往往不是孤立存在的,多個線程之間需要經常進行通信

線程間通信的體現
1.1個線程傳遞數據給另一個線程
2.在1個線程中執行完特定任務後,轉達另一個線程繼續執行任務

線程間通信的常用方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

NSThread 創建的三種方法:
1、創建方法:
- 直接alloc initWith…
- 分離出子線程 detachNewThread
- 開啓後臺線程 performSelectorInBackground

2、對比:
- alloc 代碼量大,但是能拿到線程對象
- detachNewThread 無法拿到線程對象進行詳細設置
- perfromSelectorInBackground 無法拿到線程對象進行詳細設置

3、生命週期:
當線程內部的任務執行完畢的時候,線程對象會被自動釋放

請簡單列出NSThread線程的幾種狀態,並說明狀態轉換的邏輯

狀態:新建 - 就緒 - 運行 - 阻塞 - 死亡

切換條件:
- 當創建了線程對象之後,線程處於新建狀態(例如[NSThread alloc] initWithTarget…),此時會在內存中創建線程對象分配資源
- 當啓動線程後,線程處於就緒狀態,(如調用start方法),此時線程對象會被移入到可調度線程池中【新建->就緒】
- 當CPU調度到該線程的時候,線程處於運行狀態,CPU調度其他線程的時候(時間片過完)線程會從運行狀態切換到就緒狀態。【就緒<->運行】
- 當線程需要“等待同步鎖或者是被調用了seelp方法”時,線程會從運行狀態切換到阻塞狀態,此時在內存中線程對象會被從可調度線程池中移除,處於阻塞狀態的線程無法工作【運行->阻塞】
- 當線程“同步鎖或者seelp方法”結束時,那個線程會從阻塞狀態切換到就緒狀態,此時線程對象會被移入到可調度線程池【阻塞->就緒】
- 當“任務執行完畢或者異常退出”的時候,線程進入到“死亡狀態”,在內存中分配的資源被回收,線程進入到死亡狀態之後,在也不能有任何的其他狀態切換,是爲終結狀態。

請簡單介紹下GCD這門技術
1.GCD的全稱Grand Central Dispatch,是iOS開發中一門用來實現多線程編程的技術
2.GCD本身是純C語言的,但是內部提供了非常強大的函數
3.GCD是蘋果公司爲多核的並行運算提供的解決方案,並且自動利用更多的CPU內核(如雙核、四核)
4.GCD自動管理線程的生命週期(如創建線程,調度任務,銷燬線程等等都不需要程序員關係)
5.使用GCD實現多線程編程只需要兩個步驟,先封裝好要執行的任務(代碼塊),然後把任務添加到對應的隊列中即可

優勢:
1.GCD是蘋果公司爲多核的並行運算提出的解決方案
2.GCD會自動利用更多的CPU內核
3.GCD會自動管理線程生命週期(創建線程、調度任務、銷燬線程)
4.程序員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼

2個核心概念:
任務:執行什麼任務
隊列:用來存放任務

使用步驟:
1.創建隊列
2.封裝任務,把任務添加到隊列,使用函數封裝任務

將任務添加到隊列中:
1.GCD對自動將隊列中的任務取出,放到對應的線程中執行
2.任務的取出蹲循隊列的FIFO原則:先進先出,後進後出

數據結構:
棧:(先進先出)像杯子一樣,把任務先後順序一個一個放進去,取出的時候從上面往下面取,先進後出
隊列:隊列像管子一樣,有兩個端口,把任務一個一個從A出口存進去,取得從B口取出,先進先出,後進後出

GCD中2個用來執行任務的常用函數:
用同步的方式執行任務:

dispatch_sync(dispatch_que_t queue, dispatch_block_t block);
            queue:隊列
            block:任務

用異步的方式執行任務:

dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

同步和異步的區別(任務的執行方式):
同步:只能在當前線程中執行任務,不具備開啓新線程的能力
異步:可以在新的線程中執行任務,具備開啓新線程的能力

封裝任務的函數:
1. 同步函數:dispatch_sync
① 不具備開線程的能力,不能開線程
② 任務執行的方式:同步
2. 異步函數:dispatch_async
① 具備開線程的能力,可以開線程
② 任務執行的方式:異步

GCD中的隊列
1. 併發隊列(Concurrent Dispatch Queue)
1.可以讓多任務併發(同時)執行(自動開啓多個線程同時執行任務)
2.併發功能只有在異步(dispatch_async)函數下才有效
“只要第一個任務取出來之後,不用等待執行完畢,就可以接着取第二個任務”
①自己創建併發隊列:dispatch_queue_create concurrent
②全局併發隊列:dispatch_get_global_queue
GCD默認已經提供了全局的併發隊列,共整個應用使用,可以無需手動創建
使用dispatch_get_global_queue函數獲得全局的併發隊列
2. 串行隊列(Serial Dispatch Queue)
讓任務一個接着一個地執行(一個任務執行完畢後,在執行下一個任務)
“第一個任務取出來之後,必須等待該任務執行完,纔可以接着取第二個任務”
①自己創建:dispatch_queue_create serial
②主隊列:
使用主隊列(跟主線程相關聯的隊列)
主隊列是GCD自帶的一種特殊的串行隊列:放在主隊列中的任務,都會放到主隊列中執行
使用dispatch_get_main_queue()獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue()

任務的執行方式:
同步執行:必須等當前任務執行完畢,才能執行後面的任務
異步執行:不必等當前任務執行完畢,就能執行後面的任務

各種組合情況

  • 同步函數
    + 串行隊列 不會開線程,所有任務在當前線程中串行執行
    + 併發隊列 不會開線程,所有任務在當前線程中串行執行
    + 主隊列 死鎖
  • 異步函數
    + 串行隊列 會開1條線程,所有的任務在子線程中串行執行
    + 併發隊列 會開N條線程,所有任務在子線程中併發執行(線程的數量不等於任務數量,線程數量由GCD內部自動決定的)
    + 主隊列 不會開線程,所有的任務在主線程串行執行
  • 併發隊列:自己創建|全局隊列
    1. 自己創建的隊列要向系統申請,而且全局隊列在整個應用程序默認存在,並且有對應的優先級
    2. 柵欄函數只能用在自己創建的併發隊列纔有效(蘋果沒有給出原因)

NSOperation是對GCD的一層包裝
GCD:任務 + 隊列
NSOperation:操作 + 隊列
1. 創建隊列
2. 封裝隊列
3. 把操作添加到隊列

NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能實現多線程編程
- NSOperation和NSOperationQueue實現多線程的具體步驟
1. 先將NSOperation對象添加到NSOperationQueue中
2. 系統會自動將NSOperationQueue中的NSOperation取出來
3. 將取出的NSOperation封裝的操作放到一條線程中執行

NSOperation是個抽象類,並不具備封裝操作的能力,必須使用它的子類
使用NSOperation子類的方式有3種
1. NSInvocationOperation
2. NSBlockOperation
3. 自定義子類繼承NSOperation,實現內部相應的方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章