2016-05-30 15:39:05 xxh0307 阅读数 663

前言

类型转换 可以判断实例的类型,也可以将实例看作是父类或者子类的实例。

类型转换 在 Swift 中使用 isas 操作符实现,当然也包括后面加叹号 ! 的强制展开和后面加问号 ? 的可选类型。这两个操作符提供了一种简单达意的方式去检查值的类型或是转换它的类型。

  • 定义一个类层次作为例子
  • 检查类型
  • 向下转换 Downcasting
  • AnyAnyObject 的类型转换

也可以用类型转换来检查一个类是否实现了某个协议。这个先知道就好,后面会有详细的介绍。

这一小节有几个比较生疏的名字,譬如类层次、检查类型、向下转型等,其实在 Obejctive-C 中,这些看起来陌生的名字我们都是经常使用的,类层次就是 继承类之后子类和父类之间的关系,检查类型 is 关键字和 Obejctive-C 中的 isKindOf(_) 功能类似,而向下转化就是我们所说的强制转换,例如下面的示例代码,就是将子类强转为父类:(这个是向上转换,至于这小节说的向下转换的例子,一时没想到,并且下面的示例代码对于向下转换表述的很好。其实主要是先搞明白什么是转换。)

 UILabel *myLabel = [[UILabel alloc]init];   
 UIView *myView = (UIView *)myLabel
 myView.frame = CGRectMake(10, 10, 100, 100);

只不过在 Swift 换了个方式,使用 as 关键字来解决这个问题。

