闭包(Closures)
* 闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
* 在Swift中的闭包与C、OC中的blocks和其它编程语言(如Python)中的lambdas类似。
* 闭包可以捕获和存储上下文中定义的的任何常量和变量的引用。这就是所谓的变量和变量的自封闭,
* 因此命名为”闭包“("Closures)").Swift还会处理所有捕获的引用的内存管理。
闭包的形式有:
* (1)全局函数都是闭包,有名字但不能捕获任何值。
* (2)嵌套函数都是闭包,且有名字,也能捕获封闭函数内的值。
* (3)闭包表达式都是无名闭包,使用轻量级语法,可以根据上下文环境捕获值。
* (4) 全局函数和嵌套函数其实就是特殊的闭包
Swift中的闭包有很多优化的地方:
* (1)根据上下文推断参数和返回值类型
* (2)从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
* (3)可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)
* (4)提供了尾随闭包语法(Trailing closure syntax)
以sort 方法为例 讲解闭包的
sort(@noescape isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
1.在sort方法中调用函数
let arr = ["a", "b", "c", "m", "e", "d"]
// 排序字符串数组
func sortString(a: String, b: String) -> Bool {
return a < b
}
print(arr.sort(sortString))
2.使用闭包形式
完整闭包写法是在花括号内有参数列表和返回值,用关键字in表明闭包体的开始
闭包的语法:
{(参数)-> 返回值类型 in 功能代码}
let arr1 = ["a", "e", "q", "b"]
print(arr1.sort({ (a: String, b: String) -> Bool in
return a > b
}))
简化闭包
1.根据上下文推断类型
参数列表都没有指明类型,也没有指明返回值类型,这是因为swift可以根据上下文推测出
print(arr1.sort({ a, b in
return a < b
}))
2.单表达式闭包隐式返回
因为闭包体只有一行代码,可以省略return
print(arr1.sort({ a, b in
a > b
}))
3.参数名称缩写(($i,i=0,1,2...从0开始的)Swift会推断出闭包需要两个参数)
print(arr1.sort({$0 > $1}))
4.运算符函数
// 闭包在某些情况下可以直接使用运算符函数
print(arr1.sort(>))
5.尾随闭包
* 尾随闭包(Trailing Closures)
* 如果函数需要一个闭包参数作为参数,且这个参数是最后一个参数,而这个闭包表达式又很长时,
* 使用尾随闭包是很有用的。尾随闭包可以放在函数参数列表外,也就是括号外。如果函数只有一个参数,
* 那么可以把括号()省略掉,后面直接跟着闭包。
// 尾随闭包
func tailClosure(a: Int, b: (Int, Int) -> Int){
print("\(a) \(b(1,2))")
}
// 调用函数
// 尾随闭包的语法:函数(){闭包}
tailClosure(10) { (a: Int, b: Int) -> Int in
return a + b
}
6.捕获值 - 函数嵌套
* 闭包可以根据环境上下文捕获到定义的常量和变量。闭包可以引用和修改这些捕获到的常量和变量,
* 就算在原来的范围内定义为常量或者变量已经不再存在(很牛逼)。
* 在Swift中闭包的最简单形式是嵌套函数。
// 函数的嵌套是一种特殊形式的闭包 函数类型作为返回值 sum可以保留上次的结果
func increametMaker(cnt: Int) -> () -> Int {
var sum = 0
func increamet() -> Int {
// 在子函数中可以操作外部函数的变量
sum += cnt
return sum
}
return increamet
}
let maker = increametMaker(10)
print(maker())
7.闭包是引用类型
// 闭包是引用类型 -- 传址
let maker1 = maker // 指向同一块地址
print(maker1())
类和结构体
1.类和结构体的对比
Swift 中类和结构体有很多共同点。共同处在于:
• 定义属性用于储存值
• 定义方法用于提供功能
• 定义下标用于通过下标语法访问值
• 定义初始化器用于生成初始化值
• 通过扩展以增加默认实现的功能
• 符合协议以对某类提供标准功能
与结构体相比,类还有如下的附加功能:
• 继承允许一个类继承另一个类的特征
• 类型转换允许在运行时检查和解释一个类实例的类型
• 取消初始化器允许一个类实例释放任何其所被分配的资源
• 引用计数允许对一个类的多次引用
2.类和结构体的语法定义
通过关键字class和struct来分别表示类和结构体,并在一对大括号中定义它们的具体内容
class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}
类名和结构体名的命名规范:大驼峰
3.类和结构体实例化
结构体和类都可以使用初始化器语法来生成新的实例。初始化器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如Resolution()或VideoMode()。但是结构体还有另外一个初始化方式:成员逐一初始化器
var p = Person()
var s = Student(name: "xiaowang", age: 30)
4.属性访问 —— 以点语法的形式访问属性
5.结构体和枚举是值类型 类是引用类型
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。
引用类型在被赋予到一个变量,常量或者被传递到一个函数时,操作的并不是其拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
枚举
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
值类型
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// 输出 "cinema is now 2048 pixels wide"
print("hd is still \(hd.width ) pixels wide")
// 输出 "hd is still 1920 pixels wide"
引用类型
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 输出 "The frameRate property of theEighty is now 30.0"
6.恒等运算符
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:
• 等价于 ( === )
• 不等价于 ( !== )
类中的方法
1.实例方法和类方法
2.实例方法的调用
3.self 属性
// 实例方法的使用
class Dog {
var name: String = ""
var age: Int = 0
// 创建实例方法
func run() {
print("running")
}
// 传参
func wang(name: String) {
// 在实力方法中调用属性时 必须使用self点语法调用 不然报错
// 此时的self为当前类对象
self.name = name
}
// 类方法
static func jump() {
print("jumping")
}
}
结构体中的方法
1.值类型实例方法中修改属性
2.在可变方法中给 self 赋值
// 结构体
struct Cat {
var name: String = ""
var age: Int = 0
// 创建实例方法
func voice() {
print("喵~")
}
mutating func eat(name: String) {
// 在结构体中的实例方法 不能直接操作外面的属性 若想操作需加关键字mutating
self.name = name
}
// 创建可变方法
mutating func run(){
// 在可变方法中 可以修改self的值
self = Cat(name: "金钱豹", age: 5)
}
// 类方法
static func jump() {
print("jumping")
}
}
属性 — 存储属性和计算属性
1.存储属性
一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。
// 类中的存储属性
class People {
// 声明存储属性
var name: String = ""
// let声明的属性 在外界无法通过点语法调用修改值
let age: Int = 0
}
// 这里let可以 因为指向的地址
let people = People()
people.name = "山治"
// 不允许改变常量的值
//people.age = 30
//print(people.age)
print(people.name)
// 结构体里面的存储属性
struct Tiger {
var name: String = ""
// 在结构体中 通过let声明的属性 不能通过点语法调用修改值
let age: Int = 0
}
// 值引用
let ti = Tiger()
// 若结构体对象
// ti.name = "wang"
//ti.age = 30
延迟存储属性
// 延迟存储属性
class Monkey {
var arr = [Int]()
// 直到调用该属性时 才会进行属性的运算 类似于懒加载
lazy var name: String = ""
}
var mo = Monkey()
mo.arr.append(1)
mo.arr.append(2)
// 当调用name时 CPU才会对name属性进行运算
mo.name = "孙悟空"
print(mo.name)
2.计算属性
计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
便捷 setter 声明 — 如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称newValue
// 实现功能:创建矩形对象 将原点设置 宽高设置 查找中心点
struct Point {
var x = 0
var y = 0
}
struct Size {
var width = 0
var height = 0
}
// 声明矩形类
class Rect {
var point = Point()
var size = Size()
// 计算型属性 需要通过set/get方法的
装实现
var center: Point {
// centerPoint 等号右边的值 可用newValue代替
set {
point.x = newValue.x - size.width / 2
point.y = newValue.y - size.height / 2
}
get {
let centerX = point.x + size.width / 2
let centerY = point.y + size.height / 2
return Point(x: centerX, y: centerY)
}
}
}
var rect = Rect()
rect.point.x = 0
rect.point.y = 0
rect.size.width = 10
rect.size.height = 20
print(rect.center)
只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
// 声明只读的属性
class School {
// 学校名称
var name: String = ""
// 班级个数
var classNumber = 0
// 每个班级人的个数
var gradeNumber = 0
// 查看学校总人数 (只读属性)
// 语法 声明变量 变量名:类型{return }
var totalStudents: Int {
return classNumber * gradeNumber
}
}
var sc = School()
sc.name = "qhfz"
sc.classNumber = 20
sc.gradeNumber = 10
//sc.totalStudents = 90
print("\(sc.name)学校共有\(sc.totalStudents)学生")
类属性语法 与 属性监视器
// 属性的监视器
class Hotel {
// 监听属性是否发生改变
var name = "7tian" {
willSet {
print(name)
}
didSet {
print(name)
}
}
// 类属性
static var age: Int = 10
}
var hotel = Hotel()
hotel.name = "hanting"
// 调用类属性
Hotel.age = 50
print(Hotel.age)
继承
定义一个基类
子类生成
子类可以继续继承子类
重写方法
防止重写
// 继承 当用final修饰类时 代表该类不能被继承
class Car {
var name = ""
var size = 0
func run() {
print("running^")
}
// 防止重写 final 防止子类对父类中方法的重写
final func stop() {
print("stop")
}
}
// 继承父类Car
class Bus: Car {
var number = 0
}
// 没有多继承 但是可以多层继承
var bus = Bus()
// 继承的属性
bus.name = "Bus"
bus.size = 100
// 继承的方法
bus.run()
// 派生的属性
bus.number = 81
// 多层继承可以继承基类中的属性和方法
// 继承自Bus
class RedBus: Bus {
var color = "red"
// 重写父类的方法 override
override func run() {
// 调用父类的方法
super.run()
print("RedBus-Running^^")
}
}
var redBus = RedBus()
redBus.name = "RedBus"
redBus.run()
构造和析构
构造函数
不带外部名的构造器参数
构造函数参数的内部名称和外部名称
构造过程中常量属性的修改
// 构造函数的使用
class Animal {
func run() {
print("running^^^^^")
}
var name = ""
var age = 0
// 在构造方法中修改常量的值时 该值不能被初始化
let num: Int
// 创建构造方法给属性赋值
// 当类被初始化时(实例化) 第一个调用的函数就是构造函数
// init() {
// self.name = "cat"
// print("在构造方法中给属性赋值")
// }
// 带有参数的构造函数
// init(a: String){
// self.name = a
//
// }
// 带有外部参数的构造函数
// init(name param1: String, age param2: Int){
// self.name = param1
// self.age = param2
// }
// 隐藏所有外部参数
// init(_ param1: String, _ param2: Int) {
// self.name = param1
// self.age = param2
// }
// 在构造函数中可以访问没有被初始化的常量属性
init(n: Int) {
self.num = n
}
}
//var animal = Animal()
//var animal = Animal(a: "dog")
//var animal = Animal(name: "monkey", age: 5)
//var animal = Animal("chicken",2)
var animal = Animal(n: 100)
print(animal.num)
默认构造器
结构体的逐一成员构造器
// 默认的构造器
class Mouse {
var name = ""
}
// 每个类中会有默认构造器 init(){} 不写这行代码 也会联想小括号
var mouse = Mouse()
// 结构体的逐一成员构造器
struct Phone {
var name = ""
var age = 0
}
// 只有值类型的数据被实例化时 才有逐一成员构造器
var phone = Phone(name: "iPhone", age: 4)
构造器的继承和重写
析构过程
// 构造器的继承和重写
class Table {
var name = ""
init(name: String){
self.name = name
}
}
class Desk: Table {
override init(name: String) {
// 重写父类的构造方法 必须调用父类相关的构造方法
super.init(name: name)
print("重写构造函数 千万不要忘记super.init")
}
// 析构函数 -- 最后调用的方法 当ARC销毁对象是调用析构函数
deinit {
print("deinit")
}
}
var desk: Desk? = Desk(name: "Teacher Li Desk")
//打印结果: 重写构造函数 千万不要忘记super.init Teacher Li Desk
print(desk!.name)
// 调用析构函数 需将对象设置为可选类型
desk = nil
只读属性类型的拓展
方法扩展
// 类别
extension Int {
// 只能添加只读类型的属性
var a: Int {
return self * self
}
}
print(5.a)
class Duck {
var name = ""
}
extension Duck {
var age: String{
return self.name + "qianfeng"
}
func run() {
print("Dusk Running")
}
}
var duck = Duck()
duck.run()
duck.name = "唐老鸭"
print(duck.age)
协议语法
协议中属性的用法
协议中方法的用法
委托模式
// 协议
protocol Work: class {
// 方法
// 只需声明 默认必须实现
func teaching()
}
// 可选的协议方法
@objc protocol Teach {
optional
func say()
}
// 如果某个类既有父类 有需要遵守协议 class 类名:父类,协议,协议2.。。
class Worker: Work, Teach {
// 实现协议中的方法
func teaching() {
print("协议中的方法")
}
@objc func say() {
print("可选的协议方法")
}
}
下标的使用
// 类中下标的使用
var arr = [2,3,1,7]
print(arr[2])
class Days {
var days = ["Mon", "Tues", "wend", "Thir", "Fri"]
// 类中创建下标
// 下标可以有一个或者多个参数 有一个返回值
subscript (a: Int) -> String {
// get方法的封装
get {
return days[a]
}
}
}
let days = Days()
// 通过下标访问实例对象
let day = days[0]
print(day)
class Person {
var arr = Array(count: 10, repeatedValue: 0)
// 类中创建下标
// 下标可以有一个或者多个参数 有一个返回值
subscript (a: Int) -> Int {
// set get方法的封装
set{
arr[a] = newValue
}
get {
return arr[a]
}
}
}
let person = Person()
person[0] = 200
print(person[0])
// 对类中的数组封装起一个辅助的功能
可选链
// 可选链 - 通过连续的点语法调用
class Eye {
var num = 2
}
class Student {
// 设置可选类型 确定是以类的属性的存在
var eye: Eye? = Eye()
}
let st = Student()
// 至少有一个属性的类型为可选模式 手动将问号改为叹号
print(st.eye!.num)
is类型检测 Any与AnyObject
// 类型检测
var a = 20
//if a is Int {
// print("Int")
//}
// Any 任何类型
var objInt: Any = a
if objInt is Int {
print("Int")
}
class A {
var name = ""
}
class B: A {
}
class C {
}
var b = B()
// 判断对象是否属于类或者父类
// AnyObject 与id一致 可以指向任何实例,一般指向对象 Any是执行任何类型
var objB: AnyObject = b
if objB is A {
print("B")
}
as! as? 类型转换
// as! as? 类型转换
// 类类型做数据转换 objB as! B 将objB该类型转换成B类型
var obj = objB as! B
// 用as?做强制转换 如果强制转换成功 则是转换的数据类型 如果转换失败,则是nil
var obj2 = objB as? C
if obj2 == nil {
print("不能转化为该类型")
}
异常处理
// 异常处理
// 声明异常
enum MyError: ErrorType {
case Error0
case OutOfRange
case ErrorOther
}
func divide(a: Int, b: Int) throws -> Int {
if b == 0 {
// 抛出异常 : 1.首先将函数定义成throws 函数
throw MyError.Error0
} else {
return a / b
}
}
// 调用函数
// 抛出异常就必须接收异常
do {
let rs = try divide(10, b: 0)
print(rs)
} catch MyError.Error0 {
print("除数不能为0")
}
// 通过try! try? 调用throwing函数
// try? 有异常为nil 没异常为值
//let rs = try? divide(5, b: 0)
//if rs != nil {
// print(rs!)
//} else {
// print("youyichang-")
//}
// try! 在确定throwing函数肯定无异常时 可以通过try!调用
Swift基础知识(二)
最新推荐文章于 2024-10-07 16:59:33 发布