Swift入门

该文章只提及Swift中语法的一些细节内容。

编码方式

Swift的字母采用的是Unicode编码,Unicode叫作统一编码制,包含亚洲文字编码,如中文、日文、韩文甚至聊天工具的表情符号,原则上Swift命名很多样,但是仍需要遵守一定的命名规范。
Unicode, 与ASCII一样,是一种跨语言、跨平台的文本编码标准。

var 与 let

如果数据类型是引用数据类型时候,最好声明为let,let声明的引用数据类型不会被改变引用(指针),但可以改变其内容。
在Swift中数据类型分为:值类型引用类型
值类型:

  • 整形
  • 浮点型
  • 布尔型
  • 字符
  • 字符串
  • 元组
  • 集合
  • 枚举
  • 结构体

而类属于引用类型。引用的本质上是指向对象的指针,它是一个地址。

class Person {
  var name: String
  var age: Int
  init(name: String, age: Int) {
      self.name = name
      self.age = age
  }
}

let p1 = Person(name: "Tony", age: 18)
p1.age = 20

p1 = Person(name: "Tom", age: 18)//编译错误

p1是类Person实例化的引用,因为申明为Int类型不能改变p1的引用,但可以改变实例内容
总结
let和var申明关键字时,原则上优先使用let,可以防止程序运行过程中不必要的修改并提高程序的可读性,引用数据类型声明经常采用let声明,虽然从业务层面来讲并不需要一个常量,但可以防止引用数据类型在程序运行期间被错误地修改。

表达式

1 不指定数据类型

var a1 = 10// 自动调用对应构造函数的隐式声明机制  显示的声明机制是var a1 = Int.init(10)  
let a2 = 20
var a = a1 > a2 ? "a1" : "a2"

我们直接为常量或者变量赋值,而并没有指定数据类型,因为Swift中可以自动推断数据类型。

2 指定数据类型

var a1: Int = 10
let a2: Int = 20
var a = a1 > a2 ? "a1" : "a2"

在声明时在变量或者常量后添加指定数据类型。

3 使用分号

var a1: Int = 10; let a2: Int = 20
var a = a1 > a2 ? "a1" : "a2"

在Swift中,一条语句结束可以不加分号,也可以加分号,但是有一种情况必须添加,多行表达式在同一行,使用分号来区分表达式。

可选类型

Swift语言与Objective-C和Java等语言有很大的不同,Swift所有的数据类型声明的变量或者常量都是不能为空值(nil)的。

可选类型的概念

var n1: Int = 10
n1 = nil//编译错误
let str: String = nil//编译错误

Int类型和String类型都不能接受nil,但程序运行期间有时被赋值为nil是难免但,为此,Swift为每一种数据类型提供一种可选类型,即在某个数据类型后加上问号?或感叹号!。

var n1: Int? = 10
n1 = nil
let str: String! = nil

Int?和String!都是Int和String的可选类型,可以接受nil。

可选类型值拆包

var n1: Int? = 10
print(n1)

输出的结果是Optional(10),而非10。说明n1不是普通类型,也不能与不同值进行计算,试图与值计算会报错。

var n1; Int? = 10
print(n1 + 100)//编译错误

因此对可选类型进行拆包是很重要的。分为显示拆包隐式拆包。使用?声明的可选类型在拆包时需要使用!,这种拆包方式称为显示拆包,使用!声明的可选类型在拆包时可以不使用!,这种拆包方式称为隐式拆包

可选绑定

在不能保证可选类型值是否为非空之前最好不要拆包,否则可以会出现运行时错误。

func divide(n1: Int, n2: Int) ->Double? {
    if n2 == 0 {
        return nil
    }
    return Double(n1) / Double(n2)
}

divide函数实现两个数的除法,函数返回值要么有值,要么是空值,为了能接受空值,我们需要声明返回类型为可选类型。

如果divide函数是别人封装好的,我们可以判断可选类型是否为空值,

