从0开始学Swift笔记整理(三)

这是跟在上一篇博文后续内容:

——Swift中相关的属性

存储属性

Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据。存储属性可以存储数据,分为常量属性(用关键字let定义)和变量属性(用关键var定义)。

存储属性概念:

我们在前面曾用到过属性,Employee类和Department结构体。它们的类图如下,Employee 的部门属性dept与Department之间进行了关联。

我们可以在定义存储属性时指定默认值,示例代码如下:

class Employee {

let no: Int = 0

var name: String = ""

var job: String?

var salary: Double = 0

var dept: Department?

}

struct Department {

let no: Int = 0

var name: String = ""

}

let emp = Employee()

emp.no = 100 //编译错误:修改常量属性,程序会发生编译错误

let dept = Department()

dept.name = "SALES" //编译错误:dept是值类型,值类型不能修改,即便它的属性name是变量属性,也不能修改

let emp1 = Employee()

emp1.name =  "Tony"

计算属性

计算属性本身不存储数据,而是从其他存储属性中计算得到数据。

计算属性提供了一个getter(取值访问器)来获取值,以及一个可选的setter(设置访问器)来间接设置其他属性或变量的值。计算属性的语法格式如下:

面向对象类型 类型名 {

存储属性

......

var 计算属性名: 属性数据类型 {

get {

return 计算后属性值

}

set (新属性值) {

......

}

}

}

定义计算属性比较麻烦,要注意后面的几个大括号的对齐关系。

我们先看一个示例:

import Foundation

class Employee {

var no: Int = 0

var firstName: String = "Tony" //存储属性

var lastName: String = "Guan" //存储属性

var job: String?

var salary: Double = 0

lazy var dept: Department = Department()

var fullName: String { //计算属性

get {

return firstName + "." + lastName //返回拼接的结果

}

set (newFullName) { //存储传递进来的参数值

var name =
newFullName.componentsSeparatedByString(".")

firstName = name[0]

lastName = name[1]

}

}

}

struct Department {

let no: Int = 0

var name: String = ""

}

var emp = Employee()

print(emp.fullName) //取出属性值

emp.fullName = "Tom.Guan" //给属性赋值

print(emp.fullName)

只读计算属性:

计算属性可以只有getter访问器,没有setter访问器,这就是只读计算属性。指定计算属性不仅不用写setter访问器,而且get{}代码也可以省略。与上一节相比,代码将大大减少。修改上一节示例为只读计算属性,代码如下:

class Employee {

var no: Int = 0

var firstName: String = "Tony"

var lastName: String = "Guan"

var job: String?

var salary: Double = 0

lazy var dept: Department = Department()

var fullName: String { //简洁的setter访问器

return firstName + "." + lastName

}

}

struct Department {

let no: Int = 0

var name: String = ""

}

var emp = Employee()

print(emp.fullName)

只读计算属性不能够赋值,下列语句是错误的。

emp.fullName = "Tom.Guan"

属性观察者

为了监听属性的变化,Swift提供了属性观察者。属性观察者能够监听存储属性的变化,即便变化前后的值相同,它们也能监听到。

属性观察者主要有以下两个:

· willSet:观察者在修改之前调用。

· didSet:观察者在修改之后立刻调用。

属性观察者的语法格式如下:

面向对象类型 类型名 {

...

var 存储属性: 属性数据类型 = 初始化值 {

willSet(新值) {   //定义willSet观察者。“新值”是传递给willSet观察者的参数,它保存了将要替换原来属性的新值

...

}

didSet(旧值) {  //定义didSet观察者。“旧值”是传递给didSet观察者的参数,它保存了被新属性替换的旧值。

...

}

}

}

属性观察者的语法格式比计算属性要混乱。

属性观察者可以在类和结构体中使用,不能在枚举中使用。

示例代码如下:

class Employee {

var no: Int = 0

var name: String = "Tony" {

willSet(newNameValue)
{ //定义name属性的willSet观察者,newNameValue是由我们分配的传递新值的参数名

print("员工name新值:\(newNameValue)")

}

didSet(oldNameValue)
{ //定义name属性的didSet观察者,oldNameValue是由我们分配的传递旧值的参数名

print("员工name旧值:\(oldNameValue)")

}

}

var job: String?

var salary: Double = 0

var dept: Department?

}

