好吧,讓我們從頭來過。什麼是物件程式設計程式設計呢?

先讓我們從上往下理解:人是一個喜歡歸類的動物,所以有生物學上的綱目。綱,即是我們程式設計裡的基類(也叫父類);目,即是我們程式設計裡的派生類(也叫子類)。基類與派生類是相對而言的,但跟綱目一樣,都是以相同特徵、行為來劃分的集合,同時派生類是基類特徵、行為的延伸與拓展。特徵在這裡叫屬性,行為在這裡叫方法,這也是類的基本。另外,同生物學上的趨同進化一樣,不同的類需要共同的方法時,我們使用了協議。所以協議是一種誇類別的同一行為。

再讓我們從下往上理解:當多行語句需要重複使用,我們把它們歸集在一起,稱為

函式

。當多個函式需要聯合使用時,我們又把它們歸集在一起,有的稱為

,有的稱為

。單說類這邊,為了有所區別,只好稱類裡面的函式為

方法

,共用的變數為

屬性

。到了這一步,我們只能基於集合(類)呼叫方法與屬性了,稱為

封裝

;而有些方法、屬性只是用於內部流程,所以有多事的分了

私有

公有

;用著用著,感覺以前的函式、變數能全域性範圍裡直接使用也挺好,於是來一個

靜態方法、靜態變數

;當類多了起來,又看到某些類有共同部分,於是又把它歸集出來,稱為

基類

;剩下各自不同的部分稱為

派生類

;派生類不想把相同的部分都寫上,於是用

繼承

;即然它們有共同的部分,派生類一定範圍上可作基類用,稱為

多型

;基類的部分方法只做約定,於是有了

抽象方法;

有抽象方法的類叫

抽象類;

忽然發現某派生類繼承的方法不適當怎麼辦?那重寫唄,所以有

重寫

;一個類不僅與甲有相同部分還與乙也有相同部分,怎麼辦?那就來個

多繼承

;哎呀呀,多繼承又出錯了,怎麼辦?精簡,跟乙的相同部分不能太具體,好,就這樣定了,再改過名字稱為

協議

。同樣的事情要針對不同的資料型別寫好幾個方法稱為

過載;

這樣太麻煩了,搞出個

泛型……。

之如此類,所以我一直認為類是過度集合的產物。

新的程式語言,有一些不再拘泥面向物件程式設計,它們同時支援函數語言程式設計與面向物件程式設計。Swift是其中之一,所以我們是幸福的。

到這裡,我們將開始軍團化作戰訓練。你沒看錯,面向物件程式設計,就是程式開發中的軍團化作戰。所以你就是那未來的海陸空三軍(Enumeration 列舉、Structure 結構體、 Class 類)總司令。矮油,統一全宇宙的任務就交給你了。

所以,這一章我們將學習Enumeration 列舉、Structure 結構體、 Class 類。讓我們一起加油吧!

慢著,如果你有面向物件程式設計的開發經驗,你可能只需要瞭解下面12點。其中Enumeration 列舉有4點,Structure 結構與Class 類有6點,其餘的有2點。

Enumeration 列舉、Structure 結構體,是值型別,Class是引用型別。值型別定義為常量後不能修改其元素值,引用型別則可以。

Structure 結構體、Class 類,最大的不同之處在於前者不能繼承,其餘的在應用上基本相同。

泛型、協議上與其它語言沒什麼區別。

Enumeration 列舉可定義為不同型別。

enum

Direction1

{

case

east

south

west

north

}

// print(Direction1。north。rawValue)

// Value of type ‘Direction1’ has no member ‘rawValue’

enum

Direction2

Int

{

case

east

=

1

south

west

north

}

print

Direction2

north

rawValue

// Prints 4

enum

Direction3

String

{

case

east

south

west

north

}

print

Direction3

north

rawValue

// Prints north

2. Enumeration 列舉可以巢狀。

enum

LoopDoll

{

enum

Disneyland

{

case

A

case

B

}

enum

Barbie

{

case

X

case

Y

}

}

3. Enumeration 列舉可使用引數,這一點極大豐富了列舉的應用。

enum

Name

{

case

first

String

case

last

String

}

var

name

=

Name

first

“Jiang”

switch

name

{

case

first

let

s

):

print

“Your first name is

\(

s

case

last

let

s

):

print

“Your last name is

\(

s

}

// Prints Your first name is Jiang

4. Enumeration 列舉可使用遞迴 ,方法是:列舉使用的引數,設定其型別為當前列舉型別。

enum

