面向過程:面向過程編成将所要解決的問題按解決問題的步驟進行分析。如果是大問題,就分解成為多個不同的小問題,在程式裡叫做劃分成不同的子產品。每一個解決的步驟可能是一行或者幾行代碼,也可能是一個函數。這樣把每一個步驟都實作或者解決掉,最後把所有的解決手段按順序進行調用,如果遇到一些意外情況就分情況處理,如果遇到重複的操作就循環處理,如此就完成了整個程式。
面向對象:以面向對象的方式去解決問題的思路,我們關注的往往不是解決問題的步驟。更多的是将問題所涉及的方面進行一些角色層次上的劃分。在每個角色上都有其特性,也有角色所要承擔的責任。厘清楚了這些之後,通過整合各個角色之間的行為互相配合,來最終解決問題。我們劃分問題解決中各個參與者的角色,并不是為了完成某一個步驟而是從所劃分的角色本身出發,描述其在整個問題解決過程中的行為。
面向過程的編成,步驟劃分是關鍵。而面向對象的編成,對象分析是關鍵。面向過程的編成,随着步驟的增多和步驟的複雜,程式的邏輯也将更加的複雜。而面相對象的編成,跟步驟無關,各個對象各司其職,職責清楚,并不會因步驟的複雜而複雜,至少複雜的程度并不像面向過程那麼深,并且更适合做大型問題的解決方案。面向過程編成中,是以過程、步驟、算法為驅動的,我們的程式邏輯跟這三點緊緊關聯。而面向對象的實作中,對象之間是以方法、消息來互相通信協調的,程式可以友善實作角色的分離和分層。
面向對象程式設計的特點:面向對象程式設計有三大特點,稱之為封裝性、繼承性和多态性。
封裝性:封裝性意味着至少三點。一是說我們的類或對象在外部看來是一個有資料和方法的整體,不是支離破碎的資料、行為、步驟,而是整體的對象,有對象然後才是對象的屬性和方法。二是說我們的類或者對象對外界存在一定的資訊隐藏,對内來設定哪些外界能看到,哪些看不到,對外,隻能看到能看到的,操作能操作到的。三是外界知道對象可以接受那些資訊,進行什麼樣的處理,但是具體是如何進的,外界不用知道也無從知道。
簡單點講,封裝性就是為了友善類的設計者對資訊進行隐藏,提高安全性。而對調用者而言,也減少了需要知道的内容,更不需要知道實作,減少了調用的複雜度。
繼承性:繼承性是說,在面向對象中,除非某個類已經顯式的指出不允許在其上做功能的擴充,否則我們有辦法對其進行擴充,不論這個類的源代碼是否為我們所掌握,我們都能根據現有的類來建立新類,擴充類的能力。
多态性:多态性指的是,由于不同的類進行了不同的封裝,它們公開出來的方法可能有同一個方法,但是外界調用同一個方法的時候,不同的類傳回不同的行為,這就是多态性。通過繼承過程中對同一方法的不同實作,就可以實作多态。
為什麼要使用面向對象程式設計:
總結來講,面向對象有如下優點:
1、易維護:采用面向對象思想設計的結構,可讀性高,由于繼承的存在,即使改變需求,那麼維護也隻是在局部子產品進行,是以維護起來非常的友善,成本也較低。
2、品質高:在設計時,可重用現有的、在以前的項目的領域中已經測試過的類,使系統滿足業務需求并具有較高的品質。
3、效率高:在軟體開發時,根據設計的需求對現實世界的事物進行抽象,産生類。使用這樣的方法解決問題,接近日常生活和自然的思考方式,勢必提高軟體開發到的效率和品質。
4、易擴充:由于繼承、封裝、多态的特性,自然設計出高内聚、低耦合的系統結構,使得系統更靈活、更容易擴充,而且成本較低。
類:類是一種抽象的概念,它實際上并不存在于現實中的時間和空間裡。在定義一個類時,我們就會在類中定義該類事物的特性和行為,從術語上講,特性叫類的屬性,行為叫類的方法。類是一個靜态的概念,類本身不攜帶任何資料。當沒有為類建立任何對象時,類本身不存在于記憶體空間中。
1)類與結構體的關系(在 Swift 語言中):
它們的共同點如下:
1、都可以定義一些可以指派的屬性。屬性的類型和寫法大多相同。當然也有不同,比如類型屬性的寫法,在類中用 class 字首,在結構體中用 static 字首修飾。
2、都可以定義具有功能性的方法。可定義方法類型也大多相同。但正如屬性,對于類型方法的定義,在類中用 class 字首,在結構體中用 static 字首修飾。
3、都可以定義下标腳本,使用下智語法。
4、都可以定義初始化方法來設定初始狀态,初始化屬性。
5、都可以做擴充實作。
它們的不同點如下:
1、在資料結構的擴充和繼承方面,類更加強大。
2、我們可以讓一個類的執行個體來反初始化,适時釋放存儲空間,但是結構體做不到。
3、類的對象是引用類型,而結構體是值類型。
從上面可以看出類、結構體在定義、初始化方法、屬性定義等方面幾乎完全相同,class 和 struct 關鍵字可以互換。但類的指派是傳引,而結構體則是傳值。兩個結構體,隻要屬性值都相同,我們就可以認為結構體相同,是同一個結構體。而兩個類的對象,光是屬性值相同,并不一定相同,不一定是類的同一個對象。
一般來說,下面的一個或多個條件滿足時,應當選擇建立一個結構:
1、結構主要是用來封裝一些簡單的資料值。
2、當複制或者傳遞的時候更希望這些封裝的資料被指派,而不是被引用過去。
3、所有被結構存儲的屬性本身也是數字類型。
4、結構不需要被另外一個類型繼承或者完成其他行為。
用類會是更好的選擇。也就是說一般情況下,資料都會定義為類。
2)類擴充: 在 Swift 語言中類擴充是一個強大的工具我們可以通過這個擴充文法完成如下事情:
1、給已有的類添加計算屬性和計算靜态屬性。
2、定義新的執行個體方法和類型方法。
3、提供新的構造器。
4、定義下标腳本。
5、使一個已有的類型符合某個協定。
在定義類擴充時,在類名前直接加字首 extension 。
3)類組合:在面向對象的設計原則中,有一條叫“組合優先于繼承”。組合就是在已有的類或者子產品上實作一個新功能時,可以通過在新的類裡實作新功能,同時在新的類裡,調用或者組合已有的對象,兩者聯合在一起實作功能。既是說一個類的某一屬性是另一個類的執行個體。
白箱複用:繼承時子類繼承父類,子類就擁有了父類所有相關的屬性和方法,也就是說父類的實作細節,對于子類其實是可見的。使用繼承的目的是為了代碼複用,這種複用方式叫白箱複用。
黑箱複用:組合是通過對象的方式通路已有的類的功能。在類中,可以封裝的比較徹底,類所暴露出來的接口和屬性是相當有限的,在調用時也不需要明白類的實作細節,這種複用方式叫黑箱複用。
4)類字首:
使用 Objective-C 開發 iOS 程式時,最好在每個類名前面加一個字首,用來辨別這個類。
目的是防止 N 個人開發了一樣的類,出現沖突。
比如 Jake Will、Kate Room 在同一個項目中都各自開發了個 Button 類,這樣的程式是不能運作起來的。
解決方案:Jake Will 的類名叫做 JWButton,Kate Room 的類名叫做 KRButton。
類字首的設定
Xcode 6 之後:
建立完項目後設定。

