設計模式-MVC模式和KVC/KVO模式以及單例模式的理解記錄

MVC模式:

M: model 是數據模型
V: view 是視圖
C: controller 是控制器

一張圖解釋MVC

model 與view 不能直接交互,需要通過controller來同步
model負責數據和狀態的更新,view顯示給用戶看的內容

如何通信:

  • controller到model: 導入model類,實例化model對象,進行數據同步
    controller到view: 在controller中創建View的控件outlet屬性進行通信

  • view到controller: 不能在view類中實例化controller,需要使用target: action目標動作機制或者delegate委托機制實現通信
    target:action:用戶在view上觸發事件,view會產生一個action動作,在controller中,通過addtarget:方法,接受action動作。controller自身設置target,view在需要通知controller時向controller發送action
    delegate: controller作為view的被委托者,代理。為view 提供顯示需要的數據
    分為動作類delegate:在controller中響應action動作和data source數據源類delegate:為view 提供需要顯示的數據

  • model到controller: 不能在model類中實例化controller,因為model不知道有多少個controller引用它。
    需要使用通知機制和KVO模式和controller通信
    Notification: model自己設置一個通知中心NSNotificationCenter,需要知道model數據變化的的controller自己注冊一個通知addObserver來監聽model的數據數據變化,讓不需要監聽model需要移除注冊監聽
    KVO: 使用addObserver:forKeyPath:option:context:方法和removeObserver:forKeyPath:方法來對model設置和移除注冊監聽

MVC模式的優勢:

  1. 低耦合性
  2. 高重用性
  3. 可適用性
  4. 可維護性

MVVM的簡單理解:

MVVM模式是MVC模式的增強,把MVC中controller里的表示model給view顯示數據的表示邏輯部分提取出來了——view model
MVVM 即 model view viewcontroller view model

單例模式:

應用程序中的類只擁有一個實例:
NSApplication NSFileManager NSBundle UserDefault VIAccelermeter
單例易于訪問

實現單例:

  1. 在類的內部提供一個static修飾的全局變量
  2. 提供一個類方法,方便外界訪問
  3. 重寫+allocWithZone方法,保證永遠都只為單例對象分配一次內存空間
  4. 重寫-copyWithZone方法和-MutableCopyWithZone方法

KVC模式:

KVC概念::KeyValueCoding 鍵值編碼

KVC可以直接通過字符串類型的屬性名key來訪問某個類屬性的機制,支持對象和基本數據類型(自動封裝,解裝)不是通過調用的Setter、Getter方法訪問
關鍵方法定義在 NSKeyValueCodingProtocol

KVC用法:

獲取值

- valueForKey:,傳入NSString屬性的名字。
- valueForKeyPath:,傳入NSString屬性的路徑,xx.xx形式。
- valueForUndefinedKey它的默認實現是拋出異常,可以重寫這個函數做錯誤處理。

修改值

- setValue:forKey:
- setValue:forKeyPath:
- setValue:forUndefinedKey:
- setNilValueForKey: 當對非類對象屬性設置nil時,調用,默認拋出異常。
一對多關系成員的情況
- mutableArrayValueForKey:有序一對多關系成員 NSArray
- mutableSetValueForKey:無序一對多關系成員 NSSet

鍵值驗證(Key-Value Validation)

KVC提供屬性值確認的API,它可以用來檢查set的值是否正確、為不正確的值做一個替換值或者拒絕設置新值并返回錯誤原因
調用核查方法:
- validateValue:forKey:error:,默認實現會搜索 validate<Key>:error:格式的核查方法,找到則調用,未找到默認返回YES

集合操作:

集合操作通過對valueForKeyPath:傳遞參數來使用,一定要用在集合(如:array)上,否則產生運行時刻錯誤。其格式如下:
Left keypath部分:需要操作對象路徑。
Collectionoperator部分:通過@符號確定使用的集合操作。
Rightkey path部分:需要進行集合操作的屬性。

  • 數據操作
    @avg:平均值
    @count:總數
    @max:最大
    @min:最小
    @sum:總數
    確保操作的屬性為數字類型,否則運行時刻錯誤。
  1. 對象操作
    針對數組的情況
    @distinctUnionOfObjects:返回指定屬性去重后的值的數組
    @unionOfObjects:返回指定屬性的值的數組,不去重
    屬性的值不能為空,否則產生異常。
  2. 數組操作
    針對數組的數組情況
    @distinctUnionOfArrays:返回指定屬性去重后的值的數組
    @unionOfArrays:返回指定屬性的值的數組,不去重
    @distinctUnionOfSets:同上,只是返回值為NSSet

KVC鍵值查找(搜索單值成員)

- setValue:forKey:搜索方式

