使用 'class' 关键字后跟类名来创建一个类。在一个类中,申明一个属性的写法,同申明一个常量或变量的写法是一样的。同样的,类中的方法(method)同函数(function)的申明写法是一样的。例:
通过在类名后添加括号来创建一个类的实例。使用 ‘点语法’ 来调用一个实例的属性和方法。例:
目前这个版本的 ‘Shape’ 类缺少了一些重要的东西 —— 一个构造器,当一个创建一个实例的时候,初始化一个类。使用 ‘init’来创建一个(构造器)
可以看到,在swift中,init方法不需要调用父类的init(虽然例子中的类并没有父类)。这一点和oc中是不同的。在后面的篇章中,会对这一问题有更多的讨论。
在这里要留意下,在构造器中‘self’被用来将(NameShape,即本类的)name属性和 name形参区分开。当你创建一个类的实例时,构造器中的参数被一一传递第就像是一个函数调用。每个属性需要分配一个值 —— 要么是在它(属性)申明的时候(像是numberOfSides)要么是在构造器中(像是name)。
使用 ‘deinit’ 创建一个析构器(类似于 dealloc)如果你需要在对象被释放前,做一些清理工作的话。
子类们在他们的类名之后,包含他们的父类名称,使用冒号区分开。对类而言,并不需要基于一个标准的根类(standard root class),所以,你可以按需要包含或者是忽略父类。
子类中的方法,如果重写了父类的实现,那就需要使用 ‘override’ 关键字来标示 —— 如果没有加 ‘override’ 关键字,而意外的覆盖了一个方法,这将会被编译器认为是一个错误。编译器同样也会侦测出那些使用了‘override’关键字——却实际上并没有覆盖任何父类中的方法——的方法。例:
从图中的例子中可以看到,在子类的构造方法中,首先初始化self的属性,然后调用super的构造方法。而且,在调用super的init 方法前,是不能调用name属性的。原因是name是从父类继承下来的属性,而在调用super的init的方法前,self并没有初始化,所以也就调用不到name。
在oc中,构造函数是首先调用super的init,这样先构造出self,而后可以对self进行进一步的初始化操作。swift中,首先将self的私有属性赋初始值,而后在构造self(通过调用super的init)。这个流程和oc是不一样的。
实际上,熟悉oc的开发者会知道,oc中的super实际上指的是self。也就是说,在oc中[super init]实际上使用self调用super的init方法。这个过程中,self的所有实际上都会被初始化。而在super的init方法中没有初始化的属性(self的私有属性)就会有一个默认的值。譬如说,bool 类型的话就是no,NSArray类型的话就是nil等等。
swift中,self在调用super的init方法前必须先将自己的私有属性全部赋初始值,不然会报错
swift中的super.init初始化的仅仅是super中的属性,和self的私有属性是没有关系的了。这么做其实更严谨。
除了简单的存储属性之外,属性还能有setter和getter方法。例:
oc的类中,每一个属性都会默认有一个setter和getter方法。而在swift中,拥有setter/getter方法的是计算属性,而用来存储值(结果)的存储属性。如例中,sideLength是存储属性,而perimeter是计算属性。此二者是不一样的。
如果,将例子中的perimeter的set方法改为,perimeter = newValue / 3.0。那么结果就会变成死循环。因为此时perimeter是左值,意味着在set方法中不断地在调用set方法,最终导致死循环。(然而在编译中是可以通过的)在后面的篇章中会看到,category中可以添加计算属性,但是不能添加存储属性(继承于NSObject的可以使用runtime,但实际上,这样添加的存储属性,也不是真正意义上的属性)。原因是category是在类的方法列表(method list)中添加方法——扩展方法列表。而类的属性存在于属性列表(property list)中,所以category是不能添加属性的。
类实例管理着属性列表(的地址),属性列表中存储的是类属性的地址,category为方法列表添加方法,也就是在方法列表中添加新方法的地址。
在perimeter的setter方法中,新的值有一个隐式的名称 —— newValue。你可以在set之后,添加括号,然后给出一个显式的名称,例如:set(newValue_1)。注意,EquilateralTriangle类的构造器,有三个不同的步骤:
1、为子类中申明的属性(sideLength)赋值
2、调用父类的构造器
3、改变在父类中定义的属性的值。任何额外的初始化步骤,譬如调用使用方法,getters或者是setters,也能在这个时间点中进行。
要修改从父类继承的属性,或者是调用方法(类方法除外),都要先调用super的init方法。否则,self是未初始化的,会导致错误。
如果你不需要对属性进行计算,但是你仍然给出一段代码,希望在属性将设置一个新的值之前,或者已经设置了一个新的值之后运行,那么你可以使用willSet和didSet。任何时候(构造器之外),只要属性的值发生了变化,你提供的代码都会运行。例如,下面的类中,确保类的三角形(属性)的边长总是和他的正方形(属性)的边长相等。例:
当遇到可选值的时候,例如方法,属性和下表,你可以在操作符前使用 "?"。如果"?"前的值是nil,那么所有在"?"之后的(代码,逻辑)都会被忽略,同时整个表达式是nil的;否则(即不为nil),可选值是被拆包的(unwrapped),并且所有"?"之后的行为都使用拆包后的值。在两种情况下整个表达式的值是一个可选值。例:
所谓可选值,指的是这个值可能为空或者不为空,而不是说这个值的类型是可变的。为空的话,"?"后面的部分不会执行。譬如,例中的optionalSquare?.sideLength。如果,optionalSquare是空的,那么sideLength属性不会被调用,而且作为左值的sideLength也是nil。