if let result = divide(n1: 100, n2: 0) {
	print(result)
	print("Sucess.")
} else {
	//print(result) 编译错误
	print("failure.")
}

这种可选类型在if或者while语句中赋值并进行判断的写法成为可选绑定,可选绑定做了两件事情:

  1. 判断表达式是否为空值nil
  2. 如果非空则将可选类型拆包赋给一个常量

常量的作用域是if或者while为true的分支中,所以在第一个大括号内result常量作用域是有效的,而在第二个区域内是无效的。

语句

switch语句

Swift彻底颠覆了自C语言以来大家对于Swift的认知,这个颠覆主要表现在两个方面:

  1. C、C++、Objective-C、Java甚至C#语言中,switch只能比较离散的单个的整数(或者自动转化为整数)的常数或者变量,而Swift中的switch语句可以使用整数、浮点数、字符、字符串和元组等类型,而且它的数值可以是离散的也可是连续的范围。
  2. Swift中的switch语句case分支不需要显示提供break语句,分支执行结束后会自动跳出,但每个switch语句中必须有一条default语句。在使用枚举类型时可以没有default分支,在其他类型是不允许的。

guard语句

与if的语句非常类似,可以判断一个条件为true情况下执行某语句,否则终止或跳过执行某语句。它的设计目的是替换复杂if-else嵌套,提高程序的可读性。guard语句必须带有else语句,它的语法如下:

guard 条件表达式 else {
    跳转语句
}
语句组

当条件表达式为true时跳过else语句中的内容,执行语句组内容,条件表达式为false时执行else语句中的内容。跳转语句一般是return、break、continue和throw等关键字用于guard语句中。

struct Blog {
    let name: String?
    let URL: String?
    let Author: String?
}

func ifLongStyleBlog(blog: Blog) {
    if let blogName = blog.name {
        print("这篇博客名: \(blogName)")
        
        if let blogAuthor = blog.Author {
            print("这篇博客由\(blogAuthor)写的")
            
            if let blogURL = blog.URL {
                print("这篇博客网址: \(blogURL)")
            } else {
                print("这篇博客没有网址!")
            }
        } else {
            print("这篇博客没有作者!")
        }
    } else {
        print("这篇博客没有名字!")
    }
}

func guardLongStyleBlog(blog: Blog) {
    guard let blogName = blog.name else {
        print("这篇博客没有名字!")
        return
    }
    
    print("这篇博客名: \(blogName)")
    
    guard let blogAuthor = blog.Author else {
        print("这篇博客没有作者!")
        return
    }
    
    print("这篇博客由\(blogAuthor)写的")
    
    guard let blogURL = blog.URL else {
        print("这篇博客没有网址!")
        return
    }
    
    print("这篇博客网址: \(blogURL)")
}

在多个if-else语句嵌套的情况下guard语句更有优势,可以提高程序可读性。

值绑定

有时候在一些控制语句中可以将表达式的值临时赋给一个变量或者常量,这些变量或常量能够在控制语句中使用,这称为"值绑定"。

函数

调用函数的时候不能缺少参数的标签

使用外部参数名

为了提高程序的可读性,我们可以为函数中的参数提供一个外部参数名。

func rectangleArea(width: Double, height: Double) -> Double {
    let area = width * height
    return area
}
print(rectangleArea(width: 300, height: 200))

其中,参数width和height是函数名的一部分,但是它们只能在函数的内部使用,称为局部参数名。我们还可以提供一个外部参数名,

func rectangleArea(W width: Double, H height: Double) -> Double {
    let area = width * height
    return area
}
print(rectangleArea(W: 300, H: 200))

在局部参数名之前给一个外部参数名,那么在调用时必须使用外部参数名,所以W和H不能省略。
可以使用_表示省略外部参数名,这样函数调用的时候就可以省略外部参数名了。

func rectangleArea(_ width: Double, _ height: Double) ->Double {
    let area = width * height
    return area
}
print(rectangleArea(300, 200))

可变参数