1、首先搜索setKey:方法。(key指成員變量名,首字母大寫)
2、上面的setter方法沒找到,如果類方法accessInstanceVariablesDirectly返回YES。那么按 _key,_isKey,key,iskey的順序搜索成員名。(NSKeyValueCodingCatogery中實現的類方法,默認實現為返回YES)
3、如果沒有找到成員變量,調用setValue:forUnderfinedKey:

- valueForKey:的搜索方式

1、首先按getKey,key,isKey的順序查找getter方法,找到直接調用。如果是BOOL、int等內建值類型,會做NSNumber的轉換
2、上面的getter沒找到,查找countOfKey、objectInKeyAtindex、KeyAtindexes格式的方法。如果countOfKey和另外兩個方法中的一個找到,那么就會返回一個可以響應NSArray所有方法的代理集合的NSArray消息方法
3、還沒找到,查找countOfKey、enumeratorOfKey、memberOfKey格式的方法。如果這三個方法都找到,那么就返回一個可以響應NSSet所有方法的代理集合
4、還是沒找到,如果類方法accessInstanceVariablesDirectly返回YES。那么按 _key,_isKey,key,iskey的順序搜索成員名
5、再沒找到,調用valueForUndefinedKey

KVC實現分析:

KVC運用了isa-swizzing技術。isa-swizzing就是類型混合指針機制。KVC通過isa-swizzing實現其內部查找定位。isa指針(is kind of 的意思)指向維護分發表的對象的類,該分發表實際上包含了指向實現類中的方法的指針和其他數據。
例如:
[site setValue:@"sitename" forKey:@"name"];
//會被編譯器處理成

IMP method = objc_msg_loopup(site->isa,sel);
method(site,sel,@"sitename",@"name");```
每個類都有一張方法表,是一個hash表,值是還書指針IMP,SEL的名稱就是查表時所用的鍵。

SEL數據類型:查找方法表時所用的鍵。定義成char*,實質上可以理解成int值。
IMP數據類型:他其實就是一個編譯器內部實現時候的函數指針。當Objective-C編譯器去處理實現一個方法的時候,就會指向一個IMP對象,這個對象是C語言表述的類型

### KVC的內部機制:

一個對象在調用setValue的時候進行了如下操作:
1. 根據方法名找到運行方法的時候需要的環境參數
2. 他會從自己的isa指針結合環境參數,找到具體的方法實現接口。
3. 再直接查找得來的具體的實現方法


## KVO模式

### KVO概念:鍵值觀察Key-Value-Observer就是觀察者模式
觀察者模式的定義:一個目標對象管理所有依賴于它的觀察者對象,并在它自身的狀態改變時主動通知觀察者對象。這個主動通知通常是通過調用各觀察者對象所提供的接口方法來實現的。觀察者模式較完美地將目標對象與觀察者對象解耦

### KVO實現步驟:
1. 注冊監聽

//keyPath就是要觀察的屬性值
//options給你觀察鍵值變化的選擇
//context方便傳輸你需要的數據
-(void)addObserver:(NSObject *)anObserver
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context

2. 實現監聽
  ```//change里存儲了一些變化的數據,比如變化前的數據,變化后的數據;
  //如果注冊時context不為空,這里context就能接收到
  -(void)observeValueForKeyPath:(NSString \*)keyPath
                     ofObject:(id)object
                       change:(NSDictionary \*)change
                      context:(void \*)context
  1. 移除監聽

使用觀察者模式需要被觀察者的配合,當被觀察者的狀態發生變化的時候通過事先定義好的接口(協議)通知觀察者。在KVO的使用中我們并不需要向被觀察者添加額外的代碼,就能在被觀察的屬性變化的時候得到通知

KVO實現步驟分析:

  1. 當類A的對象第一次被觀察的時候,系統會在運行期動態創建類A的派生類。我們稱為B。
  2. 在派生類B中重寫類A的setter方法,B類在被重寫的setter方法中實現通知機制。
  3. 類B重寫會 class方法,將自己偽裝成類A。類B還會重寫dealloc方法釋放資源。
  4. 系統將所有指向類A對象的isa指針指向類B的對象。

KVO同KVC一樣,通過 isa-swizzling 技術來實現。當觀察者被注冊為一個對象的屬性的觀察對象的isa指針被修改,指向一個中間類,而不是在真實的類。其結果是,isa指針的值并不一定反映實例的實際類。

所以不能依靠isa指針來確定對象是否是一個類的成員。應該使用class方法來確定對象實例的類

使用KVO的幾種方法:

  • 使用了KVC
    使用了KVC,如果有訪問器方法,則運行時會在訪問器方法中調用will/didChangeValueForKey:方法;
    沒用訪問器方法,運行時會在setValue:forKey方法中調用will/didChangeValueForKey:方法。
  • 有訪問器方法
    運行時會重寫訪問器方法調用will/didChangeValueForKey:方法。
    因此,直接調用訪問器方法改變屬性值時,KVO也能監聽到。
  • 顯示調用will/didChangeValueForKey:方法。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容