分条详述

  1. 定义一个类层次作为例子

    可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个结构层次中的其他类型。下面的示例代码就是定义了一个父类和对应的两个子类:

    //  定义一个基类,媒体资源类,包含一个名字
    class MediaItem {
    var name: String   //  假设继承它的所有子类都有 name 这个属性
    //  构造器
    init(mediaName name: String) {
        self.name = name
    }
    }
    //  Movie 子类
    class Movie: MediaItem {
    //  导演属性
    var director: String
    //  构造器,作品名和导演名
    init(movieName name: String, movieDirector director: String ) {
        self.director = director
        super.init(mediaName: name)     //  继承
    }
    }
    //  Song 子类
    class Song: MediaItem {
    //  艺术家属性
    var artist: String
    //  构造器
    init(songName name: String, songArtist artist: String) {
        self.artist = artist
        super.init(mediaName: name)
    }
    }

    上面的几行代码就定义了一个父类和两个子类,下面创建一个数组常量 library ,包含 MovieSong 类型数据。

    注意,这里并没有去指定数组内数据的类型,当然我们知道,数组内的数据类型必须是一致的,由于 MovieSong 都继承自 MediaItem ,所以由内容推断出 [MediaItem] 类作为 library 的类型:

    //  定义一个资源数组
    let library = [Movie(movieName: "美人鱼", movieDirector: "周星驰"),
                  Movie(movieName: "我的特工爷爷", movieDirector: "洪金宝"),
                  Song(songName: "Five Hundred Miles", songArtist: "Justin"),
                  Song(songName: "Hello", songArtist: "Adele"),
                  Movie(movieName: "战狼", movieDirector: "吴京")]

    这里在多说一点,虽然 library 中的数据都是 [MediaItem] 类型,但是数组内存储的媒体依然是 MovieSong 。如果要迭代这个数组,依次取出的实例是 [MediaItem] 类型的,而不是 MovieSong 类型。为了让他们作为原本的类型工作,那么就需要类型转换。

  2. 类型检查

    用类型检查操作符 is 来检查一个实例是否属于特定子类型。如果属于那个子类型,类检查操作符返回 true ,否则返回 false

    var movieCount = 0      //  数组内电影资源数量
    var songCount = 0       //  数组内歌曲资源数量
    for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
    }
    //  打印查看数组内不同资源数目
    print( "movieCount = \(movieCount) , songCount = \(songCount)")
    //  输出: movieCount = 3 , songCount = 2
  3. 向下转型

    某类型的一个常量或变量可能在幕后属于另一个子类。当确定这种情况时,就可以尝试向下转换到它的子类型,用类型转换操作符 as! 或者 as?

    因为向下转型可能会失败,类型转换操作符带有两种不同形式。条件形式 as? 返回一个试图向下转成的类型的可选值 optional value强制形式 as! 把试图向下转型和强制解包 force-unwraps 结果作为一个混合动作。

    当不确定向下转型是否可以成功时,用类型转换的条件形式 as? 。条件形式的类型转换总是返回一个可选值,并且若向下转换是不可能的,可选值将是 nil 。这使我们能够检查向下转换是否成功。

    只有确定向下转化一定成功时,才使用强制形式 as! 。当试图向下转型为一个不正确的类型时,强制形式的类型转化会触发一个运行时的错误。

    至于什么时候用条件形式,什么时候用强制形式,这个需要实际开发中去总结。举个例子吧,例如自定义的 UICollectionViewCell ,这里显然用强制形式更合适些。

    let myCell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCollectionViewCell

    而下面的这个例子,就必须要用条件形式了,因为我们并不确定数据到底是什么类型的:

    //  向下转换
    for item in library {
    if let movie = item as? Movie {
        print(movie.name, movie.director)
    } else if let song = item as? Song {
        print(song.name, song.artist)
    }
    }
    //  输出:
    美人鱼 周星驰
    我的特工爷爷 洪金宝
    Five Hundred Miles Justin
    Hello Adele
    战狼 吴京

    let movie = item as? Movie 这行代码可理解为 尝试将 item 转换为 Movie 类型。若成功,设置一个新的临时常量 movie 来存储返回的可选 Movie

    注意,转换并没有真的改变它的实例或它的值。潜在的、根本的实例保持不变,只是简单地把它作为它被转换成的类来使用。

  4. AnyAnyObject 的类型转换

    Swift 为不确定的类型提供了两种特殊的类型别名:

    • AnyObject 可以代表任何 class 类型的实例。
    • Any 可以表示任何类型,包括方法类型 function types

    注意,只有当明确的需要它的行为和功能时才使用 AnyAnyObject 。在代码中使用自己期望的明确的类型往往是更好的,所以不是迫不得已,还是少用这两个类型的好。

    • AnyObject 类型

      当我们在工作中使用 Cocoa APIs ,我们一般会接收一个 [AnyObject] 类型的数组,或者说 一个任何对象类型的数组 。这是因为 Objective-C 没有明确的类型化数组。但是,常常可以从 API 提供的信息中清晰的确定数组中对象的类型。

      在这些情况下,可以使用强制形式的类型转换来向下转换数组中每一项 到 比 AnyObject 更明确地类型,不需要条件形式的可选解析 optional unwrapping

      下面示例代码展示强制形式的类型转换,个人理解这仅仅是个展示用法的代码,实际开发中明明知道数组内的类型,应该不会用 AnyObject 去替代吧。我在开发中遇到的一种需要使用 AnyObject 的情况是开发一个无限轮播控件,轮播的数据从外部获取,但是这个数据并不是唯一的,有可能是本地图片数据 Image,也可能是一堆的图片链接 String,也可能是接收到一个数据 Model ,这个时候用于接收的数据内部就是使用的 AnyObject ,然后再去判断数据类型,做不同的操作。

      //  已知数组内部是 Movie 类型的数据
      let someObjects: [AnyObject] = [Movie(movieName: "美人鱼", movieDirector: "周星驰"),
                                  Movie(movieName: "我的特工爷爷", movieDirector: "洪金宝"),
                                  Movie(movieName: "战狼", movieDirector: "吴京")]
      //  遍历时可以直接强制形式的类型转换
      for object in someObjects {
      let movie = object as! Movie
      print(movie.name, movie.director)
      }
      //  其实还有更简单的转化方式,就是直接向下转换整个数组
      for object in someObjects as! [Movie] {
      print(object.name, object.director)
      }
    • Any 类型

      这里仅仅展示一个示例,使用 Any 类型来混合不同的数据类型一起工作,包括方法类型和非 class 类型。创建一个可以存储 Any 类型的数组 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)" })

      这个数组包含2个 Int 值,2个 Double 值,1个 String 值,1个元组 (Double, Double) ,1个 Movie,和一个获取 String 值并返回另一个 String 值得闭包表达式。

      然后利用 switch 表达式中的 case 语句,并结合 isas 操作符来操作数组内部的数据,简单来说就是下面这个样子的(下面的示例代码是全部copy教材中的)

      for thing in things {
      switch thing {
      case 0 as Int:
          print("zero as an Int")
      case 0 as Double:
          print("zero as a Double")
      case let someInt as Int:
          print("an integer value of \(someInt)")
      case let someDouble as Double where someDouble > 0:
          print("a positive double value of \(someDouble)")
      case is Double:
          print("some other double value that I don't want to print")
      case let someString as String:
          print("a string value of \"\(someString)\"")
      case let (x, y) as (Double, Double):
          print("an (x, y) point at \(x), \(y)")
      case let movie as Movie:
          print("a movie called '\(movie.name)', dir. \(movie.director)")
      case let stringConverter as String -> String:
          print(stringConverter("Michael"))
      default:
          print("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

总结

重点是掌握检查类型 is 和 向下转换 as?as! 的用法。

吃多了狗粮,好累

2016-05-12 12:11:41 lovexieyuan520 阅读数 1396

类型转换在Java和Swift中非常的相似,这个一般都是在类中, 可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。也可以用它来检查一个类型是否实现了某个协议,协议相当于Java中的接口。

检查类型(Checking Type)

用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false。Java中使用instanceof关键字来类型检查。
swift:

    if item is Movie {
        ++movieCount
    } else if item is Song {
        ++songCount
    }

Java:

        if(item instanceof Movie) {
            ++movieCount
        } else if(item instanceof Song) {
            ++songCount
        }

向下转型(Downcasting)

某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as? 或 as!)。
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form)as? 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 as! 把试图向下转型和强制解包(force-unwraps)转换结果结合为一个操作。

当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 nil。这使你能够检查向下转型是否成功。

只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
swift:

    if let movie = item as? Movie {
        print("Movie: '\(movie.name)', dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: '\(song.name)', by \(song.artist)")
    }

Java:

Movie movie = ((Movie)item)

很像吧。

Any 和 AnyObject 的类型转换

Swift 为不确定类型提供了两种特殊的类型别名:

  • AnyObject 可以表示任何类类型的实例。
  • Any 可以表示任何类型,包括函数类型。

Java中所有类都是Object类的子类,所以Object可以表示任何类型的实例,包括函数类型,不区分类类型和其他类型。
这个很简单,不必多说。

2016-04-21 14:22:24 qq_26463549 阅读数 382

swift 中的数组
· Array 数组
· 使用 [ ] 操作符声明数组

//数组
let myArray1 = []          //声明空数组

//let myArray2 = String[]() //这种方式会出现以下异常
//instance member 'subscript' cannot be used on type 'String'

var shoppingList = ["mac","ipad","iphone"]  //声明数组

//数组中的常用方法
print(shoppingList.count)       //数组的个数
print(shoppingList.capacity)    //数组的容量(容量的值>=count,并且是2的次方)
print(shoppingList.isEmpty)     //数组是否为空

//增加
shoppingList.append("ipod")     //给数组增加一个元素
print(shoppingList)             //输出这个数组的元素

shoppingList.insert("iMac", atIndex:2)  //插入一个元素,注意atIndex
print(shoppingList)        //输出插入元素后的新数组元素

//shoppingList += "a"
//在学习中看到,这种方式也是可以添加元素的,但是在我这里是会出现异常的,
//cannot convert value of type '[String]' to expected argument type 'inout String'

shoppingList += ["b","c"]  //再次增加两个元素
print(shoppingList)

//修改
print(shoppingList[1])     //输入list下标为1的值
shoppingList[1] = "ipad air"  //给list中下标为1的重新赋值
print(shoppingList[1])     //输出

shoppingList[2...4] = ["1","2","3"]  //把下标是2,3,4的元素替换成["1","2","3"]
print(shoppingList)

shoppingList[2...4] = ["1","2"]  //把下标是2,3,4的元素替换成["1","2"],这将意味着除了可以替换值以外,还可以改变数组的个数
print(shoppingList)

//删除
shoppingList.removeLast()  //移除最后一个元素
print(shoppingList)
shoppingList.removeAtIndex(2)  //移除下标为2的元素删除,在我这里应该是元素 1 
print(shoppingList)
//shoppingList.removeAll()   //移除所有元素
print(shoppingList)

//数组的遍历1
for item in shoppingList{
    print(item)
}

//数组的遍历2
//for (index, value) in enumerate(shoppingList){   //enumerate()这个方法是我学习过程中,看到的,用在这里出现了如下异常,
//'enumerate' is unvailable: call the 'enumerate()' method on the sequence
// 随后我使用EnumerateSequence()方法替换,虽然可以出效果,但是还是出现了如下警告。
//'init' is deprecated: it will be removed in Swift 3. Use the 'enumerate()' method...
//    print("Item\(index + 1):\(value)")
//}

Dictionary 字典

· 使用 [key : value] 操作符声明字典

//Dictionary 字典
//使用 [key : value] 操作符声明字典
let emptyDictionary = [:]   //声明一个空的字典
let emptyDictionary1 = Dictionary<String,Float>()  //声明一个空的字典,key为String类型,value为Float类型

var letter = ["A":"a","B":"b"]   //就是键值对
print(letter)

//添加、修改
letter ["A"] = "aa"    //查找原来的字典,如果包含这个key,那么就把这个字典对应的value进行替换。如果没有找到,即添加
print(letter)

letter ["C"] = "c"
print(letter)

//删除
letter["C"] = nil   //第一种方法
print(letter)

letter.removeValueForKey("B")   //第二种方法
print(letter)

//输出count
print("letter count is " + String(letter.count))
print("letter count is \(letter.count)")

//Dictionary 遍历
for (key , value) in letter{
    print("\(key) : \(value)")
}

//遍历所有的key
for key in letter.keys{
    print(key)
}

//遍历所有的value
for value in letter.values{
    print(value)
}

let Allkeys = Array(letter.keys)   //把所有的key转换为数组
print(Allkeys)
let Allvalues = Array(letter.values)  //把所有的value转换为数组
print(Allvalues)

以上就是关于数组和字典的一些基本的知识,由于我自己也是初学,所以有些东西(例如本文中的一些异常),该怎么解决。还不是很明白。希望大家可以互相交流。共同进步!

2014-06-20 08:51:33 GooHong 阅读数 2301

一 类型检查

1、 类型检查操作符

      类型检查用来检查或转换一个实例的类型到另外的类型的一种方式。

      在Swift中,类型检查使用is和as操作符来实现。

      is操作符用来检查一个实例是否是某种特定类型,如果是返回true,否则返回false。

      as操作符用来把某个实例转型为另外的类型,由于实例转型可能失败,因此Swift为as操作符提供了两种形式:选项形式as?和强制形式as。

      选项形式(as?)的操作执行转换并返回期望类型的一个选项值,如果转换成功则返回的选项包含有效值,否则选项值为 nil

      强制形式(as )的操作执行一个实例到目的类型的强制转换,因此使用该形式可能触发一个运行时错误。

      如下例所示。

class MediaItem {

   var name:String

   init(name:String) {

       self.name =name

    }

}

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)

    }

}

