## Swift 類
### 定義類
類是自定義的數據類型,所以類名首字母需要大寫。
類屬性必須賦初始值,或者使用構造函數中賦初始值
```
class 類名稱{
var 屬性: 屬性變量類型
let 屬性:屬性常量類型
// 構造函數
init(){
}
// 類方法
func funcName(<#parameters#>) -> <#return type#> {
<#function body#>
}
}
```
定義一個類
```
class Person {
var firstName: String
var lastName: String
// 指定構造函數
init(firstName: String , lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// 可失敗的構造函數 排除 nil 的情況
init?( fullName: String ){
guard let spaceIndex = fullName.range(of: " ")?.lowerBound else {
return nil
}
self.firstName = fullName.substring(to: spaceIndex)
self.lastName = fullName.substring(from: fullName.index(after: spaceIndex))
}
// 定義方法
func fullName() -> String{
return self.firstName + " " + self.lastName
}
}
let person1 = Person(firstName: "Steve", lastName: "Jobs")
let person2 = Person(fullName: "Steve Jobs")
person1.fullName() // Steve Jobs
```
### 類是引用類型 Reference Type
類不是值類型,而是一個**引用類型**
```
class Person {
var firstName: String
var lastName: String
var career: String?
// 指定構造函數
init(firstName: String , lastName: String , career: String){
self.firstName = firstName
self.lastName = lastName
self.career = career
}
// 指定構造函數
init(firstName: String , lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// 定義方法
func fullName() -> String{
return self.firstName + " " + self.lastName
}
}
let person1 = Person(firstName: "Steve", lastName: "Jobs",career: "Developer")
let person2 = person1 // person2 是 person1 的別名,共用內存地址空間
person2.career = "CEO"
person1
person2
```
#### Swift 引用類型的特點 Reference Type
**類是引用類型(Reference Type)**。下面以引用類型 `Class` 和值類型`Struce`、`Enum`作比較。
```
class Person {
let firstName: String
let lastName: String
var career: String?
// 指定構造函數
init(firstName: String , lastName: String , career: String){
self.firstName = firstName
self.lastName = lastName
self.career = career
}
// 指定構造函數
init(firstName: String , lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// 定義方法
func fullName() -> String{
return self.firstName + " " + self.lastName
}
func changeCareer(newCareer: String) {
self.career = newCareer
}
}
let person = Person(firstName: "Steve", lastName: "Jobs")
person.career // nil
person.career = "CEO" // 賦值修改類的變量屬性
person.career // CEO
// 對于一個類的變量屬性,如果他的實例對象是常量,依然可以在外界對它的值做修改。
person.changeCareer(newCareer: "winner")
person.career // winner
```
#### 在結構體中方法改變自身屬性的舉例
使用 `mutating` 關鍵字定義在結構體方法前,說明當前方法需要自己修改自己的屬性。
```
struct Location{
var x = 0
var y = 0
mutating func goEast(){
self.x += 1
}
}
```
#### 在枚舉類型方法中改變自身屬性
使用 `mutating` 關鍵字定義在枚舉類型的方法前,說明當前方法需要自己修改自己的屬性。
```
var location = Location()
location.goEast()
enum Swith{
case On
case Off
mutating func click() {
switch self {
case .On:
self = .Off
case .Off:
self = .On
}
}
}
var swith = Swith.On
swith.click() // Off
swith.click() // On
```
### 類相等的比較
在Swift中,默認情況下`==`不能直接應用在類的實例化對象相等的比較上(`==`比較的本質是**值的比較**,只可以對兩個值類型作比較)。
使用 `===` 判斷兩個類(引用類型)的實例話對象是否相等,即比較它們是否是指向同一內存地址。
```
class Person {
let firstName: String
let lastName: String
var career: String? // 職業,字符串的可選性
// 指定構造函數
init(firstName: String , lastName: String , career: String){
self.firstName = firstName
self.lastName = lastName
self.career = career
}
// 指定構造函數
init(firstName: String , lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// 定義方法
func fullName() -> String{
return self.firstName + " " + self.lastName
}
func changeCareer(newCareer: String) {
self.career = newCareer
}
}
let person1 = Person(firstName: "Steve", lastName: "Jobs")
let person2 = person1
person1 === person2 // true 引用同一個內存空間
// 重新實例化一個相同內容的數據后,發現對象不相等
let person3 = Person(firstName: "Steve", lastName: "Jobs")
person1 === person3 // false 兩次實例化,計算機分配了兩個內存空間存儲不同的對象
person1 !== person3 // true
```
> 判定兩個不同的實例可以使用 `!==` 進行判斷。
### 類的繼承
在Swift中,類可以繼承,而結構體不可以,當我們發現在業務邏輯中需要使用繼承,那么應該選擇類這種數據類型。
```
// 游戲角色類
class Avatar{
var name: String // 角色名
var life: Int = 100 // 血量
var isAlive: Bool = true // 是否還活著
// 指定構造函數
init(name: String) {
self.name = name
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
let user1 = User(name: "Stive") // 從父類繼承的構造函數
user1.name // 讀取父類屬性
user1.score
user1.life
user1.isAlive
user1.beAttacked(attack: 10) // 讀取父類方法
user1.life
user1.getScore(score: 10)
user1.getScore(score: 100)
user1.score
user1.level
// 魔術師類
class Magician: User {
var magic: Int = 100
}
let magician = Magician(name: "harry potter") // 調用父類的父類的構造函數
// 調用父類或者父類的父類的屬性
magician.name
magician.life
magician.isAlive
magician.score
magician.level
magician.magic
```
類與類的繼承不僅僅可以子類繼承父類,可以一層層不斷的繼承下去。
> 如果類不允許子類繼承,可以使用 `final` 關鍵字修飾類。
### 類的多態
舉例在游戲中的角色之間的關系,使用多態對角色進行批量操作。
```
// 游戲角色類
class Avatar{
var name: String // 角色名
var life: Int = 100 { // 屬性觀察器
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
} // 血量
var isAlive: Bool = true // 是否還活著
// 指定構造函數
init(name: String) {
self.name = name
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
// 魔術師類
final class Magician: User {
var magic: Int = 100
// 治療
func heal(user: User){
user.life += 10
}
}
//戰士類
final class Warrior: User {
var weapon: String? // 武器屬性
}
// 怪獸
class Monster: Avatar {
func attack(user: User, amount: Int) {
user.beAttacked(attack: amount)
}
}
// 僵尸
final class Zombie: Monster {
var type: String = "Default"
}
let player1 = Magician(name: "Harry Potter") // 魔法師角色對象
let player2 = Warrior(name: "Stive") // 戰士角色對象
let zombie = Zombie(name: "Default Zombie") // 僵尸角色對象
let monster = Monster(name: "Monster") // 怪獸角色對象
func printBasicInfo(avatar: Avatar) { // 打印對象的基本信息
print("The avatar's name is \(avatar.name)")
print("The life is \(avatar.life). He is \((avatar.isAlive) ? "still alive" : "dead")")
print("")
}
printBasicInfo(avatar: player1)
printBasicInfo(avatar: player2)
printBasicInfo(avatar: zombie)
printBasicInfo(avatar: monster)
// 批量操作Avatar對象
let avatarArr: [Avatar] = [player1, player2, zombie, monster]
// 全部受到10攻擊
for avatar in avatarArr {
avatar.beAttacked(attack: 10)
}
printBasicInfo(avatar: player1)
printBasicInfo(avatar: player2)
printBasicInfo(avatar: zombie)
printBasicInfo(avatar: monster)
player1.heal(user: player2) // 魔法師給用戶治療
```
### 類的重載
當繼承的過程中,我們想在子類中重寫父類的屬性或者方法時,需要使用 `override` 關鍵字重寫父類的方法或者屬性。
當父類中的屬性或者方法是 `final` 時候,那么這時這個方法是不可以覆蓋的。
```
// 游戲角色類
class Avatar{
var name: String // 角色名
var life: Int = 100 {
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
}// 血量
var isAlive: Bool = true // 是否還活著
var description: String { // 存儲型屬性
return "I'm avatar \(name)."
}
// 指定構造函數
init(name: String) {
self.name = name
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm user \(name)."
}
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
// 魔術師類
final class Magician: User {
var magic: Int = 100
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm magician \(name)."
}
// 治療
func heal(user: User){
user.life += 10
}
}
//戰士類
final class Warrior: User {
var weapon: String? // 武器屬性
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm warrior \(name)."
}
override func beAttacked(attack: Int) { // 子類重載了父類的方法
self.life -= attack/2
}
}
// 怪獸
class Monster: Avatar {
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm monster \(name)."
}
func attack(user: User, amount: Int) {
user.beAttacked(attack: amount)
}
}
// 僵尸
final class Zombie: Monster {
var type: String = "Default"
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm zombie \(name)."
}
}
let player1 = Magician(name: "Harry Potter") // 魔法師角色對象
let player2 = Warrior(name: "Stive") // 戰士角色對象
let zombie = Zombie(name: "Default Zombie") // 僵尸角色對象
let monster = Monster(name: "Monster") // 怪獸角色對象
print(player1.description)
print(player2.description)
print(zombie.description)
print(monster.description)
let avatarArr: [Avatar] = [player1, player2, zombie, monster]
for avatar in avatarArr {
print(avatar.description)
}
// 調用不同類實例化對象的方法
monster.attack(user: player1, amount: 20)
player1.life
monster.attack(user: player2, amount: 20)
player2.life
```
### Swift兩段式構造
在`Swift`語言中,當需要子類構造函數初始化父類的屬性時,關于父類的屬性必須通過父類的構造函數來進行構造,這時需要使用`super`關鍵字調用父類的構造函數初始化父類的屬性。并且調用父類的構造函數之前需要先初始化子類相關的所有屬性完成。
在`Swift`構造函數中,可以有自己的邏輯,但是這個邏輯全都是用戶初始化類。當類還沒有構造完成時,不能使用 `self.屬性名或者方法名`來調用其它還沒有構造的屬性以及所有的方法的,這是因為此時,`self`還不存在。
對于子類而言,我們可能首先需要構造自身的屬性,然后構造父類的屬性,最后再做進一步的類的完善(此時才可以使用`self`這個關鍵字)。
```
// 游戲角色類
class Avatar{
var name: String // 角色名
var life: Int = 100 {
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
}// 血量
var isAlive: Bool = true // 是否還活著
var description: String { // 存儲型屬性
return "I'm avatar \(name)."
}
// 指定構造函數
init(name: String) {
self.name = name
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
override var description: String { // 子類重寫屬性覆蓋父類屬性
return "I'm user \(name)."
}
var gourp: String
init(group: String, name: String) { // 子類指定構造函數初始化對象屬性
// 構造
self.gourp = group // 首先初始化自身的屬性
super.init(name: name) // 使用super關鍵字調用父類的構造函數初始化父類的屬性
// 進一步完善
if group == "" {
self.getScore(score: -10)
}
}
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
//戰士類
final class Warrior: User {
var weapon: String // 武器屬性
// 子類指定構造函數初始化對象屬性
init(group: String, name: String, weapon: String) {
self.weapon = weapon // 首先初始化自身的屬性
super.init(group: group, name: name)
}
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm warrior \(name)."
}
override func beAttacked(attack: Int) { // 子類重載了父類的方法
self.life -= attack/2
}
}
let user = User(group: "Apple", name: "Stive")
```
> 注意看上面子類的構造函數中的屬性賦值的先后順序。
### 便利構造函數和指定構造函數
因為構造函數也是一個函數,所以很多函數的特性也可以在構造函數中應用,比如:可以在構造函數中指定默認的參數。
便利的構造函數(convenience關鍵字):一定要調用到自身的一個指定構造函中,只有指定的構造函數才能調用父類的構造函數。
```
// 游戲角色類
class Avatar {
var name: String // 角色名
var life: Int = 100 {
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
}// 血量
var isAlive: Bool = true // 是否還活著
var description: String { // 存儲型屬性
return "I'm avatar \(name)."
}
// 指定構造函數
init(name: String) {
self.name = name
}
// 方便的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init (firstName: String, lastName: String) {
self.init(name: firstName + lastName)
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm user \(name)."
}
var gourp: String
// 指定構造函數
init(name: String, group: String = "") { // 子類構造函數初始化group屬性
// 構造
self.gourp = group // 首先初始化自身的屬性
super.init(name: name) // 使用super關鍵字調用父類的構造函數初始化父類的屬性
// 進一步完善
if group == "" {
self.getScore(score: -10)
}
}
// 方便的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init(group: String = "") {
// 沒有名字調用自身靜態函數自動生成名字
let name = User.generateUserName()
self.init(name: name, group: group)
}
static func generateUserName () -> String {
return "Player" + String(arc4random())
}
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
//戰士類
final class Warrior: User {
var weapon: String // 武器屬性
// 子類構造函數初始化weapon屬性
init(name: String, group: String, weapon: String = "Sword") { // 構造函數默認參數
self.weapon = weapon // 首先初始化自身的屬性
super.init(name: name, group: group)
}
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm warrior \(name)."
}
override func beAttacked(attack: Int) { // 子類重載了父類的方法
self.life -= attack/2
}
}
// 魔術師類
final class Magician: User {
var magic: Int = 100
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm magician \(name)."
}
// 子類構造函數重寫父類構造函數
override init(name: String, group: String) {
let defaultGrouops: [String] = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]
for theGroup in defaultGrouops {
if group == theGroup {
super.init(name: name, group: group)
return
}
}
// 如果傳入的group字符串不在數組單元中,分配一個隨機的分組
let group = defaultGrouops[ Int(arc4random_uniform(3))]
super.init(name: name, group: group)
}
// 治療
func heal(user: User){
user.life += 10
}
}
let player1 = Warrior(name: "Stive", group: "Apple")
player1.weapon
```
> 上述demo中,各個類中都有相應的指定構造函數和便利構造函數。
> 具體的調用規則是:便利構造函數只能調用自身的指定構造函數實例化對象屬性,而指定構造函數可以調用父類的指定構造函數實例化對象屬性。
在Swift為什么需要便利的構造函數?
當我們使用構造函數構造對象屬性時,如果需要調用自身的指定構造函數來初始化對象屬性,那么這時就需要使用便利的構造函數。
并且遍歷構造函數只能調用自身的遍歷構造函數,不能使用父類繼承的指定構造函數。而自身的指定構造函數則可以調用父類的指定構造函數。
### Swift構造函數的繼承
```
// 游戲角色類
class Avatar {
var name: String // 角色名
var life: Int = 100 {
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
}// 血量
var isAlive: Bool = true // 是否還活著
var description: String { // 存儲型屬性
return "I'm avatar \(name)."
}
// 指定構造函數
init(name: String) {
self.name = name
}
// 便利的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init (firstName: String, lastName: String) {
self.init(name: firstName + lastName)
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm user \(name)."
}
var gourp: String
// 指定構造函數
init(name: String, group: String) { // 子類構造函數初始化group屬性
// 構造
self.gourp = group // 首先初始化自身的屬性
super.init(name: name) // 使用super關鍵字調用父類的構造函數初始化父類的屬性
// 進一步完善
if group == "" {
self.getScore(score: -10)
}
}
// 方便的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init(group: String) {
// 沒有名字自動生成名字
let name = User.generateUserName()
self.init(name: name, group: group)
}
// 重載構造函數
convenience override init(name: String) {
self.init(name: name, group: "")
}
static func generateUserName () -> String {
return "Player" + String(arc4random())
}
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
let user = User(firstName: "Stive", lastName: "Jobs")
class Monster: Avatar {
//如果子類沒有實現父類的指定構造函數,則會自動繼承父類的指定構造函數。 所以下面的遍歷構造函數也可以使用自身繼承自父類的指定構造函數了。
convenience init(type: String) {
self.init(name: type)
}
}
let monster = Monster(type: "")
```
> 在類的繼承關系中
> 1. 如果子類實現了父類所有的指定構造函數,則自動繼承父類的所有便利構造函數。
> 2. 如果子類沒有實現父類的指定構造函數,則會自動繼承父類的指定構造函數。又根據上面的原則,那么對應的便利構造函數也就都繼承下來。
相關閱讀:[簡書-Swift中的構造函數及其繼承](http://www.jianshu.com/p/ff52f6f3e6c0)
### required構造函數
在類的編寫過程中,我們書寫一個父類的構造函數時,如果構造函數很重要,必須要繼承的子類實現這個構造函數,可以使用 `required` 關鍵字。
在子類繼承類并重寫該構造函數時,不需要寫`override` 覆蓋父類的這個 `required` 構造函數。
```
// 游戲角色類
class Avatar {
var name: String // 角色名
var life: Int = 100 {
didSet {
if self.life <= 0 {
self.isAlive = false
}
if self.life > 100 {
self.life = 100
}
}
}// 血量
var isAlive: Bool = true // 是否還活著
var description: String { // 存儲型屬性
return "I'm avatar \(name)."
}
// required構造函數
required init(name: String) {
self.name = name
}
// 便利的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init (firstName: String, lastName: String) {
self.init(name: firstName + lastName)
}
// 角色受攻擊邏輯
func beAttacked(attack: Int) {
self.life -= attack
if life <= 0 {
isAlive = false
}
}
}
// 玩家繼承自角色類
class User: Avatar {
var score: Int = 0 // 玩家得分
var level: Int = 0 // 玩家等級
override var description: String { // 子類重新定義屬性覆蓋父類屬性
return "I'm user \(name)."
}
var gourp: String
// 指定構造函數
init(name: String, group: String) { // 子類構造函數初始化group屬性
// 構造
self.gourp = group // 首先初始化自身的屬性
super.init(name: name) // 使用super關鍵字調用父類的構造函數初始化父類的屬性
// 進一步完善
if group == "" {
self.getScore(score: -10)
}
}
// 方便的構造函數 (構造函數中調用了另外的一個自身的構造函數,它自身并沒有把整個對象構造完成)
convenience init(group: String) {
// 沒有名字自動生成名字
let name = User.generateUserName()
self.init(name: name, group: group)
}
// 重載required構造函數
convenience required init(name: String) {
self.init(name: name, group: "")
}
static func generateUserName () -> String {
return "Player" + String(arc4random())
}
func getScore(score: Int) {
self.score += score
if self.score > level * 100{
level += 1
}
}
}
let user = User(firstName: "Stive", lastName: "Jobs")
class Monster: Avatar {
//如果子類沒有實現父類的指定構造函數,則會自動繼承父類的指定構造函數。 所以下面的遍歷構造函數也可以使用自身繼承自父類的指定構造函數了。
convenience init(type: String) {
self.init(name: type)
}
}
let monster = Monster(type: "")
class NPC: Avatar {
let career: String
// required 構造函數
convenience required init(name: String) {
self.init(name: name, career: "")
}
init(name: String, career: String) {
self.career = career
super.init(name: name)
}
}
```
- 學習筆記
- 基礎
- 基本類型之整型
- 基本類型之浮點型
- 基本類型之布爾類型以及簡單的 if 語句
- 基礎類型之元組
- 基本類型之其他
- 運算符
- 基礎運算符
- 比較運算符、邏輯運算符
- 三元運算符
- 范圍運算符for-in
- 邏輯控制
- 循環結構
- 選擇結構
- 字符串
- Character和Unicode
- String.index 和 range
- 可選型
- 容器類
- 數組初始化
- 數組基本操作
- 字典初始化
- 字典基本操作
- 集合初始化
- 集合基本操作
- 函數
- 閉包
- 枚舉
- 結構體
- 類
- 文檔注釋
- 屬性和方法
- 下標和運算符重載
- 拓展和泛型
- 協議
- 其他
- Swift 3.0 For 循環
- Swift 隨機數的生成
- IOS開發玩轉界面 UIKit
- UILable 文本顯示控件
- UIButton 簡單的交互控件
- UIImageView 圖片控件
- UISearchBar 搜索控件