Swift 学习笔记三

Swift属性

Swift属性将值跟特定的类,结构体,枚举关联。分为存储属性计算属性,通常用于特定类型的实例。属性也可以直接用于类型本身,称为类型属性

可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。

存储属性

存储特定类或结构体的实例里的一个常量或变量。

延迟存储属性

延迟存储属性指当第一次被调用的时候才会计算其初始值的属性。在属性声明前用lazy来标识,必须将延迟存储属性声明成变量,因为属性的值在实例构造完成之前可能无法得到。

一般用于:

  1. 延迟对象的创建。
  2. 当属性的值依赖与其他未知类。

计算属性

计算属性不直接存储值,而是提供一个getter来获取值,可选的setter来间接设置其他属性或变量的值。

如果计算属性的setter没有定义表示新值的参数名,则可以使用默认名称newValue

只读计算属性

只有getter没有getter的计算属性就是只读计算属性。

注意:必须使用var来定义计算属性,因为它们的值不是固定的。let关键值只用来声明常量属性,表示初始化后再也无法修改的值。

属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器。可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(存储属性和计算属性)添加属性观察器。

全局变量和局部变量

计算属性和属性观察器所描述的模式也可以用于全局变量和局部变量。

类型属性

类型属性是作为类型定义的一部分写在类型最外层{}内。使用关键字static来定义值类型的类型属性,关键字class来为类定义类型属性。

获取和设置类型属性的值

类型属性的访问通过.运算符来进行,但是类型属性是通过类型本身来获取和设置,而不是通过实例。

Swift方法

Swift方式是与某些特定类型关联的函数。

OC中类是唯一能够定义方法的类型。Swift中,不仅能够选择是否要定义一个类/结构体/枚举,还能在其中定义方法。

实例方法提供如下方法。

方法的局部参数名称和外部参数名称

Swift函数可以同时有一个局部名称和一个外部名称。Swift方法默认仅给方法的第一个参数名称一个局部参数名称,同时给之后的参数为全局参数名称。

我们也可以强制给第一个参数添加外部名称,把这个局部名称当中外部名称使用。相反,我们也可以使用_设置第二个及后续的参数不提供外部名称。

self属性

类型的每个实例都有一个隐含的属性叫做self,self完全等同于该实例本身。

在实例方法中修改值类型

Swift语言中结构体和枚举是值类型。一般情况下,值类型的属性不能在他的实例方法中被修改,但是如果我们想在实例方法中更改结构体或枚举里的属性值,我们该可以选择变异(mutating)这个方法,并且他做的任何改变在方法结束时还会保留在原始结构中。

方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。

类型方法

给类型本身调用的方法叫做类型方法。声明结构体和枚举的类型方法,在方法func前加关键字static。类可能会用关键字class来允许子类重写父类的实现方法。类型方法和实例方法一样用.语法调用。

下标脚本

下标脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。

举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 someArray[index] ,访问字典(Dictionary)实例中的元素可以这样写 someDictionary[key]。

对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。

语法

下标脚本允许你通过实例后边的[]传入一个或多个索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。

定义下标脚本使用subscript关键字,显式声明入参和返回类型。与实例方法不同的是下标脚本可以什么为读写或只读,这像计算型属性。

subscript(index: Int) -> Int {
    get {
        // 用于下标脚本值的声明
    }
    set(newValue) {
        // 执行赋值操作
    }
}

用法

通常下标脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。 你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。

例如,Swift 的字典(Dictionary)实现了通过下标脚本对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚来为字典设值。

下标脚本选项

Swift继承

重写

子类可以通过继承来的方法,属性或下标脚本来实现自己定制的功能,即重写,使用override关键字来实现重写。

子类中使用super前缀来访问超类的方法,属性或下标脚本。

重写属性时注意事项:

重写属性观察器

可以在属性重写中为一个继承来的属性添加属性观察器。但是不可以为继承来的常量存储型属性或继承来的只读计算属性添加属性观察器。

防止重写

通过final关键字防止重写。如果在关键字class前添加final特性,会将整个类标记为final,这样的类是不可被继承的。

Swift构造过程

构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。

Swift 构造函数使用 init() 方法。

与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行清理内存的工作。

存储型属性的初始赋值

类和结构体在实例创建时,必须为所有存储属性设置何时的初始值。存储属性在构造器中赋值时,它们的值是被直接设置的,不会触发属性观测器。

赋值流程:

语法:

init()
{
    // 实例化后执行的代码
}

使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型。

构造参数

可以在定义构造器时提供参数:init(fromLength length: Double, fromBreadth breadth: Double) {}

内部和外部参数名

跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。

然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。

如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。

可以通过_来禁用外部参数名。

可选属性类型

当存储属性声明可选时,将自动初始化为nil

构造器中修改常量属性

只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

默认构造器

默认构造器将简单的创建一个所有属性值都设置为默认值的实例:

值类型的构造器代理

构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