Swift中函数的个数可以变化,它可以接受数量不确定的输入类型参数(这些参数具有相同类型),有点类似于传递一个数组。在参数类型名后面加入…的方式来表示这些是可变参数

func sum(numbers: Double...) ->Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total
}
sum(numbers: 100.0, 300, 200)

参数的传递引用

参数传递方式有两种:值类型和引用类型。值类型给参数传递的是参数的一个副本,引用类型是把数据本身引用(内存地址)传递过去,这样在函数的调用过程中会影响数据原型。在参数类型声明前加inout即可声明引用类型。

func increment(value: inout Double, amount: Double = 1.0) {
    value += amount
}

var value: Double = 10.0
increment(value: &value)
print(value)

increment(value: &value, amount: 100.0)
print(value)

闭包

一门计算语言要支持闭包有两个前提:

  1. 支持函数类型,能够将函数作为参数或返回值传递
  2. 支持函数嵌套
func calculate(opr: String) -> (Int, Int) -> Int {
    func add(a: Int, b: Int) -> Int {
        return a + b;
    }
    func sub(a: Int, b: Int) -> Int {
        return a - b;
    }
    var result: (Int, Int) -> Int
    switch (opr) {
    case "+":
        result = add
    case "-":
        result = sub
    default:
        result = add
    }
    return result;
}

let f1:(Int, Int) -> Int = calculate(opr: "+")
print(f1(10, 5))

let f2:(Int, Int) -> Int = calculate(opr: "-")
print(f2(10, 5))

由函数嵌套构成的返回函数类型的函数。

func calculate(opr: String) -> (Int, Int) -> Int {
    var result : (Int, Int) -> Int
    switch opr {
    case "+":
        result = {(a: Int, b: Int) -> Int in
            return a + b;
        }
    default:
        result = {(a: Int, b: Int) -> Int in
            return a - b;
        }
    }
    return result
}

let f1:(Int, Int) -> Int = calculate(opr: "+")
print(f1(10, 5))

let f2:(Int, Int) -> Int = calculate(opr: "-")
print(f2(10, 5))

原来的嵌套函数add和sub被表达式所取代,
{(a: Int, b: Int) -> Int in return a + b}
这就是Swift中的闭包表达式。通过以上的演变,我们可以给出Swift中闭包一个定义:闭包是自包含的匿名函数代码块,可以作为表达式、函数参数和函数返回值,闭包表达式的运算结果是一种函数类型

闭包表达式使用灵活,其标准语法格式如下:

{(参数列表) -> 返回值类型 in
    语句组
}

根据情况可以类型推断简化,隐藏关键字return,省略参数名。

可选链

在Swift程序表达式中会看到问号?和感叹号!,这些符号都与可选类型和可选链有关。

可选链的概念

class Employee {
    var no: Int = 0
    var name: String = ""
    var dept: Department = Department()
}

class Department {
    var no: Int = 0
    var name: String = "SALES"
    var comp: Company = Company()
}

class Company {
    var no: Int = 0
    var name: String = "Ali"
}

let emp = Employee()
print(emp.dept.comp.name)

Employee类的基础上增加了Department类,Department类上增加了Company类,Employee类通过dept属性与Department类关联,Department类通过comp属性与Company类关联。给定一个Employee实例,通过emp.dept.comp.name可以引用到Company实例,形成一个链条。但是链条中的任何一个环节断裂(为nil)都无法引用到最后的目标。
事实上,使用Department的构造函数实例化dept属性,也就是说一个Employee实例一定有一个Department与其关联,但现实世界并不总是存在,例如一个新入职的员工未必有部门,这种关联可能有,可能没有,所以我们需要一个可选的关联属性。修改代码如下。

class Employee {
    var no: Int = 0
    var name: String = ""
    var dept: Department?
}

class Department {
    var no: Int = 0
    var name: String = "SALES"
    var comp: Company?
}

class Company {
    var no: Int = 0
    var name: String = "Ali"
}