Climb

{

case

level

Int

indirect

case

upper

Climb

}

func

motion

_

climb

Climb

)->

Int

{

switch

climb

{

case

level

let

i

):

return

i

case

upper

let

start

):

return

motion

start

+

1

}

}

let

level

=

Climb

level

12

let

upper

=

Climb

upper

level

let

m

=

motion

upper

print

m

// print 13

5. Structure 結構體 、Class 類,使用init作建構函式,使用self指例項自身。

struct

Student

{

let

name

String

init

name

String

){

self

name

=

name

}

}

6. Structure 結構體 、Class 類, init方法可過載,只需要引數名不同即可。init方法中過載的需要呼叫Designated的,其中Class 類中需要使用關鍵詞convenience.

struct

Student

{

let

name

String

init

name

String

){

// Designated

self

name

=

name

}

init

alias

String

){

// convenience

self

init

name

alias

}

}

class

Student

{

let

name

String

init

name

String

){

// Designated

self

name

=

name

}

convenience

init

alias

String

){

// convenience

self

init

name

alias

}

}

7. Structure 結構體 、Class 類,不初始化的變數需添可選值符號“?”。未標記的則需在初始化時賦值。

struct

Student

{

let

name

String

var

arg

Int

// 可選值符號

init

name

String

){

self

name

=

name

}

}

8. Structure 結構體 、Class 類,支援subscript 下標語法。使用字典或陣列的方式賦值與取值。

class

City

{

var

province

String

var

info

Dictionary

<

String

String

>?

init

province

String

){

self

province

=

province

self

info

=

[:]

}

// 這裡是使用下標語法

subscript

city

String

->

String

{

get

{

return

self

info

?[

city

??

“”

}

set

{

self

info

?[

city

=

newValue

}

}

}

let

city

=

City

province

“HuBei”

city

“Daye”

=

“Daye is a top 100 city”

print

city

province

// Prints HuBei

print

city

“Daye”

])

// Prints Daye is a top 100 city

9. Structure 結構體 、Class 類, 支援Extension 拓展。使用關鍵詞extension對已有結構體與類實現新的功能。

extension

Int

{

func

add

_

number

Int

)->

Int

{

return

self

+

number

}

}

print

8。

add

10

))

// Prints 18

10. Structure 結構體 、Class 類,支援Delegate 委託 、 Protocol 協議。Delegate 委託在實際開發中需要使用,這裡介紹一下。

/** 1。 定義一下協議 **/

protocol

MyDelegate

{

func

doSomething

str

String

->

()

}

/** 2。 應用這個協議 **/

class

Part

{

var

delegate

MyDelegate

func

show

()

{

delegate

?。

doSomething

str

“Hello world!”

}

}

/** 3。 實現這個協議 **/

class

Layout

MyDelegate

{

let

part

Part

init

(){

self

part

=

Part

()

part

delegate

=

self

}

func

doSomething

str

String

{

print

“Layout: ”

+

str

}

}

let

layout

=

Layout

()

layout

part

show

()

// Prints Layout: Layout: Hello world!

11. Optional Chaining 可選鏈,使用“?”、“!”。這個非常強大。“?”表示:沒有返回nil,有返回值。“!”表示:確定有,沒有返回系統錯誤。

let

result

=

“data”

:[

“user”

:[

“name”

“Jiang Youhua”

]]]

if

let

name

=

result

