Object Types
函数参数可以有默认值
property必须在声明时或是在初始化方法中被初始化
Referring to self
在实例属性没有初始化完成前,初始化方法不能显式或隐匿的引用self(An initializer may not refer to self, explicitly or implicitly, until all instance properties have been initialized).
struct Cat {
var name: String
var license: Int
init(name:String, license:Int) {
self.name = name
meow() //compile error
self.license = license
}
func meow() {
print("meow")
}
}
Delegating initializer
An intializer that calls another initializer is called a delegating initializer.
A delegating initailzer cannot set an immutable property(a let
variable).因为只有在调用了其他初始化方法后才能引用属性,这个时候实例已经完全初始化了,已经无法再初始化let的属性了。
struct Digit {
var number: Int
var meaningOfLife: Bool
//let meaningOfLife :Bool
init(number:Int) {
self.number = number
self.meaningOfLife = false
}
init() {
self.init(number:43)
self.mearningOfLife = true //如果meaningOfLife声明为let这里就编译错误了
}
}
Failable initailizer
有可能返回nil的初始化方法。An initializer can return an Optional wrapping the new instance.
calss Dog {
let name: String
let license: Int
init!(name:String, license:Int) {
self.name = name
self.license = license
if name.isEmpty {
return nil
}
if license <= 0 {
return nil
}
}
}
上面的返回值是Dog!
, the Optional is implicitly unwrapped.
可以直接使用返回的结果,但是如果返回nil, 直接使用就会导致crash
UIImage
的初始化方法init?(named:)
也是failable initializer,它没有implicitly unwrapped,所以返回是UIImage?
,在使用前必须unwrapped.
Properties
- 有固定的类型
- 可以是var或let
- 可以是stored或computed
- 可以有setter observer
- 实例属性可以声明为lazy
存储型(stored)的实例属性必须有初始值(可以在声明时赋,也可以在初始化方法中赋值)。在属性初始化时不会调用Setter observer。
初始化属性的代码不能获取实例属性也不能调用实例方法,因为会显式或隐匿的引用到self
,但是在初始化时还没有self
。
class Moi {
let first = "mat"
let last = "Nur"
let whole = self.first + " " + self.last //compile error
let who : String { //改用计算型的
return self.first + " " + self.last
}
lazy var who2: String = self.first + " " + self.last
}
上面在初始化时访问了其他属性导致编译错误,可以改为用计算型的,因为计算型的不会真正计算直到self
存在。
另外,还可以通过声明whole为lazy来解决。同样只有在self存在的时候才执行。
同样,计算型的和lazy的可以调用实例方法。
属性的初始化方法可以有多行代码,你可以定义执行匿名函数。如果代码有访问其他的属性或是实例方法,必须声明为lazy
class Moi {
let first = "mat"
let last = "Nur"
lazy var whole2: String = {
var s = self.first
s.appendContentsOf(" ")
s.appendContentsOf(self.last)
return s
}()
}
静态属性初始化时可以引用其他静态属性,因为静态属性的初始化是lazy
的。
Methods
静态方法和类方法是通过type
来访问的,self
也就是type.
静态方法和类方法不能引用实例,因为根本就没有实例。因此静态方法和类方法不能直接引用任何实例属性和调用实例方法。反过来,实例方法可以访问静态属性和类属性,同样也可以调用静态方法和类方法。
关于实例方法的一个私密
实例方法实际上是静态/类方法
下面的代码是合法的
class MyClass {
var s = ""
func store(s:String) {
self.s = s
}
}
let m = MyClass()
let f = MyClass.store(m)
store
是一个实例方法,但是可以按类方法的方式来调用,参数是该类的一个实例。
原因是An instance method is actually a curried static/class method composed of two functions - one function that takes an instance, and another function that takes the parameters of the instance method.
因此,上面代码执行后f
是第二种函数,can be called as a way of passing a parameter to the store
method of the instance m
.
Enums
可以用rawValue:
的实例化方法来初始化有初始值的enum.let type = Filter(rawValue:"Album")
,因为这里给的raw value可能没有对应的case,因此这是个failable initializer,返回值是Optional的。
Enum Property
enum可以有实例属性和静态属性,但是enum的实例属性不能是存储型的(stored),因为如果相同case的两个实例如果存储了不同的实例属性值,它们就不再相同了。computed的属性是可以的。
Struct
自动会有一个无参的init()
,但是如果显式地添加了你自己的初始化方法,就不能再访问init()
了,但是可以添加init()
方法。
如果结构体有存储型属性但是没有显式的初始化方法,就会有一个根据实例属性衍生来的隐式初始化方法。
如果实例方法要设置属性值,该方法必须标识为mutating
,调用该方法的实例必须是var
类型的。
Struct As Namespace
many Objective-C enums are bridged to Swift as this kind of Struct
Classes
在Objective-C中,类是唯一的对象类型。Some built-in Swift struct are magically bridged to Objective-C class types, but your custom struct types dont’t have that magic. 这也是在与OC和Cocoa交互时,声明类而不是结构体的主要原因。
递归引用
值类型与引用类型的区别:值类型不能结构上递归,也就是值类型的实例属性不能是相同类型的实例,如下代码就会编译失败
In Swift 2.0 an enum case’s associated value can be an instance of that enum, provided the case( or the entire enum)is marked indirect
enum Node {
case None(Int)
indirect case Left(Int, Node)
indirect case Right(Int, Node)
indirect case Both(Int, Node, Node)
}
子类可以继承父类的属性,也可以添加自己的属性,也可覆盖继承来的属性。
- 通过
final
声明类可以阻止子类继承自该类。 - 同样也可以用
final
防止类成员被子类重写
Class Initializer
- Implicit initializer.如果一个类没有存储属性,亦或存储属性在声明时就初始化了,并且没有显式的初始化方法,就会有一个隐匿的初始化方法
init()
- Designated initializer.如果一个类有存储属性并且在声明时没有进行初始化,该类必须至少有一个designated initializer,当类初始化的时候所有的存储属性必须被初始化了。
- Convenience initializer.用
convenience
修饰。必须有self.init(...)
调用。在当前类中,必须调用一个designated initializer或是调用另一个convenience初始化方法。
class Dog {
var name : String
var license : Int
inti(name: String, license: Int) {
self.name = name
self.license = license
}
convenience init(license: Int) {
self.init(name:"Fido", license:license)
}
convenience init() {
self.init(license:1)
}
}
Subclass initializer
- No declared initializer. 初始化方法由父类继承来的组成
- Convenience initializer only.
- Designated initializer. 不再有继承的初始化方法了!显式的designated initializer 阻止了初始化方法的继承。现在子类有的唯一的初始化方法就是显式定义的。子类的designated initializer必须做下面的事:
- 必须保证子类所有的属性被初始化
- 必须调用
super.init(...)
,而且该方法必须是父类的designated initializer - 然后才能使用
self
来调用实例方法,或访问继承来的属性。
Override initializer
- 如果子类初始化方法的签名与父类的convenience初始化方法相同,必须也是convenience的,并且不要标识
override
- 如果子类初始化方法的签名与父类的designated初始化方法相同,可以是designated或是convenience初始化方法,并且必须标识为
override
Failable initializer
A failable initializer that returns an implicitly umwrapped Optional(init!
) is treated just like a normal initializer(init
) for purpose of overriding and delegation.对于返回init?
的failable initializer,有其他的限制:
init
可以重写init?
,反过来不行init?
可以调用init
init
可以通过init
来调用init?
,将返回结果解包
class A:NSObject {
init?(ok:Bool) {
super.init() // init? call init
}
}
class B:A {
override init(ok:Bool) { // init override init?
super.init(ok:ok)! // init call init? using "!"
}
}
Required intializer
如果初始化方法标识为required
,子类不能少了该初始化方法。
class Dog {
var name: String
required init(name:String) {
self.name = name
}
}
class NosiyDog:Dog {
var obedient = false
init(obedient:Bool) {
self.obedient = obedient
super.init(name:"Fido")
}
required init(name:String) {
super.init(name:name)
}
}
从上面代码中看到我们重写的required初始化方法没有标识为override
,但是标识为了required
,因此可以保证该要求可以一直往子类传。
Class Deinitializer
方法名是deinit
。如果一个类有父类,子类的deinitializer方法先调用,再调用父类的。
Class Properties and Methods
子类可以重写继承的属性,但是必须与要继承的属性有相同的名字和类型,并且必须标识为override
。
- 如果父类属性是可写的(存储属性或是有setter方法的计算属性),子类的重写可以为属性添加setter observer
- 子类可以重写为计算属性,但是:
- 如果父类是存储属性,子类重写为计算属性必须同时有getter和setter方法
- 如果父类属性是计算的,子类重写的计算属性必须重新实现所有父类的accessors。如果父类的属性是只读的(只有getter方法),子类重写可以添加setter方法。
重写的属性的方法可以用super
来访问被继承的属性。
static
和class
成员都会被子类继承,并且也是static
或是class
成员。
从程序员角度看static
方法与class
方法的主要不同是static方法不能重写。
静态属性与类属性之间的区别也差不多,非常明显的就是静态属性可以是stored,类属性只能是computed.
class Dog {
class var whatDogSay : String {
return "woof"
}
func bark () {
print(Dog.whatDogSay)
}
}
calss NosiyDog :Dog {
override static var whatDogSay : String {
return "WOOF"
}
}
上面代码中子类继承了whatDogSay
并且重写为类属性或是静态属性。但是即使重写为static类型的,也不能是存储属性。