設定完後,再建立新的檔案時會自動添加上設定的類字首。
對象:對象是類的具體化的東西,從抽象整體中具體化出的特定個體。
對象是一個動态的概念。每一個對象都存在着有别于其他對象的屬于自己的獨特屬性和行為。對象的屬性可以随着他自己的行為的變化而改變,我們把具體的對象叫做類的執行個體化。
屬性:也叫類的成員變量。成員變量不能獨立于類而存在。成員變量是描述類的對象的狀态資料。
屬性就是類所表示的現實對象的特性在代碼中的反應。在 Swift 語言中屬性的類型分為兩種,存儲屬性和計算屬性。
1)存儲屬性:用來表示類的一個特性的屬性。正如變量和常量一樣,屬性也分為變量屬性和常量屬性,都叫存儲屬性。并且分别用關鍵字 var 和關鍵字 let 來描述。對于存儲屬性需要注意以下三點注意事項。
第一、存儲屬性在定義時,需要為每一個屬性定義一個預設值,當然也可以在初始化的時候設定屬性的初始值。如果初始化也不定義預設值,在調用 super.init() 時會出錯,或者在聲明了對象之後,也無法為此變量指派,除非屬性變量被明确定義為強制解包可選。
第二、存儲屬性中的常量屬性并不意味着絕對不能變化,而是要看情況。如果存儲屬性的常量屬性是值類型,比如字元串、結構體,就不能再變,結構體的屬性也不能再變。如果是類,那麼就可能重新指派。
第三、存儲屬性一般要求初始化,我們可以設定一個 lazy 修飾符,使得這種初始化延遲,盡管我們在屬性聲明時已經做了這種初始化上的定義。
所謂修飾符就是加在變量、函數、類等的定義前面,用來限制或者增強變量、函數、類功能的一種特殊的關鍵字。比如修飾符 objc 表示導出給 OC 調用。
lazy 修飾的存儲屬性叫懶惰存儲屬性(懶加載)。懶惰存儲屬性是指當他第一次被使用時才進行初值計算。懶惰存儲屬性必須是變量屬性(即用 var 定義的屬性),因為他的初始值直到執行個體初始化完成之後,在調用之時才被計算。常量屬性在執行個體初始化完成之前就應該被指派,是以常量屬性不能夠被聲明為懶惰存儲屬性。
在全局變量中不需要顯式的指定 lazy 屬性,而是所有的全局變量都是延遲計算的。
2)計算屬性:本身并不直接存儲特性,提供的是一個計算後的結果。計算屬性并不能直接的儲存資料,但是可以使用 getter 和可選的 setter 來間接的獲得或者改變其他的屬性和值。
上式中第一個 set 的方法也是簡寫方法,簡寫時,新賦的值預設為 newValue。而不簡寫時可以自定義變量。
有關計算屬性,有如下幾點注意事項:
第一、計算屬性(包含隻讀計算屬性)都應該使用 var 關鍵字,因為他們的值并不是固定的。
第二、使用計算屬性一定要聲明類型,否則編譯器會報錯。
3)屬性觀察者:屬性觀察者更像是觸發器,不過屬性觀察者同觸發器不同的是,還可以在屬性變更前觸發。屬性觀察者觀察屬性值的改變并對此作出響應。當設定屬性的值時,屬性觀察者就被調用,即使當新值同原值相同時也會被調用。除了懶惰存儲屬性,你可以為任何存儲屬性加上屬性觀察者定義。另外,通過重寫子類型屬性,也可以為繼承的屬性(存儲或計算)加上屬性觀察者定義。
屬性觀察者,有如下需要注意的幾點:
1、給屬性添加觀察者必須要聲明清楚屬性類型,否則編譯器報錯。
2、willSet 可以帶一個 newName 的參數,如果不這樣的話,正如計算屬性中一樣,這個參數就預設的被命名成 newValue。
3、didSet 也可以帶一個 oldName 的參數,表示舊的屬性,如果不帶,這個參數預設命名為 oldValue。
4、屬性初始化時,willSet 和 didSet 并不會被調用。隻有在初始化上下文時,當設定屬性值時才被調用。
5、willSet 和 didSet 每次設定屬性值都會被調用,即使是設定的值和原來的值相同。
4)類型屬性:可以定義一種屬性,這種屬性不屬于任何一個類的執行個體,即不屬于任何一個對象。即使建立再多的這個類的執行個體,這個屬性也不屬于任何一個。它隻屬于類本身,這樣的屬性就稱為類型屬性。我們在類中定義的時候,直接在關鍵字 var 前加上 class 關鍵字,同時要用 get 函數的設定方法傳回變量的值,而不能直接指派,因為目前不支援。
其實類型屬性也可以改寫,但是目前 Swift 語言還支援的不太好。
但可以使用一些折中的解決方案。
方法:另一個說法也叫成員函數,英文單詞為 method,而不是表示函數的單詞 function,是以叫方法。方法中定義的是類的行為。
1)執行個體方法:在類内部的定義和實作上看起來同函數沒有差別,不過是能直接通路類的屬性。在通路類的屬性時,直接引用屬性的名稱即可,當然也可以加一個 self 字首。
類的每一個執行個體都有一個隐含屬性 self。self 完全等同于該執行個體本身,可以在類的執行個體方法中,使用 self 來引用執行個體本身,進而獲得對執行個體其他屬性和方法的通路。正由于是隐含的,這個 self 大部分情況下都可以省略。但是在類中,存在同名局部變量的情況下,self 就不能省略。
2)類型方法:類型方法不屬于任何一個類的對象,而是屬于公共的類,可以通過類名直接調用。但是正是如此,他也不能調用變量屬性,因為使用類型方法,并不需要初始化類,沒有空間存儲對象,因而也并沒有空間來儲存變量。在類型方法的定義中,使用 self 是不允許的。類型方法的定義隻需在定義的 func 前面加上 class 即可。
3)初始化方法:是在類初始化為對象時調用的方法。在建立對象之後,使用對象的屬性之前,我們得做好相應的屬性初始化,否則在沒有初始化的前提下,就會報錯。遇到這種情況時,一種解決方案是在定義變量時就做好初始化,另一種解決方案就是使用初始化方法。
初始化方法,同普通方法不同,方法名是固定的 init,也沒有傳回值,方法名 init 前面也不加 func 關鍵字。初始化方法可以接受參數,init 初始化方法的參數,在建立對象時就可以傳入,不過同函數不太一樣的是,調用時,每一個參數的變量名都需要加上,而普通函數,第一個是不需要寫的。
初始化方法可以有多個,并且每一個在方法名上都一樣,都是 init,但是參數上要有所不同。同其他語言不同,Swift 語言中,對于方法參數的不同,不僅僅是通過參數類型來判定的,還涉及參數的名稱。兩個方法相同,盡管參數資料類型一樣,但是參數名不同,就是可以同時存在。
<1>、預設初始化方法:Swift 語言将為所有屬性已提供預設值的且自身沒有定義任何構造器的結構體的基類提供一個預設的初始化方法。
第一種情況,對存儲屬性都初始化了之後,可以用預設初始化方法執行個體化對象。
第二種情況,對類存儲屬性設定為可選存儲屬性,可以用預設初始化方法執行個體化對象。
第三種情況,對類存儲屬性設定為強制解包可選,可以使用預設初始化方法執行個體化對象。
<2>、便利初始化方法:便利初始化方法的出現正像他的名字,是為了便利我們調用而出現的。在他的實作裡面,必須調用一個其他的初始化方法,便利初始化方法,在 init 前面加上 convenience 關鍵字即可。這些調用獨立初始化方法的初始化方法,就叫便利初始化方法。
<3>、閉包初始化方法:如果某個存儲類型屬性的預設值需要特别定制或準備,你就可以使用閉包或全局函數來為其屬性提供定制的預設值。閉包後需要加“( )”,這裡用來告訴 Swift 需要立刻執行此閉包,否則會把閉包本身作為值賦給了屬性。
4)反初始化方法:我們假設一個場景,我要建立一個資料庫通路類,在初始化時打開連結,如果程式退出,連接配接不釋放,資源就會浪費。是以我們引入一種新的特殊方法,跟初始化方法相對,叫反初始化方法。反初始化方法的方法名為 deinit,在把對象設定為 nil 時,系統會自動調用反初始化方法。
5)重載:在類中,這種相同名字不同參數的方法的寫法有一個專門的術語來描述,叫做重載。在别的語言中,重載的情況隻出現在類定義裡,但是在 Swift 語言中,類中的執行個體方法可以重載,而全局的函數也可以重載,隻要參數名或者參數别名不同就可以實作重載,而不用關心參數資料類型的相同與不同。
1)封裝:封裝主要有兩大目的:一是為了我們使用資料更加友善。二是為了資料保護。
在 Swift 語言中,通路修飾符也分為三類,分别是 private、internal、public,并且從 Xcode6 beta4 才開始支援。Swift 對通路權限的控制,不是基于類的,而是基于檔案的。
在 Swift 語言中,若要設定某一屬性的通路權限,需在定義屬性時加上相應的修飾字首。如:private var name:String = "" ,修飾隻對這個屬性變量起作用。
private :所修飾的屬性或者方法隻能在目前 Swift 源檔案裡可以通路,在别的檔案裡通路會出錯。
internal:所修飾的屬性或者方法在源代碼所在的整個子產品都可以通路。是預設的通路權限。
如果是架構或者是庫代碼,則在整個架構内部可以通路,架構由外部代碼所引用時,則不可通路。如果是 App 代碼,在整個 App 内部可以通路。
public :所修飾的屬性或者方法對于引用了該檔案或者子產品的檔案來講,都能通路這些屬性和方法。
一般來說 public 的出現是為了 API 開發而設定。
2)繼承:由上至下,是一個具體化的過程,由下至上,是一個抽象化的過程。而繼承的概念就是在這種基礎上産生的。
我們使用“ : ”符号來表示繼承的關系。在定義類時的類名後加上“ : ”符号和基類名,表示目前定義的類繼承了該基類。
繼承後子類自動就獲得了父類的相關屬性和方法。
self 指向的是目前對象,而對于超類屬性和方法的通路,如果繼承自父類或祖先類的,就可以用 self 來通路。但在初始化方法中,我們調用父類的 init 方法時,隻能用 super 來指向和通路。
重寫(覆寫):如果子類和父類的方法、屬性重名,有一個專門的術語叫覆寫或者重寫。我們可以在子類中寫上同名,同樣參數和傳回值的方法,以重寫父類中的相應方法。參數一定要相同,否則就是重載,而不是重寫或者覆寫。
重寫方法或參數時需在方法的 func 前和屬性的類型 var 前加上 override 關鍵字。
防止父類中的某個方法被重寫,可以在父類中定義方法的時候在 func 前寫上 @final 關鍵字。這樣子類想覆寫時就會出錯。
3)多态:同樣的操作或者方法,不同的對象在執行時會出現完全不同的行為,這就叫多态。
多态是講具有同樣的父類的一批類之間,都重寫了父類的同一個方法實作自己的行為,但是表現出來則各不相同。多态屬于一種叫模闆模式的設計模式。
多态是針對不同對象調用同一個方法,能産生不一樣的結果。而重載是在同一個類内實作了多個同名的方法,重載針對同一個對象,同名的方法,參數不同,調用後結果相同不相同不關心。
我們有時實作一個類,并不想擁有該類的所有屬性和方法。甚至由多個類來實作同一個類,隻是想擁有同一個方法,不想獲得父類的具體實作。
Swift 語言中協定類似于在别的語言裡的接口,協定裡隻做了方法的聲明,包括方法名、傳回值、參數等這些資訊,而沒有具體的方法實作。這樣的話,類實作了該協定,也隻是獲得了一個方法的聲明,而不是詳細的實作。達到了幾個好處:一是隻實作了該實作的,子類隻知道了該知道的。其次,協定也可以作為資料類型來傳輸。這樣在傳遞資料時,我們不用關注對象的類型,提高了抽象層次和通用性,也就是可複用性。
Swift 語言中協定的定義方法用 protocol 關鍵字,類實作協定隻需在類名後加“ : 協定名”,類似于一個類繼承自某個協定。
協定本身可以繼承自另一個協定,當然不能繼承自另一個類,因為協定是不能有具體方法實作的,是以不能繼承自類,協定繼承自另一個協定,無非就是多了一個方法定義。
協定的方法前面可以加 @optional 構成可選方法,但值得注意的是,這個修飾符隻适用于通過導出給 OC 代碼調用的 Swift 代碼實作。