struct Department {

var no: Int = 10 {

willSet
{    //定义no属性的willSet观察者,注意这里没有声明参数,但是我们可以在观察者内部使用newValue

print("部门编号新值:\(newValue)")

}

didSet
{    //定义no属性的didSet观察者,注意这里也没有声明参数,但是我们可以在观察者内部使用oldValue

print("部门编号旧值:\(oldValue)")

}

}

var name: String = "RESEARCH"

}

var emp = Employee()

emp.no = 100

emp.name = "Smith"

var dept = Department()

dept.no = 30

上述代码运行结果如下:

员工name新值:Smith

员工name旧值:Tony

部门编号新值:30

部门编号旧值:10

静态属性

我先来设计一个类:有一个Account(银行账户)类,假设它有3个属性:amount(账户金额)、interestRate(利率)和owner(账户名)。

在这3个属性中,amount和owner会因人而异,不同的账户这些内容是不同的,而所有账户的interestRate都是相同的。

amount和owner属性与账户个体有关,称为实例属性。interestRate属性与个体无关,或者说是所有账户个体共享的,这种属性称为静态属性或类型属性。

面向对象类型(结构体、枚举和类)都可以定义静态属性,它们的语法格式分别如下所示:

struct 结构体名 {   //定义结构体, 结构体中可以定义静态存储属性和静态计算属性

static var(或let)
存储属性  = "xxx"

...

static var 计算属性名: 属性数据类型 {

get {

return 计算后属性值

}

set (新属性值) {

...

}

}

}

enum 枚举名 {  //定义枚举,枚举中不可以定义实例存储属性,但可以定义静态存储属性,也可以定义静态计算属性

static var(或let)
存储属性  = "xxx"

...

static var 计算属性名: 属性数据类型 {

get {

return 计算后属性值

}

set (新属性值) {

...

}

}

}

class 类名 {    //定义类,类中不仅可以定义实例存储属性,还可以定义静态存储属性

static var(或let)
存储属性  = "xxx"

...

class(或static) 
var 计算属性名: 属性数据类型 {

get {

return 计算后属性值

}

set (新属性值) {

...

}

}

}

结构体静态计算属性也可以是只读的,语法如下:

static var 计算属性名: 属性数据类型
{

return 计算后属性值

}

看一个Account结构体静态属性示例:

struct Account {//定义Account结构体

var amount: Double = 0.0 //账户金额

var owner: String = "" //账户名

static var interestRate: Double = 0.0668  //定义静态存储属性interestRate利率

static var staticProp: Double { //定义静态计算属性staticProp

return interestRate * 1_000_000

}

var instanceProp: Double { //定义实例计算属性instanceProp

return Account.interestRate * amount

}

}

//访问静态属性

print(Account.staticProp)

var myAccount = Account()

//访问实例属性

myAccount.amount = 1_000_000

//访问静态属性

print(myAccount.instanceProp)

——Swift下标

var studentList: String[]  = ["张三","李四","王五"]

studentList[0] = "诸葛亮"

var studentDictionary = [102: "张三",105: "李四", 109: "王五"]

studentDictionary[110] = "赵六"

在访问数组和字典的时候,可以采用下标访问。其中数组的下标是整数类型索引,字典的下标是它的“键”。

Swift中的下标相当于Java中的索引属性和C#中的索引器。

下标访问的语法格式如下:

面向对象类型 类型名 {

其他属性

...

subscript(参数: 参数数据类型) -> 返回值数据类型 {

get {

return 返回值

}

set(新属性值) {

...

}

}

}

下标也有类似于计算属性的getter和setter访问器。

getter访问器是一个方法,在最后使用return语句将计算结果返回。

setter访问器“新属性值”是要赋值给属性值。参数的声明可以省略,系统会分配一个默认的参数newValue。

在Swift中没有提供二维数组,只有一维数组Array。可以自定义一个二维数组类型,然后通过两个下标参数访问它的元素,形式上类似于C语言的二维数组。

——默认构造函数

结构体和类的实例在构造过程中会调用一种特殊的init方法,称为构造函数。构造函数没有返回值,可以重载。在多个构造函数重载的情况下,运行环境可以根据它的外部参数名或参数列表调用合适的构造函数。

结构体和类在构造过程中会调用一个构造函数,即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。下面看示例代码:

class Rectangle {

var width: Double  = 0.0

var height: Double = 0.0

}

var rect = Rectangle() //创建实例,并调用默认构造函数init()

rect.width = 320.0

rect.height = 480.0

print("长方形:\(rect.width) x \(rect.height)")

Rectangle()表示调用了某个方法,这个方法就是默认构造函数init()。