“data”

]?[

“user”

]?[

“name”

{

print

“Welcome to

\(

name

}

else

{

print

“Result is error”

}

// Prints Welcome to Jiang Youhua

12. as,is 。as的作用:從子類物件轉換為父類物件,向上轉型使用;消除二義性,數值型別轉換。is的作用:判斷某個物件是否是某個特定類的物件。

let

result

Dictionary

<

String

Any

>

=

“code”

1

“info”

“succeed”

“data”

:[

“id”

1

“username”

“Jiang Youhua”

“role”

1

]]

if

let

data

=

result

“data”

as

String

Any

{

let

name

=

data

“username”

as

String

print

“Welcome to

\(

name

}

else

{

print

“Json turned to Dictionary error”

}

// Prints Welcome to Jiang Youhua

好,我們還是假裝以前沒學過程式設計,較細緻的來一遍。主要涉及Enumeration 列舉、Structure 結構體、Class 型別。在Swift中Enumeration 列舉、Structure 結構體經過了強化,能承擔更多的工作。

一、Enumeration 列舉,列舉是有並列特性的資料的集合。

Enumeration 列舉最原始的作用是:讓資料具有可讀性。透過下面示例可以看出列舉在這方面的優勢。

/** 定義方向列舉 **/

enum

Direction

{

case

east

// 東方

case

south

// 南方

case

west

// 西方

case

north

// 北方

}

/** 應用列舉 **/

let

direction

=

Direction

south

switch

direction

{

case

east

print

“Direction in the east”

case

south

print

“Direction in the south”

case

west

print

“Direction in the west”

case

north

print

“Direction in the north”

}

// Prints Direction in the south

Enumeration 列舉在Swift支援不同的資料型別。這個可能是為支援引數化與巢狀的產物,否則我沒看出其有積極意義。

/** 定義Int值的列舉 **/

enum

Direction1

Int

{

case

east

=

1

south

west

north

}

print

Direction1

north

rawValue

// Prints 4

// 定義Int值的列舉,後續元素按順序賦值。

/** 定義String值的列舉 **/

enum

Direction2

String

{

case

east

south

west

north

}

print

Direction2

north

rawValue

// Prints north

// 定義String值的列舉,元素值為元素名的字串。

Enumeration 列舉巢狀,巢狀讓列舉有了多重歸集的功能。

/** 列舉巢狀 **/

enum

LoopDoll

{

enum

Disneyland

{

case

A

case

B

}

enum

Barbie

{

case

X

case

Y

}

}

let

doll

=

LoopDoll

Barbie

X

// doll is X

Enumeration 列舉支援元素帶引數,我把它近似的理解成函式型別。

/** 列舉支援元素帶引數 **/

enum

Barcode

{

case

upc

Int

Int

Int

Int

case

qrCode

String

}

/** 引數在列舉中傳遞 **/

let

productBarcode

=

Barcode

qrCode

“ABCDEFGHIJKLMNOP”

switch

productBarcode

{

case

upc

let

numberSystem

let

manufacturer

let

product

let

check

):

print

“UPC:

\(

numberSystem

\(

manufacturer

\(

product

\(

check

。”

case

qrCode

let

productCode

):

print

“QR code:

\(

productCode

。”

}

// Prints QR code: ABCDEFGHIJKLMNOP。

Enumeration 列舉支援元素帶引數,並形成遞迴應用。這一個要仔細看,有點繞。

/** 遞迴列舉 **/

enum

ArithmeticExpression

{

case

number

Int

indirect

case

addition

ArithmeticExpression

ArithmeticExpression

indirect

case

multiplication

ArithmeticExpression

ArithmeticExpression

}

// indirect 可以不寫, 可以認為number, addition, multiplication帶了不同的資料

let

five

=

ArithmeticExpression

number

5

let

four

=

ArithmeticExpression

number

4

let

sum

=

ArithmeticExpression

addition

five

four

let

product

=

ArithmeticExpression

multiplication

sum

ArithmeticExpression

number

2

))

func

evaluate

_

expression

ArithmeticExpression

->

Int

{

switch

expression

{

case

let

number

value

):

// 路由1

return

value

case

let

addition

left

right

):

// 路由2

return

evaluate

left

+

evaluate

right

case

let

multiplication

left

right

):

// 路由3

return

evaluate

left

*

evaluate

right

}

}

let

i

=

evaluate

product

print

i

// Prints 18

// 解釋:

// 1。 根據“路由1” 得到evaluate(。number(value)) = value。

// 2。 根據product的值,確定函式進入“路由3“,返回evaluate(left) * evaluate(right)。同時確定left=sum right=。number(2)。

// 3。 根據解釋1和right=。number(2), 得出right = 2,

// 4。 根據sum的值,確定函式進入“路由2”,返回evaluate(left) + evaluate(right)。同時確定left=five right=four

// 5。 根據解釋1和left=five, right=four, 得出five = 5, four = 4。

// 6。 根據解釋4,確定sum = 5 + 4, 所以得出sum = 9

// 7。 根據解釋2、6、3, 確定evaluate(product) = 9 * 2, 所以 i = 18

二、Structure 結構體 、 Class 類、Protocol協議。

Structure 結構體定義與呼叫。

/** Structure 結構體 **/

struct

Point

{

let

x

Int

// 屬性

let

y

Int

init

x

Int

y

Int

){

// 構造方法

self

x

=

x

self

y

=

y

}

func

out