let library = [

   Movie(name:"Casablanca",director:"Michael Curtiz"),

   Song(name:"Blue Suede Shoes",artist:"Elvis Presley"),

Astley")

]

var movieCount =0

var songCount =0 

for item in library {

   if item is Movie {

        ++movieCount

    }else if item is Song {

        ++songCount

    }

}

for item in library {

   if let movie =item asMovie {

       println("Movie: '\(movie.name)', dir. \(movie.director)")

    }else if let song =item asSong {

       println("Song: '\(song.name)', by \(song.artist)")

    }

}

     本例首先定义了一个媒体类的继承树,其中两个类Movie和Song都继承自其基类MediaItem,接着定义一个包含这两个媒体项实例的数组library,然后在for in循环中使用is操作符来检查某个媒体项是否是特定类型的实例,使用as操作符的选项形式as?来转换媒体项的实例为特定类型的实例。

2、 任意类型的使用

  Swift提供了两个特定的类型别名:AnyObject与Any。

  AnyObject类型能代表任何类类型。

  Any类型能代表任意除了函数类型之外的所有其它类型。

  例子展示了如何使用AnyObject类型定义一个AnyObject类型的数组以及该数组的使用,AnyObject类型的数组成员可以是任意的类的实例,例子中为Movie类的实例。