构造器代理规则

值类型:不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。 你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。

类类型:它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。

累的继承和构造过程

Swift提供两种类型构造器:指定构造器遍历构造器

指定构造器:

Init(parameters) {
    statements
}

便利构造器:

convenience init(parameters) {
      statements
}

构造器的继承和重载

Swift中的子类不会默认继承父类的构造器。

父类构造器仅在确定和安全的情况下被继承。

当重写父类指定构造器时,需要写override修饰符。

可失败构造器

如果一个类、结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败的构造器。

变量初始化失败的原因可能有:

你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)或叹号(init!)。

枚举类型的可失败构造器

你可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。

类的可失败构造器

值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。 但是,类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。

init?(studname: String) {
        self.studname = studname
        if studname.isEmpty { return nil }
    }

覆盖一个可失败构造器

你也可以用子类的可失败构造器覆盖基类的可失败构造器。

你也可以用子类的非可失败构造器覆盖一个基类的可失败构造器。

你可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。

一个非可失败的构造器永远也不能代理调用一个可失败构造器

swift析构过程

在类实例被释放之前,析构函数被立即调用。用关键字deinit来标识析构函数。析构函数只适合类类型

析构过程原理

Swift自动释放不在需要的实例以释放资源。

Swift通过ARC处理实例的内存管理。

通常实例被释放时不需要手动去清理。但是,当使用自己的资源时,可能需要一些额外的清理。

语法:

在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号。

deinit {
    // 执行析构过程
}

Swift可选链

可选链(optional chaining)是一种可以请求和滴哦阿勇属性,方法和脚本的过程,用于请求或调用的目标可能为nil。

可选链返回两个值:nil和目标值。

多次请求或调用可以被链接成一个链,如果任意一个节点为nil将导致整个链失效。

可选链可替代强制解析

通过在属性、方法、或下标脚本的可选值后面放一个问号(?),即可定义一个可选链。

可选链?

感叹号!

为可选链定义模型类

你可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用他们之间复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。

Swift自动引用计数(ARC)

ARC功能:

解决实例之间的循环强引用

Swift提供两种方法来解决使用类的属性时所遇到的循环强引用问题:

若引用和无主引用允许循环引用中的一个实例引用引用另外一个实例而不是保持强引用。这样实例能够互相引用而不产生循环强引用。

对于生命周期中会变为nil的实例使用弱引用。

相反的,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。

闭包引起的循环强引用

循环强引用会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包中又使用了实例。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod。这两种情况都导致了闭包 “捕获” self,从而产生了循环强引用。

弱引用和无主引用

当闭包和捕获的实例总是相互引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。相反,当捕获引用有时可能会是nil时,将闭包内的捕获定义为弱引用。如果捕获的引用绝对不会置为nil,应该用无主引用,而不是弱引用。

Swift类型转换

Swift语音类型转换可以判断实例的类型,也可以用于检测实例类型是否属于其父类或子类的实例。

Swift中类型转换使用isas操作符实现,is用于检测值的类型,as用于转换类型。

类型转换也可以用来检测一个类是否实现了某个协议。

向下转型

向下转型,用类型转换操作符(as?或as!

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

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

Any和AnyObject的类型转换

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

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

在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。

Swift扩展

扩展就是向一个已有的类,结构体或枚举类型添加新功能。可以添加新功能,但不能重写已有功能。

Swift扩展功能:

语法:

使用关键字:extension

extension SomeType {
    // 加到SomeType的新功能写到这里
}

一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议:

extension SomeType: SomeProtocol, AnotherProctocol {
    // 协议实现写到这里
}

可变实例方法

通过扩展添加的实例方法也可以修改该实例本身。

结构体和枚举类型中修改self或其属性的方法必须将该实例方法标注为mutating,正如来自原始实现的修改方法一样。

Swift协议

协议规定了用来实现某一特定功能所必须的方法和属性。

任何能够满足协议要求的类型被称为遵循这个协议。

类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。

语法:

protocol SomeProtocol {
    // 协议内容
}

要使类遵循某个协议,需要在类型名称后面加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}

如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}

对属性的规定

协议用于指定特定的实例属性或类属性,而不用指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。 协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }来表示。

对Mutating方法的规定

有时需要在方法中改变他的实例。 例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

对构造器的规定

协议可以要求它的遵循者实现指定的构造器。

你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体,语法如下:

protocol SomeProtocol {
   init(someParameter: Int)
}

协议构造器规定在类中的实现

你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上required修饰符。

protocol tcpprotocol {
   init(aprot: Int)
}

class tcpClass: tcpprotocol {
   required init(aprot: Int) {
   }
}

使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显示的实现或继承实现。如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标识requiredoverride修饰符。

协议类型

尽管协议本身并不实现任何功能,但是协议可以被当作类型来使用。

协议可以像其他普通类型一样使用,使用场景:

在扩展中添加协议成员