(){

// 方法

print

“Point。x is

\(

self

x

, point。y is

\(

self

y

}

}

let

point

=

Point

x

120

y

200

// 例項化

point

out

()

// 呼叫結構體的方法

// Prints Point。x is 120, point。y is 200

Class 類定義與呼叫。

/** Class 類 **/

class

Point

{

let

x

Int

// 屬性

let

y

Int

init

x

Int

y

Int

){

// 構造方法

self

x

=

x

self

y

=

y

}

func

out

(){

// 方法

print

“Point。x is

\(

self

x

, point。y is

\(

self

y

}

}

let

point

=

Point

x

120

y

200

// 例項化

point

out

()

// 呼叫類的方法

// Prints Point。x is 120, point。y is 200

Protocol 協議定義。協議等待著被實現。

/** Protocol 協議 **/

protocol

Point

{

var

x

Int

{

set

get

}

// 屬性

var

y

Int

{

set

get

}

func

out

()

// 方法

}

Structure 結構體、Class 類,結構體與類的共同點:

Properties 屬性。可以有屬性,等同於變數與常量。

Methods 方法。可以有方法,等同於函式。

Subscripts 下標語法。支援下標語法,類似於陣列、字典的元素賦值與取值。

Initialization 構造 。可以有建構函式,如果未定義,則系統分配init()作建構函式。

Extensions 拓展。支援拓展,可向一個已有的類、結構體新增新功能。

Protocols 協議。支援實現協議。

下示例中用來說明Structure 結構體與Class 類相同部分。可將嘗試執行後,再將關鍵字struct換為class執行。

/** 協議 **/

protocol

ManProtocol

{

var

age

Int

{

get

set

}

func

say

anything

String

}

/** 結構體與類相同的部分,可將關鍵詞struct換為class **/

struct

Student

ManProtocol

{

// 支援實現協議

var

age

Int

// 有屬性

let

name

String

var

school

String

{

// 只讀屬性

return

“Experimental primary school”

}

var

attribute

Dictionary

<

String

Any

>

init

name

String

age

Int

){

// 有init建構函式

self

name

=

name

self

age

=

age

self

attribute

=

[:]

}

subscript

key

String

->

Any

{

// 支援下標語法

get

{

return

self

attribute

key

??

0

}

set

{

self

attribute

key

=

newValue

// 系統提供,相當於set(key, value)的value

}

}

func

say

anything

String

){

// 有方法

print

\(

self

name

said that

\(

anything

}

}

extension

Student

{

// 支援拓展

func

about

()

{

print

“My name is

\(

self

name

}

}

var

student

=

Student

name

“Jiang Youhua”

age

18

// 初始化應用

student

say

anything

“who you are”

// Prints Jiang Youhua said that who you are

student

“gender”

=

“man”

// 下標語法應用

let

gender

=

student

“gender”

// gender is man

student

about

()

// 拓展的方法應用

// Prints My name is Jiang Youhua

Class 類,特有的特徵:

Inheritance 繼承,類有可繼承性。

Deinitialization 析構,透過解構函式來實現,類例項消毀時呼叫。

Type Casting 型別轉換,判斷例項的型別,透過is, as將其看做是父類或者子類的例項。

Automatic Reference Counting 自動引用計,所以納入了自動記憶體管理機制。

// 示例為讀取配置檔案的類。將檔案資料轉為配置字典未實現。

/** 基類,獲取配置 **/

class

Config

{

var

setting

Dictionary

<

String

Any

>

var

path

String

init

path

String

){

self

path

=

path

self

setting

=

[:]

}

subscript

key

String

)->

Any

{

get

{

return

self

setting

key

??

0

}

set

{

self

setting

key

=

newValue

}

}

func

formatConfig

(){

print

“This is base class”

}

}

/** 派生類,通ini檔案獲取配置 **/

class

IniConfig

Config

{

// 繼承

var

fd

Int32

//檔案描述符

override

func

formatConfig

()

{

// 支援覆蓋

let

ret

=

open

self

path

O_RDONLY

// 讀取檔案,保留控制代碼

if

ret

==

-

1

{

fd

=

nil

}

else

{

fd

=

ret

}

// TODO // 讀檔案內容山,轉為配置引數,這裡未實現

print

“This is derived class”

}

deinit

{

// 支援解構

if

let

oft

=

fd

{

close

oft

// 關閉檔案控制代碼

}

print

“Into IniConfig。deinit()”

}

}

var

config

=

Config

path

“config”

config

formatConfig

()

// Prints This is base class

if

config

is

IniConfig

{

print

“config is the base class instance”

// is 的使用,判斷是否為該類物件

}

else

{

print

“config is not the base class instance”

}

// Prints config is not the base class instance

IniConfig

path

“config。ini”

as

Config

)。