let emp = Employee()
//print(emp.dept!.comp!.name) 编译错误
print(emp.dept?.comp?.name)

将dept、comp属性改为可选类型,原来的引用方式已经不能再用了,我们前面介绍了可选类型的拆包,显示拆包有一个问题,如果可选链的某个环节为nil,将会导致运行时出错。我们可以采用更温和的方式,使用问号代替感叹号。如果某个环节为nil,不会抛出错误,会把nil返回给引用者。这种由问号引用可选类型的方式称为可选链

类和结构体的异同

在Swift中枚举、结构体、类都是具有面向对象属性的。
类和结构体都具有以下功能

  • 定义存储属性
  • 定义方法
  • 定义小标
  • 定义构造函数
  • 定义扩展
  • 实现协议

只有类才有的功能

  • 能够继承另外一个类
  • 能够核对运行时对象的类型
  • 析构对象释放资源
  • 引用计数允许一个实例有多个引用

属性与下标

存储属性

存储属性可以存储数据,分为常量属性和变量属性。存储属性适用于类和结构体两种类型,不适用枚举类型。
引用类型相当于指针,类的实例化常量相当于常量指针,常量指针是不能修改的,但是常量指针指向的内容是可以修改的,而常量值类型,无论是结构体还是枚举类型都是不能修改的。

延迟存储属性
class Employee {
    var no: Int = 0
    var name: String = ""
    var dept: Department = Department()
}

struct Department {
    let no: Int = 0
    var name: String = ""
}

let emp = Employee()

观察以上代码,在创建Employee实例时,程序会实例化dept属性。然而程序或许不关心他隶属于哪个部门,只关心他的编号和姓名。虽然不使用dept实例,但是仍然会创建会占用内存。在Java中,有一种数据持久化技术叫Hibernate,是一种延时加载技术。Swift中也采用了此种技术,修改代码如下:

class Employee {
    var no: Int = 0
    var name: String = ""
    lazy var dept: Department = Department()
}

struct Department {
    let no: Int = 0
    var name: String = ""
}

let emp = Employee()

在dept属性前加上关键字lazy声明,这样dept属性就是延时加载。顾名思义,就是dept属性只有在第一次访问时才加载,如果用于不访问,就不会创建,就可以减少内存使用。

计算属性

计算属性本身不存储数据,而是从其他存储属性中计算得到数据。与存储类型不同,类、结构体和枚举都可以定义计算属性。
提供了一个getter取值访问器来获取值,以及一个可选但setter访问器来设置值。

import Foundation

class Employee {
    var no: Int = 0
    var firstName: String = "Tony"
    var lastName: String = "Guan"
    var fullName: String {
        get {
            return firstName + "." + lastName
        }
        set {
            var name = newValue.components(separatedBy: ".")
            firstName = name[0]
            lastName = name[1]
        }
    }
}

var emp = Employee()
print(emp.fullName)

emp.fullName = "Tom.Bryant"
print(emp.fullName)

fullName由存储属性firstnamelastname拼接而成,fullName = fisrtname.lastname,Swift默认新值newValue代表要修改的新值,rawValue代表原始值。

self可以用于类、结构体和枚举类型中,代表当前实例,通过self可以访问自身的实例方法和属性,self可以省略,但是如果属性名和局部变量名或者常量发生冲突就必须使用self。

属性观察者

属性观察者可以监听存储属性的变化,即便变化前后的值相同,它们也能监听到。属性观察者可以在类和结构体中使用,不能在枚举中使用。
Swift中的属性观察者主要有以下两个:

  • willSet 观察者在修改之前调用
  • didSet 观察者在修改之后立刻调用
class Employee {
    var no: Int = 0
    var name: String = "Tony" {
        willSet {
            print("员工name新值:\(newValue)")
        }
        didSet {
            print("员工name旧值:\(oldValue)")
        }
    }
}
var emp = Employee()
emp.no = 100
emp.name = "Swift"

属性观察者就基于存储属性,所以不能在结构体中使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值