Swift-類澳腹、結(jié)構(gòu)體、屬性杨何、方法

最近項目使用的是OC酱塔,后頭看之前用Swift開發(fā)的一個項目時,發(fā)現(xiàn)很多細(xì)節(jié)都忘記了????危虱。
為了回憶和以后方便查看羊娃,現(xiàn)在根據(jù)官方文檔swift編程語言,做下筆記埃跷。

1蕊玷、 值類型和引用類型

  • 值類型被賦予給一個變量邮利、常量或者被傳遞給一個函數(shù)的時候,其值會被拷貝垃帅。

在 Swift 中延届,所有的基本類型:整數(shù)、浮點數(shù)贸诚、布爾值方庭、字符串、數(shù)組和字典酱固,都是值類型械念,并且在底層都是以結(jié)構(gòu)體的形式所實現(xiàn)。

所有的結(jié)構(gòu)體和枚舉類型都是值類型运悲。這意味著它們的實例龄减,以及實例中所包含的任何值類型屬性,在代碼中傳遞的時候都會被復(fù)制班眯。

  • 引用類型在被賦予到一個變量欺殿、常量或者被傳遞到一個函數(shù)時,其值不會被拷貝鳖敷,引用的是已存在的實例本身而不是其拷貝脖苏。

類是引用類型,因此引用的是已存在的實例本身而不是其拷貝定踱。

2棍潘、OC與Swift中的字符串、數(shù)組崖媚、和字典類型

Swift 中亦歉,String,Array和Dictionary類型均以結(jié)構(gòu)體的形式實現(xiàn)畅哑。這意味著被賦值給新的常量或變量肴楷,或者被傳入函數(shù)或方法中時,它們的值會被拷貝荠呐。

Objective-C 中NSString赛蔫,NSArray和NSDictionary類型均以類的形式實現(xiàn),而并非結(jié)構(gòu)體泥张。它們在被賦值或者被傳入函數(shù)或方法時呵恢,不會發(fā)生值拷貝,而是傳遞現(xiàn)有實例的引用媚创。

3渗钉、類和結(jié)構(gòu)體的選擇

按照通用的準(zhǔn)則,當(dāng)符合一條或多條以下條件時钞钙,考慮構(gòu)建結(jié)構(gòu)體:

  • 該數(shù)據(jù)結(jié)構(gòu)的主要目的是用來封裝少量相關(guān)簡單數(shù)據(jù)值鳄橘。
  • 有理由預(yù)計該數(shù)據(jù)結(jié)構(gòu)的實例在被賦值或傳遞時声离,封裝的數(shù)據(jù)將會被拷貝而不是被引用。
  • 該數(shù)據(jù)結(jié)構(gòu)中儲存的值類型屬性瘫怜,也應(yīng)該被拷貝术徊,而不是被引用。
  • 該數(shù)據(jù)結(jié)構(gòu)不需要去繼承另一個既有類型的屬性或者行為宝磨。

以下情境中適合使用結(jié)構(gòu)體:

  • 幾何形狀的大小弧关,封裝一個width屬性和height屬性盅安,兩者均為Double類型唤锉。
  • 一定范圍內(nèi)的路徑,封裝一個start屬性和length屬性别瞭,兩者均為Int類型窿祥。
  • 三維坐標(biāo)系內(nèi)一點,封裝x蝙寨,y和z屬性晒衩,三者均為Double類型。

在所有其它案例中墙歪,定義一個類听系,生成一個它的實例,并通過引用來管理和傳遞虹菲。實際中靠胜,這意味著絕大部分的自定義數(shù)據(jù)構(gòu)造都應(yīng)該是類,而非結(jié)構(gòu)體毕源。

4浪漠、類和結(jié)構(gòu)體

  • 類和結(jié)構(gòu)體有著類似的定義方式。我們通過關(guān)鍵字class和struct來分別表示類和結(jié)構(gòu)體霎褐,并在一對大括號中定義它們的具體內(nèi)容:
class SomeClass {
    // 在這里定義類
}
struct SomeStructure {
    // 在這里定義結(jié)構(gòu)體
}
以下是定義結(jié)構(gòu)體和定義類的示例:

其中name為可選類型址愿,值為可選String,默認(rèn)值為nil冻璃,意為沒有name值

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}
  • 類和結(jié)構(gòu)體實例
let someResolution = Resolution()
let someVideoMode = VideoMode()
  • 結(jié)構(gòu)體類型的成員逐一構(gòu)造器

所有結(jié)構(gòu)體都有一個自動生成的成員逐一構(gòu)造器响谓,用于初始化新結(jié)構(gòu)體實例中成員的屬性。

let vga = Resolution(width:640, height: 480)

類實例沒有默認(rèn)的成員逐一構(gòu)造器

  • 結(jié)構(gòu)體是值類型
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// 打印 "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// 打印 "hd is still 1920 pixels wide"
  • 類是引用類型
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"