formatConfig

()

// as 向上轉型,PlayGround中無效,

// Prints This is derived class // 呼叫子類方法

// Prints Into IniConfig。deinit() // 未賦給變數,所以及時在之記憶體中釋放,呼叫了解構函式

當你不需要繼承,Swift建議你使用Structure替代Class,這樣效能更高。

除了上面大方向的相同與不同,其實在編寫時還存在下面一些相同與不同點:

當Structure 結構體、Class 類,沒有實現建構函式時,系統隱式的提供一個無引數的建構函式。

在Structure 結構體、Class 類中,以let定義的屬性,可以在構造函數里再賦值。我喜歡這個。

在Structure 結構體、Class 類中,不想被初始化的屬性,需要使用var定義,並在最後加上“?”。

在Structure 結構體、Class 類中,系統為subscript賦值,提供關鍵詞“newValue“,表示set(key, value)中發value。

結構體例項賦予常量時,例項的屬性值不能改變,而類則可以。

結構體在沒有建構函式時也能使用引數進行例項,而類不可以。

結構體內部方法給其或其屬性賦值時,該方法需要新增”mutating“限定詞,

Enum列舉也一樣

Structure 結構體的應用例項:

struct

Point

{

let

x

Double

// 相同點:常量可以初始仳時賦值

let

y

Double

var

z

Double

// 相同點:不想在初始化的值需要加“?”

init

x

Double

y

Double

{

self

x

=

x

self

y

=

y

}

init

x

Double

y

Double

z

Double

){

// 相同步:支援多建構函式

self

init

x

x

y

y

self

z

=

z

}

}

extension

Point

{

// 相同步:支援拓展

init

width

Double

height

Double

){

// 不同點:convenience 系統提供 Struct不需要

self

init

x

height

y

width

}

}

struct

Rect

{

var

width

=

0。0

var

height

=

0。0

/** area屬性是Read-Only,只能讀,不能賦值 **/

var

area

Double

{

return

width

*

height

}

var

origin

Point

{

get

{

return

Point

x

height

/

2

y

width

/

2

}

set

{

width

=

newValue

y

*

2

// 相同點 newValue 系統提供

height

=

newValue

x

*

2

}

}

/** 內部方法改其屬性值時,需要新增mutating限定詞 **/

mutating

func

moveBy

width

Double

height

Double

{

// 不同點 mutating 系統提供,Struct、Enum需要

self

width

=

width

self

height

=

height

}

}

let

p

=

Point

x

15

y

15

// p。z = 20 // 不同點 常量p,Struct不能改屬性值

// Cannot assign to property: ‘x’ is a ‘let’ constant的

var

square

=

Rect

width

90。0

height

150。0

// 不同點 沒有建構函式,也能使用引數進行例項

print

square

origin

x

square

origin

y

square

width

square

height

// Prints 75。0 45。0 90。0 150。0

square

origin

=

p

print

square

origin

x

square

origin

y

square

width

square

height

// Prints 15。0 15。0 30。0 30。0

class 類的應用例項。

class

Point

{

let

x

Double

// 相同點:常量可以初始仳時賦值

let

y

Double

var

z

Double

// 相同點:不想在初始化的值需要加“?”

init

x

Double

y

Double

{

self

x

=

x

self

y

=

y

}

convenience

init

x

Double

y

Double

z

Double

){

// 相同步:支援多建構函式

self

init

x

x

y

y

self

z

=

z

}

}

extension

Point

{

// 相同步:支援拓展

convenience

init

width

Double

height

Double

){

// 不同點:convenience 系統提供 Class需要

self

init

x

height

y

width

}

}

struct

Rect

{

var

width

=

0。0

var

height

=

0。0

/** area屬性是Read-Only,只能讀,不能賦值 **/

var

area

Double

{

return

width

*

height

}

var

origin

Point

{

get

{

return

Point

x

height

/

2

y

width

/

2

}

set

{

width

=

newValue

y

*

2

// 相同點 newValue 系統提供

height

=

newValue

x

*

2

}

}

/** 內部方法改其屬性值時,需要新增mutating限定詞 **/

mutating

func

moveBy

width

Double

height

Double

{

// 不同點 mutating 系統提供,Struct、Enum需要

self

width

=

width

self

height

=

height

}

}

let

p

=

Point

x

15

y

15

p

z

=

20

// 不同點 常量p,Class能改屬性值

// Cannot assign to property: ‘x’ is a ‘let’ constant的

var