let someObjects:AnyObject[] = [

   Movie(name:"2001: A Space Odyssey",director:"Stanley Kubrick"),

   Movie(name:"Moon",director:"Duncan Jones"),


]

for object in someObjects {

   let movie =object as Movie  

   println("Movie: '\(movie.name)', dir. \(movie.director)")

}

//以上循环使用as操作符强制转换一个类型为AnyObject的实例为确定的类型Movie类的实例,因此可能出现运行时错误。

 //也可以直接使用as 操作符直接把someObjects类型的数组转型为Movie类型的数组,如下:

 for movie in someObjects as Movie[] {

   println("Movie: '\(movie.name)', dir. \(movie.director)")

}

     如下例子展示Any的使用,可以使用Any来代替任何类型。

var things =Any[]() 

things.append(0)

things.append(0.0)

things.append("hello")

things.append((3.0,5.0))

things.append(Movie(name:"Ghostbusters",director:"Ivan Reitman"))

Any类型的数组可以包含任意类型的实例,如本例Any类型的数组things包含一个整数、浮点数、字符串、多元组、类的实例。   

二  类型嵌套     

     类型嵌套允许你在一种类型中嵌套定义另外一种类型。在另外类型中定义的类型称为嵌套类型,嵌套类型定义在它的支持类型内部。类型能够多级嵌套,嵌套类型内部可以再嵌套定义另外的类型,如下例子所示:

    struct BlackjackCard { 

   // nested Rank enumeration

   enum Rank:Int {

   case Jack, Queen, King, Ace

   // nested Values struct

   struct Values {

           let first:Int,second:Int?

        }

    }   

}

      使用点语法来存取一个嵌套类型的属性、方法、或下标。

      从嵌套类型的最外一级类型开始,一级级地向内获得要查询的嵌套类型的属性、方法、或下标。

      如下所示:

   let jackSymbol =BlackjackCard.Rank.Jack.toRaw()