事实上,在Rectangle的定义过程中省略了构造函数,相当于如下代码:

class Rectangle {

var width: Double  = 0.0

var height: Double = 0.0

init() {

}

}

如果Rectangle是结构体,则它的定义如下:

struct Rectangle {

var width: Double = 0.0

var height: Double = 0.0

}

而结构体Rectangle的默认构造函数与类Rectangle的默认构造函数是不同的,相当于如下代码:

struct Rectangle {

var width: Double = 0.0

var height: Double = 0.0

init() {

}

init(width: Double, height: Double) { //有参数的构造函数

self.width  = width

self.height  = height

}

}

要调用哪个构造函数是根据传递的参数名和参数类型决定的。

——构造函数与存储属性初始化

构造函数的主要作用是初始化实例,其中包括:初始化存储属性和其它的初始化。在Rectangle类或结构体中,如果在构造函数中初始化存储属性width和height后,那么在定义他们时就不需要初始化了。

Rectangle类代码如下:

class Rectangle {

var width: Double

var height: Double

init() {

width  = 0.0

height  = 0.0

}

}

如果存储属性在构造函数中没有初始化,在定义的时候也没有初始化,那么就会发生编译错误。

构造函数还可以初始化常量存储属性

构造函数中的局部参数名可以直接作为外部参数名使用

为了增强程序的可读性,Swift中的方法和函数可以使用外部参数名。在构造函数中也可以使用外部参数名。构造函数中的外部参数名要比一般的方法和函数更有意义,由于构造函数命名都是init,如果一个类型中有多个构造函数,我们可以通过不同的外部参数名区分调用不同的构造函数。

——构造函数重载

构造函数作为一种特殊方法,也可以重载。

Swift中构造函数可以多个,他们参数列表和返回值可以不同,这些构造函数构成重载。

为了减少多个构造函数间的代码重复,在定义构造函数时,可以通过调用其他构造函数来完成实例的部分构造过程,这个过程称为构造函数代理。构造函数代理在结构体和类中使用方式是不同,先介绍结构体中构造函数代理。

将上一节的示例修改如下:

struct Rectangle {

var width: Double

var height: Double

init(width: Double, height: Double) {

self.width   = width

self.height  = height

}

init(W width: Double,H height: Double) {

self.width   = width

self.height  = height

}

init(length: Double) {     //调用了self.init语句

self.init(W: length, H: length)

}

init()
{         //调用了self.init语句

self.init(width: 640.0, height:
940.0)

}

}

var rectc1 = Rectangle(width: 320.0, height: 480.0)