square

=

Rect

width

90。0

height

150。0

// 不同點 沒有建構函式,也能使用引數進行例項

print

square

origin

x

square

origin

y

square

width

square

height

// Prints 75。0 45。0 90。0 150。0

square

origin

=

p

print

square

origin

x

square

origin

y

square

width

square

height

// Prints 15。0 15。0 30。0 30。0

class 類的繼承,Designated、Convenience、Required。

Designated:是Swift走向有序化的最重要的一步。保證初始化時非選值屬性被賦值。

Convenience:一種便捷方式。原則,是需要呼叫本類的Designated的初始化方法。

Required:確定該初始方法一定要在派生類中重寫。

/** 基類 **/

class

Fish

{

let

weight

Double

var

length

Double

// a designated initializer。

init

weight

Double

{

self

weight

=

weight

}

// every subclass of the class must implement that initializer

// A convenience initializer must call another initializer from the same class。

// A convenience initializer must ultimately call a designated initializer。

required

convenience

init

weight

Double

length

Double

){

self

init

weight

weight

self

length

=

0。01

}

func

move

(){

print

“move fast”

}

}

/** 派生類 **/

class

Clownfish

Fish

{

var

color

String

// Setting a Default Property Value with a Closure or Function

var

age

UInt

=

{

return

8

}()

// A designated initializer must call a designated initializer from its immediate superclass。

init

weight

Double

color

String

){

self

color

=

color

super

init

weight

weight

}

// a convenience initializer。

required

convenience

init

weight

Double

length

Double

{

self

init

weight

weight

color

“red”

self

length

=

0。01

}

func

sound

(){

print

\(

self

weight

kg of

\(

self

color

fish is called zi~~~”

}

// 覆蓋了基類的方法

override

func

move

()

{

print

\(

self

weight

kg of

\(

self

color

fish move slow”

}

// 解構函式

deinit

{

// TODO

}

}

let

clown

=

Clownfish

weight

0。02

color

“blur”

clown

move

()

clown

sound

()

// Prints 0。02 kg of blur fish move slow

// Prints 0。02 kg of blur fish is called zi~~~

協議與委託。

我們定義了一個聊天的元件,它將應用於聊天室的頁面上。

我們需要像下面這樣定義委託,才能使用元件上輸入的資訊顯示在頁面上。

/** 1。 定義一下協議 **/

protocol

ChatDelegate

{

func

submitContent

str

String

->

()

}

/** 2。 應用這個協議 **/

class

ChatPart

{

var

delegate

ChatDelegate

func

show

word

String

{

delegate

?。

submitContent

str

word

}

}

/** 3。 實現這個協議 **/

class

ChatRoom

ChatDelegate

{

let

part

ChatPart

init

(){

// 新增元件到頁面上

self

part

=

ChatPart

()

// 在頁面上監聽該元件的委託

part

delegate

=

self

}

// 實現委託定的協議方法,用來處理元件傳來的資料

func

submitContent

str

String

{

print

“Layout: ”

+

str

}

}

let

room

=

ChatRoom

()

room

part

show

word

“Hi, Jiang Youhua”

// Prints Layout: Hi, Jiang Youhua

init?、init! 可能失敗的初始化。

init? 表示可失敗初始化器,失敗的時候,return nil。

init! 表示可失敗初始化器,只不過這個失敗要求觸發斷言。

import

MySQL

class

DB

{

let

mysql

Database

init

?(){

do

{

mysql

=

try

Database

host

“localhost”

user

“root”

password

“root”

database

“test”

try

mysql

execute

“SELECT @@version”

}

catch

{

print

“Unable to connect to MySQL:

\(

error

return

nil

}

}

}

let

db

DB

=

DB

()

// 這個db可能是空

Property Observer 屬性觀察員,提代兩個方法willSet、didSet來監聽屬性值的變化。

class

observer

{

var

number

Int

=

0

{

willSet

value

{

print

“~~~”

value

number

}

didSet

{

print

“。。。”

number

oldValue

// oldValue 系統提供

}

}

}

let

obj

=

observer

()

obj

number

=

10

// Prints

// ~~~ 10 0

// 。。。 10 0

static 靜態屬性,理論是一種全域性資料,透過定義的名稱來呼叫。

struct

SomeStruct

{

static

var

store

=

“Some value。”

static

var

compute

Int

{

return

1

}

}

enum

SomeEnum

{

static

var

store

=

“Some value。”

static

var

compute

Int

{

return

6

}

}

class

SomeClass

{

static

var

store

=

“Some value。”

static

var

compute

Int

{

return

27

}

}

print

SomeStruct

store

SomeStruct

compute

// Prints Some value。 1

print

SomeEnum

store

SomeEnum

compute

// Prints Some value。 6

print

SomeClass

store

SomeClass

compute

// Prints Some value。 27

private 、public、open,修飾類的屬性與方法。

private 當前類內使用

public 可以在其他作用域被訪問,但是不能在override、extension中被訪問

open 可以在其他作用域被訪問

三、一些語法糖特徵:

lazy 延遲載入,用的時候才賦值(分配記憶體),相當於這個事就這麼定了,到用的時間再處裡。

let

numbers

=

1。

。。

3

/** 常規 **/

let

map1

=

numbers

map

{

i

Int

->

Int

in

print

“。。。”

i

return

i

+

3

}

// Prints

// 。。。 1

// 。。。 2

// 。。。 3

/** lazy,這事就這麼定了,沒有處理 **/

let

map2

=

numbers

lazy

map

{

i

Int

->

Int

in

print

“~~~”

i

return

i

+

3

}

// 沒有被呼叫,所以沒有輸出

/** 遍歷map1 **/

for

i

in

map1

{

print

“。。。”

i

}

// Prints

// 。。。 4

// 。。。 5

// 。。。 6

/** 遍歷map2, 現在一起處理吧 **/

for

i

in

map2

{

print

“~~~”

i

}

// Prints

// ~~~ 1

// ~~~ 4

// ~~~ 2

// ~~~ 5

// ~~~ 3

// ~~~ 6

可選型鏈。

使用“?”,這是我最喜歡的語法糖。特別是從Go的JSON解析中走出來的人,在這裡再也不用一層層去判斷了。

“?”是判斷行不行,不行就返nil,行就繼續。“!”是一定可以的,不行則返回系統錯誤。

/** 多層的Dictionary, 用Go裡想死的心都有,在這裡就簡單多了,那一層沒有,就直接回空。 **/

var

countrys

=

“china”

“hubai”

“daye”

“ID1997”

:[

“name”

“JiangYouhua”

]]]]];

