如今已經不斷有網友發我他們在面試中遇到的面試題,這是一位程序媛前面在面試中遇到的問題面試
前面兩個過於基礎,從提升題開始分享;我的看法,勿噴設計模式
代理的⽬的是改變或傳遞控制鏈。容許⼀個類在某些特定時刻通知到其餘類,而不須要獲取到那些類的指針。能夠減小框架複雜度。數組
什麼是代理?緩存
代理是⼀種通⽤的設計模式,代理主要由三部分組成服務器
⼀、協議:用來指定代理雙方能夠作什麼,必須作什麼;app
2、代理:根據指定的協議,完成委託⽅須要實現的功能;框架
3、委託:根據指定的協議,指定代理去完成什麼功能。函數
代理的實現流程性能
在iOS中代理的本質就是代理對象內存的傳遞和操做,咱們在委託類設置代理對象後,實際上只是⽤一個id類型的指針將代理對象進⾏了一個弱引⽤。委託⽅讓代理方執⾏操做,其實是在委託類中向這個id類型指針指向的對象發送消息,⽽這個id類型指針指向的對象,就是代理對象。ui
代理的內存管理
使⽤代理若是聲明的不對,會形成循環引⽤的問題。⼀般會⽤weak修飾,⽤strong修飾會形成循環引⽤問題,⽤assign修飾會形成crash。
代理與其餘iOS中消息傳遞的⽅式的對⽐
通知:在iOS中由通知中⼼進⾏消息接收和消息⼴播,是⼀種⼀對多的消息傳遞⽅式。
代理:是⼀種通⽤的設計模式,iOS中對代理⽀持的很好,由代理對象、委託者、協議三部分組成。
Block:iOS4.0中引⼊的⼀種回調⽅法,能夠將回調處理代碼直接寫在block代碼塊中,看起來邏輯清晰代碼整⻬。
target action:經過將對象傳遞到另⼀個類中,在另⼀個類中將該對象當作target的⽅式,來調⽤該對象⽅法,從內存⻆度來講和代理相似。
KVO:NSObject的Category-NSKeyValueObserving,經過屬性監聽的⽅式來監測某個值的變化,當值發⽣變化時調⽤KVO的回調⽅法。
代理與block的對⽐
多個消息傳遞,應該使⽤delegate。在有多個消息傳遞時,⽤delegate實現更合適,看起來也更清晰。block就不太好了,這個時候block反而不便於維護,⽽且看起來⾮常臃腫,很彆扭。例如UIKit的UITableView中有不少代理若是都換成block實現,會⽐比delegates難⽤好多。
2.一個委託對象的代理屬性只能有⼀個代理對象,若是想要委託對象調⽤多個代理對象的回調應該用block。
3.單例對象最好不要⽤delegate。單例對象因爲始終都只是同⼀個對象,若是使⽤delegate,就會形成delegate屬性被從新賦值的問題,最終只能有一個對象能夠正常響應代理⽅法。
4.代理更加⾯相過程,block則更面向結果。從設計模式的角度來講,代理更加面向過程,⽽block更加⾯向結果。
5.從性能上來講,block的性能消耗要略大於delegate,由於block會涉及到棧區向堆區拷⻉等操做,時間和空間上的消耗都⼤於代理。⽽代理只是定義了⼀個⽅法列表,在遵照協議對象的objc_protocol_list中添加⼀個節點,在運⾏時向遵照協議的對象發送消息便可
什麼是多繼承?
假設C類要同時繼承A類和B類,則稱之爲多繼承。這種狀況就是多繼承。
oc中的「多繼承」
其實Objective-C不支持多繼承,因爲消息機制名字查找發⽣在運⾏時⽽非編譯時,很難解決多個基類可能致使的⼆義性問題。不過其實Objective-C 也⽆需⽀持多繼承,咱們能夠找到以下⼏種間接實現多繼承⽬的方法:
經過組合實現「多繼承」
經過協議實現「多繼承」
經過組合實現「多繼承」
經過協議實現「多繼承」
雖然OC在語法上禁⽌類使⽤多繼承,可是卻能夠⽤協議來實現多繼承。協議只能提供接⼝,而沒有提供實現⽅式,若是隻是想多繼承基類的接⼝,那麼遵照多協議⽆疑是最好的⽅法。
此⽅法缺點⽐較明顯:須要修改兩個⽗類,同時並不能調⽤兩個⽗類的原⽣⽅法,須要在⼦類中實現⽅法。
Singleton: 單例模式。 簡單來講, 就是保證在你不主動銷燬這個單例對象的狀況下, 整個項目中都始終擁有這
個單例對象, 而且這個單例對象在內存中都是同一個內存地址。
因此, 單例很重要的兩個特色:
(1) app生命週期中一直存在(除主動銷燬外)
(2) 在整個生命週期中, 都是同一個內存地址
根據這兩個特色, 我來描述一個應用中的使用場景。 最簡單和經常使用的就是, 咱們用戶的登陸信息, 不作本地緩存的話, 咱們登陸成功以後, 把服務器請求下來的用戶信息保存到單例中。 好比這樣 [UserSingletonshareInstance].name = 「張山」。 接下來, 你不管在應用的任何頁面均可以直接經過[UserSingleton shareInstance].name的方式獲取到用戶的名字, 並且這個名字都是」張三」。 其餘作法,都會比這個麻煩。
那麼怎麼寫單例呢? 核心的一點就是, 咱們平時建立一個實例對象時候用到的方法(alloc, init), 都要重寫一遍,保證使用這些方法建立對象的時候是隻分配一塊內存地址,而後第一次建立以後再建立都指向前邊已經建立過得那個內存地址,順着這個思路,代碼以下:
#import "Singleton.h" @interface Singleton()<NSCopying,NSMutableCopying> @end @implementation Singleton static Singleton* _instance = nil; +(instancetype) shareInstance { static dispatch_once_t onceToken ; dispatch_once(&onceToken, ^{ _instance = [[super allocWithZone:NULL] init] ; //不是使用alloc方法,而是調用[[super allocWithZone:NULL] init] //已經重載allocWithZone基本的對象分配方法,因此要借用父類(NSObject)的功能來幫助出處理底層內存分配的雜物 }) ; return _instance ; } +(id) allocWithZone:(struct _NSZone *)zone { return [Singleton shareInstance] ; } -(id) copyWithZone:(NSZone *)zone { return [Singleton shareInstance] ;//return _instance; } -(id) mutablecopyWithZone:(NSZone *)zone { return [Singleton shareInstance] ; } @end
而後,OC有個語法糖能夠寫:
#import "Manager.h" @implementation Manager +(Manager *)sharedManager{ static dispatch_once_t predicate; static Manager * sharedManager; dispatch_once(&predicate, ^{ sharedManager=[[Manager alloc] init]; }); return sharedManager; } @end
Selector/SEL又叫方法選擇器,SEL在objc.h中是這樣聲明的,而「@selector()」是取得一個SEL指針。說白了,方法選擇器僅僅是一個char *指針,表示它所表明的是方法的名字。 簡單來講: 「@Selector 就是用字符串表示某個類的某個方法。」 更加專業的說法是: 「Selector就是OC的虛擬表(virtual table)中指向實際執行的函數指針(function pointer)的一個C字符。」
咱們通常用它來「由於method能夠用字符串表示,所以,某個method就能夠變成用來傳遞的參數。」 再說的透明一點, 由於 selector 能夠看作是函數的另外一個名字,因此不少須要調用函數或者創建鏈接的地方,均可以用到,如下是一些具體的使用場景:
Target/Action 模式
檢查 method 是否存在
Timer
在線程中執行方法
數組排序
代替 if else / switch
調用私有 API