2016-06-26 22:06:13 ALLsharps 阅读数 407

类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。

类型转换在 Swift 中使用 is 和 as 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。

你也可以用它来检查一个类是否实现了某个协议,就像在 检验协议的一致性部分讲述的一样。

定义一个类层次作为例子

你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。

第一个代码片段定义了一个新的基础类 MediaItem。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 String 类型的 name 属性,和一个 init name 初始化器。(假定所有的媒体项都有个名称。)

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

下一个代码段定义了 MediaItem 的两个子类。第一个子类 Movie 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 director(导演)属性,和相应的初始化器。第二个子类 Song,在父类的基础上增加了一个 artist(艺术家)属性,和相应的初始化器:

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)
    }
}

最后一个代码段创建了一个数组常量 library,包含两个 Movie 实例和三个 Song 实例。library 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 Movie 和 Song有共同的父类 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 里存储的媒体项依然是 Movie 和 Song 类型的。但是,若你迭代它,依次取出的实例会是 MediaItem 类型的,而不是 Movie 和 Song 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。

检查类型(Checking Type)

用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true,否则返回 false

下面的例子定义了两个变量,movieCount 和 songCount,用来计算数组 library 中 Movie 和 Song 类型的实例数量。

var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        ++movieCount
    } else if item is Song {
        ++songCount
    }
}