if

let

name

=

countrys

“china”

]?[

“hubai”

]?[

“daye”

]?[

“ID1997”

]?[

“name”

]{

print

“Your name is

\(

name

}

else

{

print

“Without your name”

}

// Prints Your name is JiangYouhua

/** 類裡的應用 **/

class

Stationery

{

let

name

String

init

name

String

{

self

name

=

name

}

}

class

Student

{

let

name

String

init

name

String

){

self

name

=

name

}

var

stationery

Stationery

}

let

student

=

Student

name

“Jiang YouHua”

student

stationery

=

Stationery

name

“pen”

if

let

stationery

=

student

stationery

?。

name

{

print

\(

student

name

‘s

\(

stationery

!”

}

else

{

print

“Error”

}

// Prints Jiang YouHua’s pen!

Error Handling 錯誤處理。錯誤處理有四種處理方式:

向呼叫者執出錯誤。使用呼叫者時,仍需要處理錯誤。

使用do-catch語句處理。完全處理錯誤。

將錯誤作為可選值處理。使用try?,有錯誤,返回nil。無錯誤,返回值。

斷言錯誤不會發生。使用try!,有錯誤,丟擲系統錯誤,無錯誤,返回值。

下面是一個使用者在商店用積分購買商品提交訂單的示例。使用者購物時有四種狀態,未登入、登入但不是會員、會員購買的商品不需要積分,會員的積分不夠購買商品。本例用Error Handling來的四種方式來處理,當然你也可以用其它方式來處理。

import

Cocoa

/** 一個購物的示例 **/

enum

ShopError

Error

{

// 定義購買錯誤

case

notLoggedError

// 未登入錯誤

case

notMemberError

// 不是會員錯誤

case

freeToUseError

// 免費使用

case

insufficientAmountError

score

Int

// 積分不足錯誤

}

struct

User

{

// 定義使用者

var

uid

Int

// 使用者ID

var

role

Int

// 許可權值,許可權值為0,則為普通使用者,非會員

var

score

Int

// 積分值

}

class

Shopping

{

// 定義購買類

let

user

User

var

total

Int

=

0

init

user

User

){

self

user

=

user

}

// 處理購物單

func

handleOrder

commodity

Dictionary

<

String

Int

>)->(

code

Int

info

String

){

guard

user

uid

>

0

else

{

return

code

1

info

“Not Logged in”

}

guard

user

role

>

0

else

{

return

code

2

info

“Not a Member”

}

// 計算總價

for

_

i

in

commodity

{

self

total

+=

i

}

guard

self

total

>

0

else

{

return

code

3

info

“Free to Use”

}

guard

user

score

>

total

else

{

return

code

3

info

“Insufficient Amount”

}

// TODO

return

code

0

info

“Purchase success”

}

// 提交購物單,處理伺服器返回結果

func

submitoOrder

commodity

Dictionary

<

String

Int

>)

