Part 2:(ARC,Optional Chaining,Type Casting)
来源:互联网 发布:linux防火墙添加规则 编辑:程序博客网 时间:2024/06/05 22:54
16Automatic Reference Counting
swift使用arc来管理应用的内存,大部分情况下,你不需要考虑内存的管理。arc会自动为你清理内存。
但是某些情况下,arc需要了解你代码的更多信息来管理内存。这章中将会讲到。
注意:arc只应用在类实例上,结构体和枚举是值类型,没用引用计数,他们不是通过引用来保存和传递的。
16.1How ARC Works
每次你创建一个类实例,arc开辟一块内存来保存实例的信息。这块内存中有实例的类型信息,还有实例的各个属性值的信息。
另外,当一个实例不再需要,arc释放这个实例的内存以便他用。这能确保实例不用时不再占用内存。
但是,如果实例还在使用,arc就把内存释放了,那将没法再访问实例的属性,没法再使用实例的方法。如果你访问那个对象,你的程序很可能就崩溃。
为了确保实例在需要时没有被释放,arc会跟踪有多少属性,常量,变量当前在引用这个变量,只要有一个引用存在,arc就不会释放这个变量。
为了达到这个目的,当你赋值一个实例给属性,常量或变量,那个属性,常量或变量将有一个强引用(strong reference)指向这个变量。之所以叫强,是因为它抓住了这个实例,使他不会被释放。
16.2ARC in Action
class Person {
let name: String
init(name: String) {
self.name = name
println("\(name) is being initialized")
}
deinit {
println("\(name) is being deinitialized")
}
}
var reference1:Person?
var reference2:Person?
var reference3:Person?
reference1 = Person(name:"John Appleseed")
// prints "John Appleseed is being initialized"
//这时候有reference1强引用,person实例对象不会被释放
reference2 = reference1
reference3 = reference1
//这时候有3个强引用指向person实例
reference1 = nil
reference2 = nil
reference3 = nil
// prints "John Appleseed is being deinitialized"
//只有3个强引用都置nil了,这时没有person对象没有引用了,person对象才被释放
//注意在playground中是没有看到这一行的,但是如果把代码放大project里面去执行,就可以看到这一行。
16.3Strong Reference Cycles Between Class Instances
//对象间的引用循环
//在上面的例子中,arc跟踪引用的数量,并在实例不需要的时候释放对象
//但是如果两个实例互相引用,这将造成引用循环,从而两个实例都不得释放
//解决的方法是让强引用变成弱引用或不拥有引用(weak or unowned),这个后面会讲
//先来看看一个房客与套间的故事
class Person {
let name: String
init(name: String) {self.name = name }
var apartment: Apartment?
deinit {
println("\(name) is being deinitialized")
}
}
//房客有名字和套间属性
class Apartment {
let number: Int
init(number: Int) {self.number = number }
var tenant: Person?
deinit {
println("Apartment #\(number) is being deinitialized")
}
}
//套间有编号和房客属性
var john:Person?
var number73:Apartment?
john = Person(name:"John Appleseed")
number73 = Apartment(number:73)
//一个房客
//一个套间
john!.apartment = number73
number73!.tenant =john
//房客住这间套间
//套间有这位房客
john = nil
number73 = nil
//房客。。。。
//套间。。。。
//注意这时候的结果,房客还在,套间还在,只不过没被上面的变量引用而已,他们两个实例互相引用,arc没法释放他们。额。。。内存泄露。
16.4Resolving Strong Reference Cycles Between Class Instances
swift提供两种方法来解决强引用循环:弱引用和不拥有引用(weak reference and unowned reference)
弱引用和不拥有引用使得一个实例在引用循环中引用另一个实例而不去hold住它。这使得对象能互相引用而不造成强引用循环。
如果在它的生命期里引用可能被置nil,那么使用弱引用。相反,如果你知道这个引用在初始化时设置后就不会再被置nil,那么使用不拥有引用。
16.4.1Weak References
弱引用不会hold住引用对象,所以不会阻止arc释放对象。不会照成强引用循环。在属性和变量前写weak以指明是弱引用。
为了避免引用循环,如果引用可能没有值,那么使用弱引用,如果引用肯定有值,那么使用不拥有引用。
在上面的例子里面,套间可能没有租客,所以适合用弱引用。
注意:弱引用必须是变量,因为它的值可能被改变。
弱类型可能没有值,所以你必须把它声明成可选类型。在swift中你应该用可选类型来代表它可能没有值。
弱引用并不hold住实例,所以它引用的对象可能会被释放。因此,在引用对象被释放时,arc自动将引用设置为nil。
下面对上面的房客与套间的故事进行改造
class Person {
let name: String
init(name: String) {self.name = name }
var apartment: Apartment?
deinit {println("\(name) is being deinitialized") }
}
class Apartment {
let number: Int
init(number: Int) {self.number = number }
weak var tenant:Person?
deinit {println("Apartment #\(number) is being deinitialized") }
}
func test1(){
var john: Person?
var number73: Apartment?
john =Person(name: "John Appleseed")
number73 =Apartment(number: 73)
john!.apartment = number73
number73!.tenant = john
john =nil
// prints "John Appleseed is being deinitialized"
number73 =nil
// prints "Apartment #73 is being deinitialized"
}
//test1()
func test2(){
var john: Person?
var number73: Apartment?
john =Person(name: "John Appleseed")
number73 =Apartment(number: 73)
john!.apartment = number73
number73!.tenant = john
number73 =nil
john =nil
// prints "John Appleseed is being deinitialized"
// prints "Apartment #73 is being deinitialized"
}
test2()
16.4.2Unowned References
与弱引用类似,不拥有引用不会抓住实例不让释放。
与弱引用不同,不拥有引用假设一直有值。
所以,不拥有引用一般不定义成可选值。
使用unowned来标明属性和变量是不拥有的引用
因为不拥有引用是不可选类型(non-optinal),所以在使用时不需要展开它。然而当它所引用的对象被释放时,arc是不会把引用置为nil的,因为引用不可选,无法置nil。
注意:但对象被释放后,访问不拥有引用将触发运行时错误。只有在你确定这个引用肯定会指向一个实例时才使用不拥有引用。
//下面例子定义两个类,客户和信用卡。这两个类都有一个属性指向对方。
//客户和信用卡的关系与房客和套间的关系有点不同,在这里客户可以没有信用卡,但是信用卡必须对应一个客户。所以客户中得信用卡属性是可选类型,但信用卡中的客户属性是不可选类型。所以这边定义成不拥有引用
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {println("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer:Customer
init(number: UInt64, customer:Customer) {
self.number = number
self.customer = customer
}
deinit {println("Card #\(number) is being deinitialized") }
}
//定义一名客户,一张卡
var john:Customer?
john = Customer(name:"John Appleseed")
john!.card =CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
// prints "John Appleseed is being deinitialized"
// prints "Card #1234567890123456 is being deinitialized"
16.4.3Unowned References and Implicitly Unwrapped Optional Properties
//不拥有引用和隐式展开的可选属性
//房客与套间的故事,说明了一件事情,当两个属性都可以为nil,如果他们可能导致强引用循环,那么最好的办法是使用弱引用。
//客户与信用卡的关系说明,如果一个属性可以是nil,而另一个属性不能是nil,如果他们可能导致强引用循环,那么最好的办法是不拥有引用。
//但是还有一种情况,如果两个属性都必须有值,都不能为nil。这种情况下,配合使用不拥有属性和隐式展开可选属性。
//这样子两个属性都可以直接访问(可选属性使用时要展开),并且可以避免强引用循环。
//下面例子是国家与城市的关系,国家有首都,城市必须属于某个国家。
class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName:String) {
self.name = name
self.capitalCity =City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country:Country
init(name: String, country:Country) {
self.name = name
self.country = country
}
}
var country =Country(name: "Canada", capitalName:"Ottawa")
println("\(country.name)'s capital city is called \(country.capitalCity.name)")
// prints "Canada's capital city is called Ottawa"
//上面使用了隐式展开可选以满足两阶段构造器的要求。不仅首都属性可以像不可选方式使用,还避免了强引用循环。
16.5Strong Reference Cycles for Closures
如果你给类的一个属性赋值闭包,那么强引用循环也可能发生。你可能在闭包里面访问对象的某个属性,某个方法,这将导致闭包抓住对象(“capture”self),从而形成强引用循环。
闭包跟类一样,都是引用类型。当你给属性赋值闭包,也是给属性赋值闭包的一个引用。这个问题本质上跟上面的问题是一样的,两个强引用互相抓住对方。所不同的是这次是一个类实例和一个闭包罢了。
下面看个栗子
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () ->String = {
if let text =self.text {
return "<\(self.name)>\(text)</\(self.name)>"
}else {
return "<\(self.name) />"
}
}
init(name: String, text:String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
//The asHTML property is of type () -> String, or “a function that takes no parameters, and returns a String value”.
//asHTML是一个无参数的返回String类型的函数
//asHTML使用起来像是一个实例方法,但因为它是一个闭包属性,你可以对它重新赋值。
//只有真正需要其返回一个字符串值时才需要使用asHTML,所以声明成懒属性,实际上这也意味着你可以在闭包内使用self,因为懒属性都是初始化完成后才会去访问的。
var paragraph:HTMLElement? = HTMLElement(name:"p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
paragraph = nil
//不幸的是上面这样写已经造成强引用循环,对象强引用闭包,闭包里面访问self,所以也强引用对象。所以在paragraph置nil后,对象没有被释放,析构函数中的日志没有打印。
16.6Resolving Strong Reference Cycles for Closures
解决闭包与实例间强引用的方法是定义捕获列表(capture list),捕获列表定义闭包内部捕获一个或多个引用类型的使用规则。就像两个对象间强引用循环一样,你可以声明捕获引用为弱引用或不拥有引用。
注意:swift要求你写self.someProperty 或 self.someMethod,而不是直接写someProperty 或 someMethod。主要是为了提醒你小心这里捕获self。
16.6.1Defining a Capture List
捕获列表的每一项是由weak或unowned关键字和引用组成的一对。每一对都用方括号括起来,用逗号隔开。
lazyvar someClosure: (Int,String) -> String = {
[unownedself] (index: Int, stringToProcess:String) -> Stringin
// closure body goes here
}
捕获列表放在参数和返回值的前面
lazyvar someClosure: () -> String = {
[unownedself] in
// closure body goes here
}
如果根据上下文可以推断出参数列表和返回值,那么捕获列表中也可以不写,直接写in
16.6.2Weak and Unowned References
如果闭包和实例总是互相引用,并在销毁时同时销毁,那么定义成unowned 引用。
如果被捕获的引用将来可能为nil,那么使用weak引用。弱引用总是可选类型的并在引用对象销毁时自动变成nil。
上面的例子中,应该用不拥有引用的方式来处理
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () ->String = {
[unownedself] in
if let text =self.text {
return "<\(self.name)>\(text)</\(self.name)>"
}else {
return "<\(self.name) />"
}
}
init(name: String, text:String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
//In this case, the capture list is [unowned self], which means “capture self as an unowned reference rather than a strong reference”.
var paragraph:HTMLElement? = HTMLElement(name:"p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
paragraph = nil
// prints "p is being deinitialized"
17Optional Chaining
给可选值访问属性,调用方法,访问下标时,如果这个可选值有值,那么返回成功,如果可选值为nil,那么返回nil。多次查询,调用则形成链,只要链中有一环为nil,那么整个就返回nil。
17.1Optional Chaining as an Alternative to Forced Unwrapping
在可选值后面接问号,形成可选链,然后访问属性,调用方法,下标。这有点像在可选值后面接感叹号,强制展开值。这两者的区别是当可选值为nil时,使用问号的方法可以死得很优雅,而用感叹号强制展开的方法却会触发运行时异常。
因为可选链中可能产生nil值,所以即使你访问的属性,方法,下标返回的是不可选值, 其结果也仍然是可选值。你可以用这个可选值来判断你的调用是否成功。
如果一个属性的返回值是Int,那么在可选链中,返回值就是Int?
//下面代码用一个人有几套房的事情来阐明用可选链和强制展开的区别
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john =Person()
//let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
//强制展开,触发异常
//使用可选的方式
if let roomCount =john.residence?.numberOfRooms {
println("John's residence has\(roomCount) room(s).")
}else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
//给他一套房
john.residence = Residence()
if let roomCount =john.residence?.numberOfRooms {
println("John's residence has\(roomCount) room(s).")
}else {
println("Unable to retrieve the number of rooms.")
}
// prints "John's residence has 1 room(s)."
17.2Defining Model Classes for Optional Chaining
你可以使用可选链来多层调用属性,方法,下标。访问属性的属性,也叫子属性。
下面的代码建立4个模型,包含多层的可选链。
//一个人,一个住所
class Person {
var residence: Residence?
}
//一个住所,一个地址,好多个房间
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) ->Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
println("The number of rooms is\(numberOfRooms)")
}
var address: Address?
}
//房间名称
class Room {
let name: String
init(name: String) {self.name = name }
}
//地址,楼名,楼号,街道
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if buildingName !=nil {
return buildingName
}else if buildingNumber != nil {
returnbuildingNumber
}else {
return nil
}
}
}
17.3Accessing Properties Through Optional Chaining
//创建一个人,并访问其住所有几个房间
let john =Person()
if let roomCount =john.residence?.numberOfRooms {
println("John's residence has\(roomCount) room(s).")
}else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
//因为还没住所,所以无法获取房间数
//有一个地址,是他住所的地址
let someAddress =Address()
someAddress.buildingNumber ="29"
someAddress.street ="Acacia Road"
john.residence?.address =someAddress
//其实他根本没住所,哪来的地址,所以会赋值失败,住所仍然为nil
17.4Calling Methods Through Optional Chaining
可以使用可选链在可选值上调用方法,并且检查方法调用是不是成功。即使方法没有返回值也照样可以。
在上面的例子中有个方法
func printNumberOfRooms() {
println("The number of rooms is\(numberOfRooms)")
}
这个方法没有标明返回值,默认是Void,
This means that they return a value of (), or an empty tuple
这意味着它将返回(),或者空tuple
在可选链上调用这个方法,返回值将是Void?。你照样可以用if语句来判断是否调用成功。
if john.residence?.printNumberOfRooms() != nil {
println("It was possible to print the number of rooms.")
}else {
println("It was not possible to print the number of rooms.")
}
// prints "It was not possible to print the number of rooms."
iflet ret: () = john.residence?.printNumberOfRooms(){
println("成功")
}else{
println("失败")
}
//失败
//同样的,如果你想在可选链上设置属性,上一节中尝试设置一个地址给一个住所是nil的属性,其返回值也是Void?类型,你也可以用if语句来判断是否设置成功
if (john.residence?.address =someAddress) != nil {
println("It was possible to set the address.")
}else {
println("It was not possible to set the address.")
}
// prints "It was not possible to set the address."
if let ret: () = (john.residence?.address = someAddress) {
println("成功")
}else{
println("失败")
}
//失败
17.5Accessing Subscripts Through Optional Chaining
可以使用可选链来获取和设置下标,并检查是否成功。
//注意问号的位置,在方括号前,不是在方括号后
iflet firstRoomName = john.residence?[0].name {
println("The first room name is\(firstRoomName).")
}else {
println("Unable to retrieve the first room name.")
}
// prints "Unable to retrieve the first room name."
john.residence?[0] =Room(name: "Bathroom")
//设置房间,因为住所为nil,所以照样失败
//有住所了,接下来的访问都是成功的
let johnsHouse =Residence()
johnsHouse.rooms.append(Room(name:"Living Room"))
johnsHouse.rooms.append(Room(name:"Kitchen"))
john.residence = johnsHouse
iflet firstRoomName = john.residence?[0].name {
println("The first room name is\(firstRoomName).")
}else {
println("Unable to retrieve the first room name.")
}
// prints "The first room name is Living Room."
17.5.1Accessing Subscripts of Optional Type
如果一个下标返回的就是可选类型,比如字典类型的key下标。那么在下标的方括号后面加问号形成链。
var testScores = ["Dave": [86,82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] =91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] =72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
17.6Linking Multiple Levels of Chaining
连接多层的可选链来访问深层次的属性,方法,和下标。当然了,返回值不会是多层可选。
换种说法:
如果你想访问的类型是不可选的,那么因为可选链返回值将是可选的。
如果你想访问的类型是可选的,它不会变成双层可选
因此:
如果你通过可选链访问一个Int类型,最终结果就是Int?,不管通过几层可选访问到它。
如果你通过可选链访问一个Int?类型,最终结果也是Int?,不管通过几层可选访问到它。
if let johnsStreet =john.residence?.address?.street {
println("John's street name is\(johnsStreet).")
}else {
println("Unable to retrieve the address.")
}
// prints "Unable to retrieve the address."
//当前住所有值,但是住所的地址为nil,所有最终结果是nil
let johnsAddress =Address()
johnsAddress.buildingName ="The Larches"
johnsAddress.street ="Laurel Street"
john.residence!.address =johnsAddress
if let johnsStreet =john.residence?.address?.street {
println("John's street name is\(johnsStreet).")
}else {
println("Unable to retrieve the address.")
}
// prints "John's street name is Laurel Street."
//赋值住所的地址后,就可以通过多层的可选链访问到地址
17.7Chaining on Methods with Optional Return Values
可选链中也可以有方法及其返回值
iflet buildingIdentifier = john.residence?.address?.buildingIdentifier() {
println("John's building identifier is\(buildingIdentifier).")
}
// prints "John's building identifier is The Larches."
iflet beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
println("John's building identifier begins with \"The\".")
}else {
println("John's building identifier does not begin with \"The\".")
}
}
// prints "John's building identifier begins with "The"."
18Type Casting
使用is和as操作符
18.1Defining a Class Hierarchy for Type Casting
//媒体
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"),
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]
//当你从媒体库中获取到媒体将都是MediaItem类型,你需要手动检查他们的类型,并向下转换他们的类型
18.2Checking Type
var movieCount =0
var songCount =0
for itemin library {
if item is Movie {
++movieCount
}else if itemis Song {
++songCount
}
}
println("Media library contains\(movieCount) movies and\(songCount) songs")
// prints "Media library contains 2 movies and 3 songs"
18.3Downcasting
某某类型的常量或变量可能实际引用的是他的子类的实例。如果你能确定,你就可以用as把它向下转型成子类。
向下转型可能失败,所以有两种向下转型的方式。as? 返回你转型目标类型的可选值。as 向下转型,并强制展开。
当你不确定是否成功的时候,请使用as? 。如果转型失败会返回nil,你可以通过转型结果判断转型是否成功。
如果你能确定转型能成功,你才使用as。否则,一旦转型失败,将触发运行时异常。
for itemin library {
if let movie = itemas? 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
18.4Type Casting for Any and AnyObject
swift提供了两种无类型的类型别名。
1)AnyObject可以代表任何类类型实例。
2)Any可以代表任何类型实例,包括函数类型。
注意:当你确实需要用到他们时采用,为实例指明更确切的类型总是更好的。
18.1AnyObject
如果需要配合Cocoa API,推荐使用[AnyObject],“一组任意类型值”。因为OC没有某种切确类型的数组。不过你可能可以知道这个数组里面包含的都是什么值。
这种情况下,你可以使用强制转换来向下转型,而不用可选转型,然后展开。
看下例:
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实例,我们可以向下转型并直接展开
for objectin 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
//一种更简单的方式,先整个数组向下转型,而不是取出来后一个一个转型
for movie insomeObjects 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
18.2Any
一个使用Any的混合不同类型的例子。包含函数类型和非类类型。
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) -> Stringin "Hello, \(name)" })
for thingin things {
switch thing {
case 0 as Int:
println("zero as an Int")
case 0 as Double:
println("zero as a Double")
case let someIntas Int:
println("an integer value of\(someInt)")
case let someDoubleas 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 someStringas String:
println("a string value of \"\(someString)\"")
case let (x, y)as (Double,Double):
println("an (x, y) point at\(x), \(y)")
case let movieas Movie:
println("a movie called '\(movie.name)', dir.\(movie.director)")
case let stringConverteras 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
第一次看到switch这么用,不明觉厉啊
注意:在switch中使用强制转型操作符(as,not as?)来检查和转换某个类型。这种做法在switch中是安全的。
- Part 2:(ARC,Optional Chaining,Type Casting)
- Optional-Optional Chaining
- swift optional chaining
- swift optional chaining
- Type Casting
- Type Casting
- Type Casting
- Type Casting
- Type Casting
- Swift可选链(Optional Chaining)
- C++ Type Casting
- swift type casting
- 17.Swift-可选链接(Optional Chaining)
- Swift Optional Chaining and nil Coalesce
- Casting 和Casting type的区别
- 使用可选值链(optional chaining)解析optional变量
- one question about type casting
- C++--类型转换(Type Casting)
- 语音识别之语音控制
- sublime 使用技巧2
- String、StringBuilder、StringBuffer类使用笔记
- 设计模式之访问者模式
- Cocos2d-x数据篇01:UserDefault数据存储
- Part 2:(ARC,Optional Chaining,Type Casting)
- iOS自定义alertView,继承自UIView
- 手机卫士-02
- 常用的内置视图的使用
- 拓扑排序
- static class 静态类(Java)
- kindeditor在线HTML编辑器
- pdf如何转换成word文档教程
- 通过系统签名使apk有系统权限