需要注意的是tenEighty和alsoTenEighty被聲明為常量而不是變量省艳。然而你依然可以改變tenEighty.frameRate和alsoTenEighty.frameRate歌粥,因為tenEighty和alsoTenEighty這兩個常量的值并未改變。它們并不“存儲”這個VideoMode實例拍埠,而僅僅是對VideoMode實例的引用失驶。所以,改變的是被引用的VideoMode的frameRate屬性枣购,而不是引用VideoMode的常量的值嬉探。

5擦耀、屬性

屬性將值跟特定的類、結(jié)構(gòu)或枚舉關(guān)聯(lián)涩堤。存儲屬性存儲常量或變量作為實例的一部分眷蜓,而計算屬性計算(不是存儲)一個值。
注意計算屬性可以用于類胎围、結(jié)構(gòu)體和枚舉吁系,存儲屬性只能用于類和結(jié)構(gòu)體。Swift 中的屬性沒有對應(yīng)的實例變量

  • 存儲屬性

存儲屬性就是存儲在特定類或結(jié)構(gòu)體實例里的常量或變量白魂。

例如:FixedLengthRange 的實例包含一個名為 firstValue 的變量存儲屬性和一個名為 length 的常量存儲屬性汽纤。

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 該區(qū)間表示整數(shù)0,1福荸,2
rangeOfThreeItems.firstValue = 6
// 該區(qū)間現(xiàn)在表示整數(shù)6蕴坪,7,8

注意如果創(chuàng)建了一個結(jié)構(gòu)體的實例并將其賦值給一個常量敬锐,則無法修改該實例的任何屬性背传,即使有屬性被聲明為變量也不行:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 該區(qū)間表示整數(shù)0,1台夺,2径玖,3
rangeOfFourItems.firstValue = 6
// 盡管 firstValue 是個變量屬性,這里還是會報錯

原因:結(jié)構(gòu)體屬于值類型颤介,當(dāng)值類型的實例被聲明為常量的時候梳星,它的所有屬性也就成了常量。

  • Lazy Stored Properties