print("Media library contains \(movieCount) movies and \(songCount) songs")
// prints "Media library contains 2 movies and 3 songs"

示例迭代了数组 library 中的所有项。每一次,for-in 循环设置 item 为数组中的下一个MediaItem

若当前 MediaItem 是一个 Movie 类型的实例,item is Movie 返回 true,相反返回 false。同样的,item is Song 检查item是否为 Song 类型的实例。在循环结束后,movieCount 和 songCount 的值就是被找到属于各自的类型的实例数量。

向下转型(Downcasting)

某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(as? 或 as!)

因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) as? 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 as! 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。

当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 nil。这使你能够检查向下转型是否成功。

只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。

下面的例子,迭代了 library 里的每一个 MediaItem,并打印出适当的描述。要这样做,item 需要真正作为 Movie 或 Song 的类型来使用,不仅仅是作为 MediaItem。为了能够在描述中使用 Movie 或 Song的 director 或 artist 属性,这是必要的。

在这个示例中,数组中的每一个 item 可能是 Movie 或 Song。事前你不知道每个 item 的真实类型,所以这里使用条件形式的类型转换(as?)去检查循环里的每次下转。

for item in library {
    if let movie = item as? Movie {
        print("Movie: '\(movie.name)', dir. \(movie.director)")
    } else if let song = item as? Song {
        print("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 下转为 Movie。因为 item 是一个 MediaItem 类型的实例,它可能是一个Movie;同样,它也可能是一个 Song,或者仅仅是基类 MediaItem。因为不确定,as?形式在试图下转时将返回一个可选值。item as? Movie 的返回值是 Movie? 或 “可选 Movie”类型。

当向下转型为 Movie 应用在两个 Song 实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 Movie 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“if let movie = item as? Movie”,可以这样解读:

“尝试将 item 转为 Movie 类型。若成功,设置一个新的临时常量 movie 来存储返回的可选 Movie

若向下转型成功,然后 movie 的属性将用于打印一个 Movie 实例的描述,包括它的导演的名字 director。相近的原理被用来检测 Song 实例,当 Song 被找到时则打印它的描述(包含 artist 的名字)。

注意:
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。

AnyAnyObject的类型转换

Swift为不确定类型提供了两种特殊类型别名:

  • AnyObject可以代表任何class类型的实例。
  • Any可以表示任何类型,包括方法类型(function types)。

注意:
只有当你明确的需要它的行为和功能时才使用AnyAnyObject。在你的代码里使用你期望的明确的类型总是更好的。

AnyObject类型

当在工作中使用 Cocoa APIs,我们一般会接收一个[AnyObject]类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。

在这些情况下,你可以使用强制形式的类型转换(as)来下转在数组中的每一项到比 AnyObject 更明确的类型,不需要可选解析(optional unwrapping)。

下面的示例定义了一个 [AnyObject] 类型的数组并填入三个Movie类型的实例:

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

因为知道这个数组只包含 Movie 实例,你可以直接用(as!)下转并解包到不可选的Movie类型:

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

为了变为一个更短的形式,下转someObjects数组为[Movie]类型来代替下转数组中每一项的方式。

for movie in someObjects as! [Movie] {
    print("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类型

这里有个示例,使用 Any 类型来和混合的不同类型一起工作,包括方法类型和非 class 类型。它创建了一个可以存储Any类型的数组 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 数组包含两个 Int 值,2个 Double 值,1个 String 值,一个元组 (Double, Double) ,电影“Ghostbusters”,和一个获取 String 值并返回另一个 String 值的闭包表达式。

你可以在 switch 表达式的cases中使用 is 和 as 操作符来发觉只知道是 Any 或 AnyObject 的常量或变量的类型。下面的示例迭代 things 数组中的每一项的并用switch语句查找每一项的类型。这几种switch 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印:

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called '\(movie.name)', dir. \(movie.director)")
    case let stringConverter as String -> String:
        print(stringConverter("Michael"))
    default:
        print("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

注意:
在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。

Swift3.0 类型检查

阅读数 2078

学习swift笔记

阅读数 1

没有更多推荐了,返回首页