鍍金池/ 教程/ iOS/ 類型轉(zhuǎn)換(Type Casting)
方法 - Methods
關(guān)于 Swift
下標(biāo)腳本(Subscripts)
類和結(jié)構(gòu)體
類型轉(zhuǎn)換(Type Casting)
控制流
析構(gòu)過(guò)程(Deinitialization)
集合類型 (Collection Types)
構(gòu)造過(guò)程(Initialization)
Optional Chaining
枚舉(Enumerations)
自動(dòng)引用計(jì)數(shù)
繼承(Inheritance)
擴(kuò)展(Extensions)
泛型
字符串和字符(Strings and Characters)
函數(shù)(Functions)
高級(jí)運(yùn)算符
訪問(wèn)控制
基本運(yùn)算符
嵌套類型
閉包(Closures)
協(xié)議
屬性 (Properties)

類型轉(zhuǎn)換(Type Casting)

類型轉(zhuǎn)換可以判斷實(shí)例的類型,也可以將實(shí)例看做是其父類或者子類的實(shí)例。

類型轉(zhuǎn)換在 Swift 中使用isas操作符實(shí)現(xiàn)。這兩個(gè)操作符提供了一種簡(jiǎn)單達(dá)意的方式去檢查值的類型或者轉(zhuǎn)換它的類型。

你也可以用來(lái)檢查一個(gè)類是否實(shí)現(xiàn)了某個(gè)協(xié)議,就像在 Checking for Protocol Conformance部分講述的一樣。

定義一個(gè)類層次作為例子

你可以將它用在類和子類的層次結(jié)構(gòu)上,檢查特定類實(shí)例的類型并且轉(zhuǎn)換這個(gè)類實(shí)例的類型成為這個(gè)層次結(jié)構(gòu)中的其他類型。這下面的三個(gè)代碼段定義了一個(gè)類層次和一個(gè)包含了幾個(gè)這些類實(shí)例的數(shù)組,作為類型轉(zhuǎn)換的例子。

第一個(gè)代碼片段定義了一個(gè)新的基礎(chǔ)類MediaItem。這個(gè)類為任何出現(xiàn)在數(shù)字媒體庫(kù)的媒體項(xiàng)提供基礎(chǔ)功能。特別的,它聲明了一個(gè) String 類型的 name 屬性,和一個(gè)init name初始化器。(它假定所有的媒體項(xiàng)都有個(gè)名稱。)

    class MediaItem {
        var name: String
        init(name: String) {
            self.name = name
        }
    }

下一個(gè)代碼段定義了 MediaItem 的兩個(gè)子類。第一個(gè)子類Movie,在父類(或者說(shuō)基類)的基礎(chǔ)上增加了一個(gè) director(導(dǎo)演) 屬性,和相應(yīng)的初始化器。第二個(gè)類在父類的基礎(chǔ)上增加了一個(gè) artist(藝術(shù)家) 屬性,和相應(yīng)的初始化器:

    class Movie: MediaItem {
        var director: String
        init(name: String, director: String) {
            self.director = director
            super.init(name: name)
        }
    }
    class Song: MediaItem {
        var artist: String
        init(name: String, artist: String) {
            self.artist = artist
            super.init(name: name)
        }
    }

最后一個(gè)代碼段創(chuàng)建了一個(gè)數(shù)組常量 library,包含兩個(gè)Movie實(shí)例和三個(gè)Song實(shí)例。library的類型是在它被初始化時(shí)根據(jù)它數(shù)組中所包含的內(nèi)容推斷來(lái)的。Swift 的類型檢測(cè)器能夠演繹出MovieSong 有共同的父類 MediaItem ,所以它推斷出 MediaItem[] 類作為 library 的類型。

    let library = [
        Movie(name: "Casablanca", director: "Michael Curtiz"),
        Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
        Movie(name: "Citizen Kane", director: "Orson Welles"),
        Song(name: "The One And Only", artist: "Chesney Hawkes"),
        Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
    ]
    // the type of "library" is inferred to be MediaItem[]

在幕后library 里存儲(chǔ)的媒體項(xiàng)依然是 MovieSong 類型的,但是,若你迭代它,取出的實(shí)例會(huì)是 MediaItem 類型的,而不是 MovieSong 類型的。為了讓它們作為它們本來(lái)的類型工作,你需要檢查它們的類型或者向下轉(zhuǎn)換它們的類型到其它類型,就像下面描述的一樣。

檢查類型(Checking Type)

用類型檢查操作符(is)來(lái)檢查一個(gè)實(shí)例是否屬于特定子類型。若實(shí)例屬于那個(gè)子類型,類型檢查操作符返回 true ,否則返回 false 。

下面的例子定義了兩個(gè)變量,movieCountsongCount,用來(lái)計(jì)算數(shù)組libraryMovieSong 類型的實(shí)例數(shù)量。

    var movieCount = 0
    var songCount = 0
    for item in library {
        if item is Movie {
            ++movieCount
        } else if item is Song {
            ++songCount
        }
    }
    println("Media library contains \(movieCount) movies and \(songCount) songs")
    // prints "Media library contains 2 movies and 3 songs"