延遲存儲屬性是指當(dāng)?shù)谝淮伪徽{(diào)用的時候才會計算其初始值的屬性买窟。在屬性聲明前使用 lazy 來標(biāo)示一個延遲存儲屬性丰泊。

必須將延遲存儲屬性聲明成變量(使用 var 關(guān)鍵字),因為屬性的初始值可能在實例構(gòu)造完成之后才會得到始绍。而常量屬性在構(gòu)造過程完成之前必須要有初始值瞳购,因此無法聲明成延遲屬性。

注意如果一個被標(biāo)記為 lazy 的屬性在沒有初始化時就同時被多個線程訪問亏推,則無法保證該屬性只會被初始化一次学赛。

  • 計算屬性

計算屬性不直接存儲值,而是提供一個 getter 和一個可選的 setter吞杭,來間接獲取和設(shè)置其他屬性或變量的值盏浇。

注意必須使用 var 關(guān)鍵字定義計算屬性,包括只讀計算屬性芽狗,因為它們的值不是固定的绢掰。let 關(guān)鍵字只用來聲明常量屬性,表示初始化后再也無法修改的值。
例如:Rect提供了一個名為center 的計算屬性滴劲。

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 打印 "square.origin is now at (10.0, 10.0)”
  • 簡化 setter 聲明

如果計算屬性的 setter 沒有定義表示新值的參數(shù)名攻晒,則可以使用默認(rèn)名稱 newValue。

var center: Point {
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
  • 只讀計算屬性

只有 getter 沒有 setter 的計算屬性就是只讀計算屬性班挖。

只讀計算屬性的聲明可以去掉 get 關(guān)鍵字和花括號:

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 打印 "the volume of fourByFiveByTwo is 40.0"
  • 屬性觀察

屬性觀察器監(jiān)控和響應(yīng)屬性值的變化鲁捏,每次屬性被設(shè)置值的時候都會調(diào)用屬性觀察器,即使新值和當(dāng)前值相同的時候也不例外萧芙。

可以為除了延遲存儲屬性之外的其他存儲屬性添加屬性觀察器给梅,也可以通過重寫屬性的方式為繼承的屬性(包括存儲屬性和計算屬性)添加屬性觀察器。

willSet 在新的值被設(shè)置之前調(diào)用
didSet 在新的值被設(shè)置之后調(diào)用

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
  • 類型屬性(Type Properties)

存儲型類型屬性可以是變量或常量双揪,計算型類型屬性跟實例的計算型屬性一樣只能定義成變量屬性动羽。

跟實例的存儲型屬性不同,必須給存儲型類型屬性指定默認(rèn)值盟榴,因為類型本身沒有構(gòu)造器曹质,也就無法在初始化過程中使用構(gòu)造器給類型屬性賦值婴噩。

1擎场、類型屬性語法

Swift 中,類型屬性是作為類型定義的一部分寫在類型最外層的花括號內(nèi)几莽,因此它的作用范圍也就在類型支持的范圍內(nèi)迅办。

使用關(guān)鍵字 static 來定義類型屬性。在為類定義計算型類型屬性時章蚣,可以改用關(guān)鍵字 class 來支持子類對父類的實現(xiàn)進(jìn)行重寫站欺。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}
2、類型屬性是通過類型本身來訪問纤垂,而不是通過實例矾策。
print(SomeStructure.storedTypeProperty)
// 打印 "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// 打印 "Another value.”

6、方法

  • 實例方法 (Instance Methods)

在 Objective-C 中峭沦,類是唯一能定義方法的類型贾虽。但在 Swift 中,類/結(jié)構(gòu)體/枚舉上均可以定義方法吼鱼。

1蓬豁、定義一個很簡單的Counter類:
class Counter {
    var count = 0
    func increment() {
        count += 1
    }
}

let counter = Counter()
// 初始計數(shù)值是0
counter.increment()
// 計數(shù)值現(xiàn)在是1
2、在實例方法中修改值類型

結(jié)構(gòu)體和枚舉是值類型菇肃。默認(rèn)情況下地粪,值類型的屬性不能在它的實例方法中被修改。
但是:
你可以為這個方法選擇可變(mutating)行為琐谤,然后就可以從其方法內(nèi)部改變它的屬性蟆技;

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印 "The point is now at (3.0, 4.0)"

注意不能在結(jié)構(gòu)體類型的常量(a constant of structure type)上調(diào)用可變方法。

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// 這里將會報告一個錯誤
3、在可變方法中給 self 賦值

可變方法能夠賦給隱含屬性self一個全新的實例质礼。上面Point的例子可以用下面的方式改寫:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

新版的可變方法moveBy(x:y:)創(chuàng)建了一個新的結(jié)構(gòu)體實例聊品,它的 x 和 y 的值都被設(shè)定為目標(biāo)值。調(diào)用這個版本的方法和調(diào)用上個版本的最終結(jié)果是一樣的几苍。

  • self 屬性

類型的每一個實例都有一個隱含屬性叫做self翻屈,self完全等同于該實例本身。你可以在一個實例的實例方法中使用這個隱含的self屬性來引用當(dāng)前實例妻坝。

上面例子中的increment方法還可以這樣寫:

func increment() {
    self.count += 1
}

注意實例方法的某個參數(shù)名稱與實例的某個屬性名稱相同的時候伸眶,參數(shù)名稱享有優(yōu)先權(quán)。這時你可以使用self屬性來區(qū)分參數(shù)名稱和屬性名稱刽宪。

  • 類方法

定義在類型本身上調(diào)用的方法厘贼。
在方法的func關(guān)鍵字之前加上關(guān)鍵字static,來指定類型方法圣拄。類還可以用關(guān)鍵字class來允許子類重寫父類的方法實現(xiàn)嘴秸。

在 Objective-C 中,你只能為 Objective-C 的類類型(classes)定義類型方法(type-level methods)庇谆。
在 Swift 中岳掐,你可以為所有的類、結(jié)構(gòu)體和枚舉定義類型方法饭耳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末串述,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子寞肖,更是在濱河造成了極大的恐慌纲酗,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件新蟆,死亡現(xiàn)場離奇詭異觅赊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)琼稻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門吮螺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人欣簇,你說我怎么就攤上這事规脸。” “怎么了熊咽?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵莫鸭,是天一觀的道長。 經(jīng)常有香客問我横殴,道長被因,這世上最難降的妖魔是什么卿拴? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮梨与,結(jié)果婚禮上堕花,老公的妹妹穿的比我還像新娘。我一直安慰自己粥鞋,他們只是感情好缘挽,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呻粹,像睡著了一般壕曼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上等浊,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天腮郊,我揣著相機(jī)與錄音,去河邊找鬼筹燕。 笑死轧飞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撒踪。 我是一名探鬼主播过咬,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼糠涛!你這毒婦竟也來了援奢?” 一聲冷哼從身側(cè)響起兼犯,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤忍捡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后切黔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砸脊,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年纬霞,在試婚紗的時候發(fā)現(xiàn)自己被綠了凌埂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡诗芜,死狀恐怖瞳抓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伏恐,我是刑警寧澤孩哑,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站翠桦,受9級特大地震影響横蜒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一丛晌、第九天 我趴在偏房一處隱蔽的房頂上張望仅炊。 院中可真熱鬧,春花似錦澎蛛、人聲如沸抚垄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽督勺。三九已至,卻和暖如春斤贰,著一層夾襖步出監(jiān)牢的瞬間智哀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工荧恍, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留瓷叫,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓送巡,卻偏偏與公主長得像摹菠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子骗爆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容