print("长方形:\(rectc1.width) x
\(rectc1.height)")

var rectc2 = Rectangle(W: 320.0, H: 480.0)

print("长方形:\(rectc2.width) x
\(rectc2.height)")

var rectc3 = Rectangle(length: 500.0)

print("长方形3:\(rectc3.width) x
\(rectc3.height)")

var rectc4 = Rectangle()

print("长方形4:\(rectc4.width) x
\(rectc4.height)")

将Rectangle声明为结构体类型,其中也有4个构造函数重载。

这种在同一个类型中通过self.init语句进行调用当前类型其它构造函数,其它构造函数被称为构造函数代理。

类构造函数横向代理

由于类有继承关系,类构造函数代理比较复杂,分为横向代理和向上代理。

· 横向代理类似于结构体类型构造函数代理,发生在同一类内部,这种构造函数称为便利构造函数(convenience initializers)。

· 向上代理发生在继承情况下,在子类构造过程中要先调用父类构造函数,初始化父类的存储属性,这种构造函数称为指定构造函数(designated initializers)。

构造函数调用规则

在构造函数中可以使用构造函数代理帮助完成部分构造工作。类构造函数代理分为横向代理和向上代理,横向代理只能在发生在同一类内部,这种构造函数称为便利构造函数。向上代理发生在继承的情况下,在子类构造过程中,要先调用父类构造函数初始化父类的存储属性,这种构造函数称为指定构造函数。

Person和Student类示例:

class Person {

var name: String

var age: Int

func description() -> String {

return "\(name) 年龄是: \(age)"

}

convenience init () {   //便利构造函数

self.init(name: "Tony")

self.age = 18

}

convenience init (name: String) { //便利构造函数

self.init(name: name, age: 18)

}

init (name: String, age: Int) {  //指定构造函数

self.name = name

self.age  = age

}

}

class Student: Person {

var school: String

init (name: String, age: Int, school: String)
{  //指定构造函数

self.school = school

super.init(name: name, age: age)

}

convenience override init (name: String, age: Int) {//便利构造函数

self.init(name: name, age: age,
school: "清华大学")

}

}

let student = Student()

print("学生: \(student.description())")

构造函数之间的调用形成了构造函数链,如图所示。

Swift限制构造函数之间的代理调用的规则有3条,如下所示。

1、指定构造函数必须调用其直接父类的的指定构造函数。从图可见,Student中的④号指定构造函数调用Person中的③号指定构造函数。

2、便利构造函数必须调用同一类中定义的其他构造函数。从图可见,Student中的⑤号便利构造函数调用同一类中的④号便利构造函数,Person中的①号便利构造函数调用同一类中的②号便利构造函数。

3、便利构造函数必须最终以调用一个指定构造函数结束。从图可见,Student中的⑤号便利构造函数调用同一类中的④号指定构造函数,Person中的②号便利构造函数调用同一类中的③号指定构造函数。

——类的继承

Swift中的继承只能发生在类上,不能发生在枚举和结构体上。一个类可以继承另一个类的方法、属性、下标等特征,当一个类继承其他类时,继承类叫子类,被继承类叫父类(或超类)。子类继承父类后,可以重写父类的方法、属性、下标等特征。

为了了解继承性,看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是他定义了类Person,如下所示:

class Person {

var name: String

var age: Int

func description() -> String {

return "\(name) 年龄是: \(age)"

}

init () {

name = ""

age  = 1

}

}

一周以后,小赵又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个新的类Student,如下所示:

class Student {

var name: String

var age: Int

var school: String

func description() -> String {

return "\(name) 年龄是: \(age)"

}

init() {

school = ""

name = ""

age  = 8

}

}

很多人会认为小赵的做法能够理解并相信这是可行的,但是问题在于Student和Person两个类的结构太接近了,后者只比前者多了一个属性school,却要重复定义其他所有的内容,实在让人“不甘心”。Swift提供了解决类似问题的机制,那就是类的继承,代码如下所示:

class Student: Person {

var school: String

override init() {

school = ""

super.init()

age  = 8

}

}

Student类继承了Person类中的所有特征,“:”之后的Person类是父类。Swift中的类可以没有父类,例如Person类,定义的时候后面没有“:”,这种没有父类的就是基类。

此外override init()是子类重写父类构造函数。

一般情况下,一个子类只能继承一个父类,这称为单继承,但有的情况下一个子类可以有多个不同的父类,这称为多重继承。在Swift中,类的继承只能是单继承。多重继承可以通过遵从多个协议实现。也就是说,在Swift中,一个类只能继承一个父类,但是可以遵循多个协议。

——构造函数继承

Swift中的子类构造函数的来源有两种:自己编写和从父类继承。并不是父类的所有的构造函数都能继承下来,能够从父类继承下来的构造函数是有条件的,如下所示。

· 条件1:如果子类没有定义任何指定构造函数,它将自动继承所有父类的指定构造函数。

· 条件2:如果子类提供了所有父类指定构造函数的实现,无论是通过条件1继承过来的,还是通过自己编写实现的,它都将自动继承所有父类的便利构造函数。(示例忽略)

——重写属性

重写实例属性。我们可以在子类中重写从父类继承来的属性,属性有实例属性和静态属性之分,他们在具体实现也是不同的。

实例属性的重写一方面可以重写getter和setter访问器,另一方面可以重写属性观察者。

计算静态属性需要使用getter和setter访问器,而存储属性不需要。子类在继承父类后,也可以通过getter和setter访问器重写父类的存储属性和计算属性。

从属性重写可见,子类本身并不存储数据,数据是存储在父类的存储属性中的。

重写静态属性。

在类中静态属性定义使用class或static关键字,但是使用哪一个要看子类中是否重写该属性。

class修饰的属性可以被重写,static关键字就不能被重写。(示例忽略)

这是我在学Swift整理的基础笔记,希望给更多刚学IOS开发者带来帮助,在这里博主非常感谢大家的支持!

更多的请到参考我下一篇博文。之后还在持续更新中。。。

时间: 2024-12-14 06:22:09

从0开始学Swift笔记整理(三)的相关文章

从0开始学Swift笔记整理(五)

这是跟在上一篇博文后续内容: --Core Foundation框架 Core Foundation框架是苹果公司提供一套概念来源于Foundation框架,编程接口面向C语言风格的API.虽然在Swift中调用这种C语言风格的API比较麻烦,但是在OS X和iOS开发过程中,有时候使用Core Foundation框架的API是非常方便的,例如在与C语言混合编码的时候. Core Foundation框架与Foundation框架紧密相关,他们具有与相同的接口,但是不同.Core Founda

从0开始学Swift笔记整理(四)

这是跟在上一篇博文后续内容: --重写方法 重写实例方法 在子类中重写从父类继承来的实例方法和静态方法.先介绍实例方法的重写. 下面看一个示例: class Person { var name: String var age: Int func description() -> String { //实例方法 return "\(name) 年龄是: \(age)" } class func printClass() ->() { //静态方法 print( "P

从0开始学Swift笔记整理(一)

Swift 是一种适用于 iOS 和 OS X 应用的全新编程语言,它建立在最好的 C 和 Objective-C 语言之上,并且没有 C 语言的兼容性限制.Swift 采用安全的编程模式,增加了现代功能使 编程更容易.更灵活.更有趣.Swift 以成熟且备受宠爱的 Cocoa 和 Cocoa Touch 框架为 支撑,这是一个重新构想软件开发的机会. --我的第一行Swift代码 import Foundation             //表示引入Foundation框架 var str

《从0到1》笔记 第三章 所有成功的企业都是不同的

第三章 所有成功的企业都是不同的----科技企业的创新就是为了垄断,成功的科技企业都是垄断式的. 在商界,钱就是一切,或至少是非常重要.垄断者除了想着赚钱外还有余力想其它事情,而非垄断者就不行.在完全竞争中,企业着眼于短期利益,不可能对未来进行长期规划.要想企业从每日的生成竞赛中解脱出来,唯一的方法就是:获取垄断利润. 企业成功的原因各有不同:每个垄断企业都是靠解决独一无二的问题而获得垄断地位:而企业失败的原因却相同:它们都无法逃脱竞争. 国内的垄断者,如百度,解决了中文搜索的问题,垄断了搜索的

Python学习笔记整理(三)Python中的动态类型简介

Python中只有一个赋值模型 一.缺少类型声明语句的情况 在Python中,类型是在运行过程中自动决定的,而不是通过代码声明.这意味着没有必要事声明变量.只要记住,这个概念实质上对变量,对象和它们之间的关系都适用.那么这个概念也容易理解并掌握. 1.变量,对象和引用 变量创建:一个变量,当代码第一次给它赋值时它就被创建了.之后的赋值将会改变已创建的变量名的值.Python在代码运行之前先检测变量名,可以当成是最初的赋值创建变量. 变量类型:变量永远不会有任何的它关联的类型信息或约束.类型的概念

Flex 笔记整理 三

1. Panel, TitleWindow PopUpManager 透明 用一个类,这个类里引用一个组件, P如 Panel, TitleWindow等, 利用PopUpManager来弹出显示.    可能显示的都为透明窗体.    这时要引用从引用主主程序传进来的this (Sprite) 对象. 2. Flash Builder 保存修改构建空间 很久的问题 在FB 工具窗口的 项目菜单下,取消掉 自动构建 ,     要启动时按F11 即可,    如果运行调试时,老是只在 57% 时

perl自学笔记整理(三)

关于perl中的子程序 1.定义子程序sub sub_programe_name {    程序语句体: } 2.调用子程序 &sub_programe_name; 3.子程序的返回值在子程序的执行过程中,它会不断进行运算.而最后一次运算的结果(不管是什么),都会被自动当成子程序的返回值.[[email protected] fuxi]# cat a4.pl #!/usr/bin/perl#subroutinesub sum_of_fred_and_barney {print "Hey

菜鸟学Android笔记(三十二):Request获取客户端的信息

一.request的概念及结构 1.概念 request对象是从客户端向服务器发出请求,包括用户提交的信息以及客户端的一些信息.客户端可通过HTML表单或在网页地址后面提供参数的方法提交数据,然后通过request对象的相关方法来获取这些数据.request的各种方法主要用来处理客户端浏览器提交的请求中的各项参数和选项 2.结构 request ServletRequest -- 通用request,提供一个request应该具有的最基本的方法 | |--HttpServletRequest -

Deep Learning(深度学习)学习笔记整理系列之(三)

Deep Learning(深度学习)学习笔记整理系列 [email protected] http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04-08 声明: 1)该Deep Learning的学习系列是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的版本声明也参考原文献. 2)本文仅供学术交流,非商用.所以每一部分具体的参考资料并没有详细对应.如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主