示例迭代了數(shù)組 library 中的所有項(xiàng)。每一次, for-in 循環(huán)設(shè)置 item 為數(shù)組中的下一個(gè) MediaItem。

若當(dāng)前 MediaItem 是一個(gè) Movie 類型的實(shí)例, item is Movie 返回 true,相反返回 false。同樣的,item is Song檢查item是否為Song類型的實(shí)例。在循環(huán)結(jié)束后,movieCountsongCount的值就是被找到屬于各自的類型的實(shí)例數(shù)量。

向下轉(zhuǎn)型(Downcasting)

某類型的一個(gè)常量或變量可能在幕后實(shí)際上屬于一個(gè)子類。你可以相信,上面就是這種情況。你可以嘗試向下轉(zhuǎn)到它的子類型,用類型轉(zhuǎn)換操作符(as)

因?yàn)橄蛳罗D(zhuǎn)型可能會(huì)失敗,類型轉(zhuǎn)型操作符帶有兩種不同形式??蛇x形式( optional form) as? 返回一個(gè)你試圖下轉(zhuǎn)成的類型的可選值(optional value)。強(qiáng)制形式 as 把試圖向下轉(zhuǎn)型和強(qiáng)制解包(force-unwraps)結(jié)果作為一個(gè)混合動(dòng)作。

當(dāng)你不確定向下轉(zhuǎn)型可以成功時(shí),用類型轉(zhuǎn)換的可選形式(as?)??蛇x形式的類型轉(zhuǎn)換總是返回一個(gè)可選值(optional value),并且若下轉(zhuǎn)是不可能的,可選值將是 nil 。這使你能夠檢查向下轉(zhuǎn)型是否成功。

只有你可以確定向下轉(zhuǎn)型一定會(huì)成功時(shí),才使用強(qiáng)制形式。當(dāng)你試圖向下轉(zhuǎn)型為一個(gè)不正確的類型時(shí),強(qiáng)制形式的類型轉(zhuǎn)換會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤。

下面的例子,迭代了library里的每一個(gè) MediaItem ,并打印出適當(dāng)?shù)拿枋觥R@樣做,item需要真正作為MovieSong的類型來(lái)使用。不僅僅是作為 MediaItem。為了能夠使用MovieSongdirectorartist屬性,這是必要的。

在這個(gè)示例中,數(shù)組中的每一個(gè)item可能是 MovieSong。 事前你不知道每個(gè)item的真實(shí)類型,所以這里使用可選形式的類型轉(zhuǎn)換 (as?)去檢查循環(huán)里的每次下轉(zhuǎn)。

    for item in library {
        if let movie = item as? Movie {
            println("Movie: '\(movie.name)', dir. \(movie.director)")
        } else if let song = item as? Song {
            println("Song: '\(song.name)', by \(song.artist)")
        }
    }
    // Movie: 'Casablanca', dir. Michael Curtiz
    // Song: 'Blue Suede Shoes', by Elvis Presley
    // Movie: 'Citizen Kane', dir. Orson Welles
    // Song: 'The One And Only', by Chesney Hawkes
    // Song: 'Never Gonna Give You Up', by Rick Astley

示例首先試圖將 item 下轉(zhuǎn)為 Movie。因?yàn)?item 是一個(gè) MediaItem 類型的實(shí)例,它可能是一個(gè)Movie;同樣,它可能是一個(gè) Song,或者僅僅是基類 MediaItem。因?yàn)椴淮_定,as?形式在試圖下轉(zhuǎn)時(shí)將返還一個(gè)可選值。 item as Movie 的返回值是Movie?類型或 “optional Movie”。

當(dāng)向下轉(zhuǎn)型為 Movie 應(yīng)用在兩個(gè) Song 實(shí)例時(shí)將會(huì)失敗。為了處理這種情況,上面的例子使用了可選綁定(optional binding)來(lái)檢查可選 Movie真的包含一個(gè)值(這個(gè)是為了判斷下轉(zhuǎn)是否成功。)可選綁定是這樣寫的“if let movie = item as? Movie”,可以這樣解讀:

“嘗試將 item 轉(zhuǎn)為 Movie類型。若成功,設(shè)置一個(gè)新的臨時(shí)常量 movie 來(lái)存儲(chǔ)返回的可選Movie

若向下轉(zhuǎn)型成功,然后movie的屬性將用于打印一個(gè)Movie實(shí)例的描述,包括它的導(dǎo)演的名字director。當(dāng)Song被找到時(shí),一個(gè)相近的原理被用來(lái)檢測(cè) Song 實(shí)例和打印它的描述。

注意: 轉(zhuǎn)換沒(méi)有真的改變實(shí)例或它的值。潛在的根本的實(shí)例保持不變;只是簡(jiǎn)單地把它作為它被轉(zhuǎn)換成的類來(lái)使用。