我们可以通过扩展来扩充已存在的类型(类,结构体,枚举等)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。

协议的继承

协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔。

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 协议定义
}

类专属协议

在协议的继承列表中,通过添加class关键字,限制协议只能适配到类(class)类型。该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 协议定义
}

协议合成

Swift支持合成多个协议,这在我们需要同时遵循多个协议时非常有用。

语法:

protocol<SomeProtocol, AnotherProtocol>

eg:

func show(celebrator: protocol<Stname, Stage>) {
    print("\(celebrator.name) is \(celebrator.age) years old")
}

检验协议的一致性

可以使用isas操作符来检查是否遵循某一个协议或强制转化为某一类型。

Swift泛型

Swift提供了泛型让你写出灵活且可用的函数和类型。

Swift标准库是通过泛型代码构建出来的。

Swift的数组和字典类型都是泛型集。

你可以创建一个Int数组,也可以创建一个String数组,或者甚至可以是任何其他Swift的类型数据数组。

func exchange<T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}

这个函数的泛型版本使用了占位类型名字(通常此情况下用字母T来表示)来代替实际类型名(如Int、String或Double)。占位类型名没有提示T必须是什么类型,但是它提示了a和b必须是同一类型T,而不管T表示什么类型。只有 exchange(::)函数在每次调用时所传入的实际类型才能决定T所代表的类型。

泛型类型

Swift允许你定义自己的泛型类型。

扩展泛型类型

当使用extension扩展一个泛型类型的时候,并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中申明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。

类型约束

类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。

类型约束语法

你可以写一个在一个类型参数名后面的类型约束,用冒号分隔,来作为类型参数链的一部分。

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是函数主体
}

关联类型实例

Swift中使用typealias关键字来设置关联类型。

定义一个协议时,有时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。

where语句

类型约束能够确保类型符合泛型函数或类的定义约束。

在参数列表中通过where语句定义参数的约束。

写一个where语句,紧跟在类型参数列表后面,where语句后面跟一个或多个针对关联类型的约束,以及一个或多个类型和关联类型间的等价关系。

Swift访问控制

访问控制可以限定其他源文件或模块中代码对你代码的访问级别。

你可以明确的给单个类型(类,结构体,枚举)设置访问级别,也可以给这些类型的属性,函数,初始化方法,基本类型,下标索引等设置访问级别。

协议也可以被限定在一定的范围内使用,包括协议里的全局常量,变量和函数。

访问控制基于模块和源文件。

模块值得是以独立单元构建和发布的Framework或Application。在Swift中的一个模块可以使用import关键字引入另一个模块。

源文件是单个源码文件,他通常属于一个模块,源文件可以包含多个类和函数的定义。

Swift为代码中的实体提供了三种不同的访问级别:public,internal,private.

除非有特殊的说明,否则实体都使用默认的访问级别internal。

函数类型访问权限

函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。如果不是默认权限,必须明确声明该函数。

枚举类型访问权限

枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员单独声明不同的访问级别。

子类访问权限

子类的访问级别不得高于父类的访问级别。比如,父类的访问级别是internal,子类的级别不能声明为public。

常量、变量、属性、下标访问权限

常量、变量、属性不能拥有比它们的类型更高的访问级别。

比如说,你定义一个public级别的属性,但是它的类型是private级别的,这是编译器所不允许的。

同样,下标也不能拥有比索引类型或返回类型更高的访问级别。

如果常量、变量、属性、下标索引的定义类型是private级别的,那么它们必须要明确的申明访问级别为private:private var privateInstance = SomePrivateClass()

构造器和默认构造器访问权限

初始化

我们可以给自定义的初始化方法申明访问级别,但是不要高于它所属类的访问级别。但是必要构造器例外,它的访问级别必须和所属类的访问级别相同。初始化方法的参数的访问级别也不能低于初始化方法的访问级别。

默认初始化方法

Swift为结构体、类都提供了一个默认的无参初始化方法,用于给它们的所有属性提供赋值操作,但不会给出具体值。

默认初始化方法的访问级别与所属类型的访问级别相同。

协议访问权限

如果想为一个协议明确的申明访问级别,那么需要注意一点,就是你要确保该协议只在你申明的访问级别作用域中使用。

如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如,public访问级别的其他类型,他们成员的访问级别为internal。

扩展访问权限

你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的internal访问级别。

或者,你可以明确申明扩展的访问级别(比如使用private extension)给该扩展内所有成员申明一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所申明的访问级别所覆盖。

泛型访问权限

泛型类型或泛型函数的访问级别取决于泛型类型、函数本身、泛型类型参数三者中的最低访问级别。

类型别名

任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。

比如说,一个private级别的类型别名可以设定给一个public、internal、private的类型,但是一个public级别的类型别名只能设定给一个public级别的类型,不能设定给internal或private 级别的类型。

注意:这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。


参考:

[Swift 教程 菜鸟教程](http://www.runoob.com/swift/swift-tutorial.html)