iOS項目開發過程中,是以不斷創建文件的形式進行着的。
創建得比較頻繁的文件類型是:
這兩個類型中創建的文件有:子類、分類、擴展、協議四種文件,如下:
這四類文件是頻繁創建的,我們來看一下各自分別的文件結構。認識下(常見的頭文件類型):
(一)@interface 類 <協議>
聲明一個類 遵守 某協議
(二)@interface 子類 : 父類
聲明一個類 繼承 某個父類
(三)@interface 集合類<元素類型> : 父類<元素類型>
聲明一個確定元素類型的集合類 繼承 某個確定元素類型的父集合類
(四)@interface 子類 : 父類 <協議>
聲明一個子類 繼承 某個父類 並且這個子類遵循 某協議
(五)@protocol 協議
聲明一個協議
(六)@protocol 協議 <協議>
聲明一個協議 並且合並其他協議
(七)@interface 類 (分類)
聲明關於某類 的分類
(八)@interface 類 (分類) <協議>
用一個類 的分類 來遵循 某些協議
(九)@interface 集合類 <元素類型> (分類)
聲明一個確定元素類型的集合類 的分類
(十)@interface 集合類 <元素類型> (分類) <協議>
用一個確定元素類型的集合類 的分類 來遵循 某些協議
(十一)@interface 子類 ()
聲明一個類 的擴展
(十二)@interface 子類 () <協議>
聲明一個類 的擴展 並且遵循某些協議
(十三)附加:用<>標明集合變量的元素類型或者標明遵守的協議
聲明一個確定元素類型 的集合類 的實例對象
聲明一個遵循 某些協議 的實例對象
為了將上述各種情況下的約束和“規矩”有清晰的了解,先對四個方面(繼承、分類、擴展、協議)的的內容有個熟悉的了解后,再回過頭來對上述各種情景做詳細的解讀。
關於類的繼承,通過谷歌搜索和CSDN平台的關鍵字“iOS 繼承”的搜索結果大致如下:
歸納起來,關於類的繼承需要熟練掌握的方面大致有:
(零)類的繼承究竟是什么
(一)繼承的優缺點
(二)如何取其精華去其糟粕的使用繼承
(三)繼承的實現方式
(四)多繼承的實現
(五)繼承的框架圖
(六)繼承與分類、擴展、協議相互的使用
(七)使用繼承還是分類
(八)屬性的繼承
(九)類方法、實例方法的繼承
(十)繼承、分類、擴展的區別
(十一)繼承的父類涉及到Xib文件
(十二)UI控件的繼承
比較雜亂,理解起來也是不成體系,上面的知識點用於查缺補漏還行,用於建立知識體系不要友好。
======================================
其實我們現在主要要理清楚的無非就是:類、協議、分類、擴展各自的作用,相互疊加后的效果。這個過程當中,圍繞的就是“屬性”、“方法(類方法、實例方法)、實例變量(各種范圍類型)”來進行討論。
======================================
首先,根據“格物”的思想,我們先去找到比較單一的,比較初始的物來探究。
“分類”和“擴展”都是對一個類的“補充”,因此它們兩者不能算是初始的,那就是類和協議了。通過查看父類的方法層層遞進,所有類的根類是NSObject,頭文件如下:
但是NSObject類后面還接受着一個NSObject協議,因此NSObject類不能稱之為單一、單純的物。NSObject協議的頭文件如下:
這樣看來,的確比較單純。因為就算剛剛我們從協議開始層層遞進,也會發現,這個過程中的協議基本都是屬於“合並協議”,長相就是如下:
當然也有其他比較單純的非合並協議,比如:
這樣看來,我們首先需要理解的應該是“協議”。然后圍繞的重點依然是上面所提到的:“屬性”、“方法(類方法、實例方法)、實例變量(各種范圍類型)”來進行討論。
創建一個協議文件可以發現,只有頭文件(.h文件),沒有實現文件。頭文件中的初始化內容如下:
根據OOP的封裝思想,實例變量是不應該寫入頭文件中的,因此,接下來考慮的就是將屬性和方法寫入協議中,究竟意味着什么?
首先協議中聲明屬性的情況肯定是有的哈,以圖為證:
定義好一個協議如下:
“協議”的含義其實就是約束,上面這個協議要表達的約束就是:凡是遵守這個協議的類必須要實現gameScore屬性的getter和setter方法,並且實現兩個方法,一個是實例方法,另一個是類方法。當一個類遵守這個協議時,從頭文件中看到的是什么呢?我令OJFMsgListVC這個類遵守該協議:
為了不出現警告或者是報錯,這個類的實現文件中要做出以下處理:
當然我們平時在編程使用系統框架時候,是看不到實現文件的,我將這個類的實現文件寫出來的目的是想讓大家知道,實現文件是如何與頭文件“對應”起來的。知道了它的實現文件,我們就可以放心大膽的使用OJFMsgListVC這個類了,比如:
這個過程下來,說明了一個什么樣的事實?
從表面上來看,如果一個類遵守某個協議,協議中的屬性和方法就“相當於”是這個類的頭文件中聲明的屬性和方法。
那么從更深一層的含義來說呢?我們可以這樣理解,一份協議是有其“自身的完整性”的、一份協議也許就是一種解決方案、一份協議就是一套規范。
從這個層面出發,一個類如果要遵守某個協議,其實不是那么簡單的。首先,你需要認真理解協議的含義,接下來就是在這個類的實現文件中將協議蘊含的意思表現出來,將協議內部的邏輯表現出來。(而不是簡簡單單的重寫下協議中屬性的getter和setter方法,簡單的重載下協議中的方法就行的,一定要實現協議要求的邏輯!這一點很關鍵)
--------------
既然協議對一個類有如此規范的約束效果,那我可不可以將一份完整的邏輯拆分為兩份關聯的邏輯呢?比如一份完整的邏輯需要十個方法相互調用來實現,但是我將其中的六個方法做成一份協議,讓遵守這個協議的類來實現這六個方法。當然可以,剛剛的那個做法不就是這樣的嗎,定義一份協議,讓OJFMsgListVC類遵守該協議,這樣在XYZProtolTestVC類中就可以有邏輯的調用OJFMsgListVC的遵守協議中的方法了。如果上面的代碼沒有讓你看出OJFMsgListVC和XYZProtolTestVC類是在做一份完整的邏輯,我將XYZProtolTestVC實現文件中的代碼寫成這樣:
這個就是“代理”的雛形。接下來我們推演代理的設計模式的代碼標准范式是如何得出的。
通過上面的代碼示例我們可以理解剛剛所說的“將一份完整的邏輯拆分為兩份關聯的邏輯”。然而,我們不是為了拆分而去拆分,將一部分邏輯拆分出去目的是讓另一個獨立的對象去處理,並且這個對象還是會和其他對象打交道的(而不是像上面的代碼一樣,msgListVC這個對象就直接在此創建,並在此銷毀)。也許可以這樣理解,另外一個獨立的對象,是可以有其他邏輯在身的。因此,這個獨立的對象,不需要,也沒必要在XYZProtolTestVC類中創建,只要被XYZProtolTestVC類的實例對象所引用就好了。(抱歉,我終於把這個“引用”表達出來了~),並且引用有一個好處就是,當這一份邏輯執行到某些情況下時,能夠很好找到另一半邏輯的實現位置。
因此出現了下面的代理的代碼范式:(對比圖)
------------
到目前為止,單純的協議和代理知識基本就已經討論完一部分了。引用文章中前面說的一句話
其實我們現在主要要理清楚的無非就是:類、協議、分類、擴展各自的作用,相互疊加后的效果。這個過程當中,圍繞的就是“屬性”、“方法(類方法、實例方法)、實例變量(各種范圍類型)”來進行討論。
這樣看來,我們只是把某個類遵守某個協議稍微說明白了。但是協議與繼承、與分類、與擴展相互疊加后的效果,還沒有開始。
通過文章提到的常見的頭文件類型,接下來需要討論的就是:
(1)@interface 類 <協議>
(2)@interface 子類 : 父類 <協議>
(3)@protocol 協議
(4)@protocol 協議 <協議>
(5)@interface 類 (分類) <協議>
(6)@interface 子類 () <協議>
所對應的就是:
(1)一個類遵守協議,這個基本上說清楚了。當然如果是遵守多個協議的話,那就是一個累加效果。同理。
(2)一個子類繼承父類,並且這個子類遵守某些協議。這種情況和第1中一樣,同理。
(3)聲明一個協議,上面基本說清楚了。
(4)這種寫法就是合並協議,這個新聲明的協議意味着,如果哪個類遵守了這個新協議,那么那個合並協議也同樣要遵守。
(5)一個遵守某些協議的分類。這種做法挺值得提倡。因為蘋果提出分類的目的也是減輕一個類的“負擔”。比如,讓某個協議的實現放置在這個類的分類中去實現。這樣也增加了代碼的可讀性。
(6)擴展遵守某些協議。這里會引發出一個討論,協議的遵守聲明應該放在頭文件,還是放置在實現文件中的擴展聲明中呢?
另外,由於協議中聲明了屬性和方法,如果協議中的屬性名或者方法名與類中、分類中、合並協議中發生重名怎么辦?其實沒有關系。因為協議的表層約束就是使遵守協議的類或者是分類中要實現協議中屬性的getter和setter方法,實例方法和類方法。只要實現文件中實現了這些方法,就滿足了協議所帶來的的要求,所以,重名問題上沒關系。
===========================
接下來就可以討論類的相關問題了。
===========================
關於類,圍繞的重點依然是上面所提到的:“屬性”、“方法(類方法、實例方法)、實例變量(各種范圍類型)”來進行討論。
首先我們看看比較單純簡單的類,根類有NSObject和NSProxy,通過查看頭文件會發現,雖然這兩個根類沒有父類,但是這兩個根類都遵守NSObject協議。這樣看來,我們最開始格物的對象應該就是下面這個了:
觀望類的頭文件,一般就是屬性和方法的聲明。進一步講,聲明屬性無非就是聲明了兩個方法:getter和setter方法,因此可以認為類的頭文件就是方法的聲明。這個也很符合OOP變成的思想,頭文件只聲明方法,其他對象想讀取或者設置實例對象的狀態都必須通過方法。
類的實現文件中除了頭文件聲明方法的實現之外,也有其他方法的實現和實例變量的聲明、屬性的聲明。
===============================
我突然想換一種方式講解后面的內容了。文章的開始花了很多時間把“協議”、“代理”講解清楚了,接下來主要是把類、分類、擴展這些內容表述清楚。
我接下來的思路就是:先把屬性、實例變量(聲明、范圍說明)、方法(類方法、實例方法)進行一個講解,然后就是把分類和擴展也進行分別的講解,最后,將類的繼承、類的分類、類的擴展三者揉在一起進行推演。最終的目的是希望充分的理解Objective-C這門語言設計「類、分類、擴展、協議」的良苦用心。
===============================
首先,一個類最基本的就是:變量 + 方法。
這里的變量就是OC語言中的成員變量、實例變量。
如果將成員變量在頭文件中聲明,成員變量被訪問的權限有三種:
(1)@public:公共
本類(包括分類中)、子類(包括子類的分類中)、外部類都可以直接訪問。
(2)@protected:保護(默認)
本類(包括分類中)、子類(包括子類的分類中)可以直接訪問,外部類不可以直接訪問。
(3)@private:私有
本類(包括分類中)可以直接訪問,子類(包括子類的分類中)、外部類不可以直接訪問。
如果將成員變量在實現文件中聲明,成員變量只能在本類的實現文件中訪問。
大家可以將上面的結論用代碼驗證下,注意成員變量的訪問符是“->”。
---------------------------------------
然后,方法有類方法和實例方法兩種,類方法也稱之為靜態方法。區別如下:
如果方法在頭文件中聲明的,那么:
本類、本類的分類、子類、子類的分類中都是可以調用這個方法的。
如果方法沒有在頭文件中聲明,只是在實現文件中實現出來,那么這個方法只能在本類的實現文件中被調用。
---------------------------------------
將上面的成員變量和方法理清楚以后,再來看屬性,就會清晰很多了。因為“屬性”只是一種語言特性,“點語法”也是一種語言特性,是一種語法格式。
聲明一個屬性無非就是三點:聲明一個私有的帶下划線的成員變量,聲明getter和setter方法。
如果在頭文件中聲明一個屬性name,結合上面講解的成員變量和方法,那么成員變量_name只能在本類的實現文件中被訪問。-name和-setName:方法,本類、本類的分類、子類、子類的分類中都是可以調用的。
---------------------------------------
=未完,待續=
參考:
【1】https://www.jianshu.com/p/1c315df9d6ff(iOS中實例變量與屬性的區別)
【2】https://www.jianshu.com/p/e4d278e5a254(同時重寫getter和setter方法時報錯)
【3】https://blog.csdn.net/songchunmin_/article/details/51479866(@synthesize和@dynamic的區別)
【4】
【5】