AnyAnyObject的類型轉(zhuǎn)換

Swift為不確定類型提供了兩種特殊類型別名:

  • AnyObject可以代表任何class類型的實(shí)例。
  • Any可以表示任何類型,除了方法類型(function types)。

注意: 只有當(dāng)你明確的需要它的行為和功能時(shí)才使用AnyAnyObject。在你的代碼里使用你期望的明確的類型總是更好的。

AnyObject類型

當(dāng)需要在工作中使用 Cocoa APIs,它一般接收一個(gè)AnyObject[]類型的數(shù)組,或者說(shuō)“一個(gè)任何對(duì)象類型的數(shù)組”。這是因?yàn)?Objective-C 沒(méi)有明確的類型化數(shù)組。但是,你常常可以確定包含在僅從你知道的 API 信息提供的這樣一個(gè)數(shù)組中的對(duì)象的類型。

在這些情況下,你可以使用強(qiáng)制形式的類型轉(zhuǎn)換(as)來(lái)下轉(zhuǎn)在數(shù)組中的每一項(xiàng)到比 AnyObject 更明確的類型,不需要可選解析(optional unwrapping)。

下面的示例定義了一個(gè) AnyObject[] 類型的數(shù)組并填入三個(gè)Movie類型的實(shí)例:

    let someObjects: AnyObject[] = [
        Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
        Movie(name: "Moon", director: "Duncan Jones"),
        Movie(name: "Alien", director: "Ridley Scott")
    ]

因?yàn)橹肋@個(gè)數(shù)組只包含 Movie 實(shí)例,你可以直接用(as)下轉(zhuǎn)并解包到不可選的Movie類型(ps:其實(shí)就是我們常用的正常類型,這里是為了和可選類型相對(duì)比)。

    for object in someObjects {
        let movie = object as Movie
        println("Movie: '\(movie.name)', dir. \(movie.director)")
    }
    // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
    // Movie: 'Moon', dir. Duncan Jones
    // Movie: 'Alien', dir. Ridley Scott

為了變?yōu)橐粋€(gè)更短的形式,下轉(zhuǎn)someObjects數(shù)組為Movie[]類型來(lái)代替下轉(zhuǎn)每一項(xiàng)方式。

    for movie in someObjects as Movie[] {
        println("Movie: '\(movie.name)', dir. \(movie.director)")
    }
    // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
    // Movie: 'Moon', dir. Duncan Jones
    // Movie: 'Alien', dir. Ridley Scott

Any類型

這里有個(gè)示例,使用 Any 類型來(lái)和混合的不同類型一起工作,包括非class類型。它創(chuàng)建了一個(gè)可以存儲(chǔ)Any類型的數(shù)組 things。

    var things = [Any]()
    things.append(0)
    things.append(0.0)
    things.append(42)
    things.append(3.14159)
    things.append("hello")
    things.append((3.0, 5.0))
    things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
    things.append({ (name: String) -> String in "Hello, \(name)" })

things 數(shù)組包含兩個(gè) Int 值,2個(gè) Double 值,1個(gè) String 值,一個(gè)元組 (Double, Double) ,Ivan Reitman 導(dǎo)演的電影“Ghostbusters”。

你可以在 switch cases里用isas 操作符來(lái)發(fā)覺(jué)只知道是 AnyAnyObject的常量或變量的類型。 下面的示例迭代 things數(shù)組中的每一項(xiàng)的并用switch語(yǔ)句查找每一項(xiàng)的類型。這幾種switch語(yǔ)句的情形綁定它們匹配的值到一個(gè)規(guī)定類型的常量,讓它們可以打印它們的值:

    for thing in things {
        switch thing {
        case 0 as Int:
            println("zero as an Int")
        case 0 as Double:
            println("zero as a Double")
        case let someInt as Int:
            println("an integer value of \(someInt)")
        case let someDouble as Double where someDouble > 0:
            println("a positive double value of \(someDouble)")
        case is Double:
            println("some other double value that I don't want to print")
        case let someString as String:
            println("a string value of \"\(someString)\"")
        case let (x, y) as (Double, Double):
            println("an (x, y) point at \(x), \(y)")
        case let movie as Movie:
            println("a movie called '\(movie.name)', dir. \(movie.director)")
        case let stringConverter as String -> String:
            println(stringConverter("Michael"))
        default:
            println("something else")
        }
    }
    // zero as an Int
    // zero as a Double
    // an integer value of 42
    // a positive double value of 3.14159
    // a string value of "hello"
    // an (x, y) point at 3.0, 5.0
    // a movie called 'Ghostbusters', dir. Ivan Reitman
    // Hello, Michael

注意: 在一個(gè)switch語(yǔ)句的case中使用強(qiáng)制形式的類型轉(zhuǎn)換操作符(as, 而不是 as?)來(lái)檢查和轉(zhuǎn)換到一個(gè)明確的類型。在 switch case 語(yǔ)句的內(nèi)容中這種檢查總是安全的。