throws

->

Int

{

let

result

=

self

handleOrder

commodity

commodity

switch

result

code

{

case

1

throw

ShopError

notLoggedError

case

2

throw

ShopError

notMemberError

case

3

throw

ShopError

insufficientAmountError

score

user

score

default

return

self

total

}

}

}

/** 處理方式一:錯誤向上層轉移,即未處理 **/

func

buy1

()

throws

{

let

user

=

User

uid

1997

role

1

score

0

let

order

=

“pen”

12

“book”

17

let

shop

=

Shopping

user

user

try

shop

submitoOrder

commodity

order

}

// buy1()

// 無法呼叫,因為上一層也需要處理錯誤

// Playground execution terminated: An error was thrown and was not caught。

/** 處理方式二:使用do-catch語句處理 **/

func

buy2

(){

let

user

=

User

uid

1997

role

0

score

0

let

order

=

“pen”

12

“book”

17

let

shop

=

Shopping

user

user

do

{

try

shop

submitoOrder

commodity

order

}

catch

ShopError

notLoggedError

{

print

“You are not logged in, please login”

}

catch

ShopError

notMemberError

{

print

“You are not a member, please join the membership”

}

catch

ShopError

insufficientAmountError

let

score

){

print

“Your score are not enough, the score is only 10

\(

score

}

catch

{

print

“Unexpected error:

\(

error

。”

}

}

buy2

()

// 處理了所有錯誤

// Prints You are not a member, please join the membership

/** 處理方式三:將錯誤作為可選值處理, **/

func

buy3

(){

let

user

=

User

uid

1997

role

1

score

200

let

order

=

“pen”

12

“book”

17

let

shop

=

Shopping

user

user

if

let

result

=

try

shop

submitoOrder

commodity

order

){

print

“Please pay

\(

result

yuan”

}

}

buy3

()

// 有錯就返回nil

// Prints Please pay 29 yuan

/** 處理方式四:斷言錯誤不會發生 **/

func

buy4

(){

let

user

=

User

uid

1997

role

1

score

1000

let

order

=

“pen”

12

“book”

17

“toy”

479

let

shop

=

Shopping

user

user

let

result

=

try

shop

submitoOrder

commodity

order

print

“Please pay

\(

result

yuan”

}

buy4

()

// 有錯就產生系統錯誤,Fatal error: ‘try!’ expression unexpectedly raised an error。

// Prints Please pay 508 yuan

泛型,是為了解決類似於方法過載的問題。

/** 定義一個交換值的方法, 需要為Int,Double之類各寫一個, 共計n個 **/

func

swapTwoInt

_

a

inout

Int

_

b

inout

Int

{

let

temporaryA

=

a

a

=

b

b

=

temporaryA

}

func

swapTwoStrings

_

a

inout

String

_

b

inout

String

{

let

temporaryA

=

a

a

=

b

b

=

temporaryA

}

// ……

/** 只需要定義一個泛型的, 1 = n **/

func

swapTwoValues

<

T

>(

_

a

inout

T

_

b

inout

T

{

let

temporaryA

=

a

a

=

b

b

=

temporaryA

}

var

num1

=

100

var

num2

=

200

swapTwoValues

(&

num1

&

num2

// num1 is 200, num2 is 100

var

str1

=

“A”

var

str2

=

“B”

swapTwoValues

(&

str1

&

str2

// str1 = “B”, str2 = “A”

泛型約束。型別約束指定了一個必須繼承自指定類的型別引數,或者遵循一個特定的協議或協議構成。

func

someFunction

<

T

SomeClass

U

SomeProtocol

>(

someT

T

someU

U

{

// 這裡是泛型函式的函式體部分

}

// 上面這個函式有兩個型別引數。

// 第一個型別引數 T,有一個要求 T 必須是 SomeClass 子類的型別約束;

// 第二個型別引數 U,有一個要求 U 必須符合 SomeProtocol 協議的型別約束。

下一篇,MacOS介面設計。

讓我們在這裡,遇見明天的自己!姜友華