构造器介绍
class Person {
var name: String
var age: Int
// 指派构造器前面加上required可以将构造器指定为必要构造器
// 所谓的必要构造器意味着子类也要提供一模一样的构造器
// 指派构造器(designated)
required init(name: String, age: Int) {
print("创建一个人!")
self.name = name
self.age = age
}
// 便利构造器(convenience)
convenience init() {
self.init(name: "无名氏", age: 20)
}
deinit {
print("人嗝屁了!")
}
}
class Student: Person {
var major: String
required init(name: String, age: Int) {
major = "未知"
super.init(name: name, age: age)
}
convenience init(name: String, age: Int, major: String) {
// 下面的语句必须写在调用自己的初始化方法之后否则major属性会被赋上不正确的值
// self.major = major
self.init(name: name, age: age)
self.major = major
// 初始化的第一阶段
// 1. 初始化自己特有的属性
// self.major = major
// // 子类只能调用直接父类的构造器
// // 子类构造器必须调用父类的非便利构造器(指派构造器)
// // super.init() // compiler error
// // 2. 调用父类的初始化方法
// super.init(name: name, age: age)
// // 初始化的第二阶段
// // 此处可以调用对象的方法因为对象已经完成了初始化
// study()
}
func study() {
print("\(name)正在学习.")
}
deinit {
print("学生对象嗝屁了!")
}
}
class Teacher: Person {
deinit {
print("老师对象嗝屁了!")
}
}
//// 创建一个学生对象 然后用stu1去引用它 所以此时学生对象引用计数为1
//var stu1: Student? = Student()
//// 此处没有创建新的学生对象 原来的学生对象的引用计数+1
//var stu2 = stu1
//// 同上 原来的学生对象的引用计数+1
//var stu3 = stu2
//
//// 学生对象引用计数-1
//stu1 = nil
//// 学生对象引用计数-1
//stu2 = nil
//// 学生对象引用计数-1
//// 当学生对象引用计数为0时 ARC会自动清理内存释放学生对象
//// ARC即时性的内存清理 优于Java中的Garbage Collection(垃圾回收)
//stu3 = nil
// var stu1: Student? = Student()
// weak修饰的引用(弱引用)不会增加引用计数 默认是强引用(会增加引用计数)
// weak var stu2 = stu1
// weak var stu3 = stu2
// stu1 = nil
// 如果想释放内存 程序员可以手动将一个引用赋值为nil
// func foo() {
// stu是一个局部变量/常量 在函数调用结束后局部变量就消失了
// 所以学生对象的引用计数也就变成0了 所以会被ARC释放掉
// let stu = Student()
// print(stu)
// }
// foo()
// 栈 - FILO 先进后出的结构
// 创建任何子类对象的时候一定是先创建了父类对象
// var stu: Person = Student()
// 引用转移(会导致原来对象上的引用计数-1 新对象引用计数+1)
// stu = Teacher()
// stu = Person()
// Swift的自动释放池
// 通过向autoreleasepool函数中传入一个闭包来实现
// autoreleasepool { () -> () in
// 自动释放池中的对象引用在池的边界会收到引用计数-1的消息
// 将来做iOS开发时如果某个地方会创建很多的临时对象
// 那么最好在此处设置一个自动释放池避免内存瞬时峰值过高造成闪退
// let stu1 = Student()
// let stu2 = stu1
// }
// 离开自动释放池时 stu1会收到引用计数-1消息 stu2也会收到引用计数-1消息
// 如果程序中出现了类与类之间双向关联关系 必须要将其中一端设置为weak引用
// 否则将会形成循环引用导致ARC无法释放内存
//class Emp {
// // 推荐使用
// // 如果允许使用可空类型通常使用weak来破除循环引用
// // 如果员工关联的部门对象被释放了那么dept会被赋值为nil
// // 如果要继续给dept对象发消息程序不会崩溃
// // weak var dept: Dept?
//
// // 谨慎使用
// // 如果不允许使用可空类型就必须使用unowned来破除循环引用
// // 需要注意的是如果员工对象关联的部门对象被释放了
// // 如果还要通过员工对象去操作它所关联的部门对象将导致程序崩溃
// // EXC_BAD_ACCESS
// unowned var dept: Dept
//
// init(dept: Dept) {
// print("创建一个员工")
// self.dept = dept
// }
//
// deinit {
// print("销毁一个员工")
// }
//}
//
//class Dept {
// var manager: Emp?
//
// init() {
// print("创建一个部门")
// }
//
// deinit {
// print("销毁一个部门")
// }
//}
//
//func bar() {
// // let person = Person()
// let dept = Dept()
// let emp = Emp(dept: dept)
// dept.manager = emp
//}
//
//bar()
泛型介绍
// 泛型 (generic) - 让类型不再是程序中的硬代码(写死的东西)
func bubbleSort<T: Comparable>(array: [T]) -> [T] {
var newArray = array
for i in 0..<newArray.count - 1 {
var swapped = false
for j in 0..<newArray.count - 1 - i {
if newArray[j] > newArray[j + 1] {
mySwap(&newArray[j], &newArray[j + 1])
swapped = true
}
}
if !swapped {
break
}
}
return newArray
}
// 定义一个虚拟类型T, 调用函数时根据传入的参数类型来决定T到底是什么
func mySwap<T>(inout a: T, inout _ b: T) {
let temp = a
a = b
b = temp
}
// 泛型限定
// <T: Comparable>限定T类型必须是遵循了Comparable协议的类型
func myMin<T: Comparable>(a: T, _ b: T) -> T {
return a < b ? a : b
}
栈、堆的泛型例子解析
// Swift中的类、结构和枚举都可以使用泛型
struct Stack<T> {
var data: [T] = []
// 入栈
mutating func push(elem: T) {
data.append(elem)
}
// 出栈
mutating func pop() -> T {
return data.removeLast()
}
var isEmpty: Bool {
get { return data.count == 0 }
}
}
var stack = Stack<String>()
stack.push("hello")
stack.push("good")
stack.push("zoo")
while !stack.isEmpty {
print(stack.pop())
}
可空链、开火车式编程
class Person {
var car: Car?
}
class Car {
var engine: Engine?
}
class Engine {
var id: String?
}
let p = Person()
// 可空链语法(适用于开火车式的编程)
print(p.car?.engine?.id?.uppercaseString)
运算符重载与运行时异常的处理
// 短除法(欧几里得算法)
// x和y的最大公约数跟y%x和x的最大公约数是一样的
// Greatest Common Divisor
func gcd(x: Int, _ y: Int) -> Int {
if x > y {
return gcd(y, x)
}
else if y % x != 0 {
return gcd(y % x, x)
}
else {
return x
}
}
// 定义一个遵循ErrorType协议的枚举
// 通过不同的case定义程序中可能出现的若干种异常状况
enum FractionError: ErrorType {
case ZeroDenominator // 分母为0
case DivideByZero // 除以0
}
class Fraction {
private var _num: Int
private var _den: Int
var info: String {
get {
return _num == 0 || _den == 1 ? "\(_num)" : "\(_num)/\(_den)"
}
}
// 如果一个方法抛出了异常 那么在声明方法时必须要写上throws关键字
// throws关键字是提醒方法的调用者方法可能会出状况 调用时要写try
init(num: Int, den: Int) throws {
_num = num
_den = den
if _den == 0 {
// 如果程序中出现问题就抛出错误(异常)
// 被throw关键字抛出的必须是遵循ErrorType协议的东西
throw FractionError.ZeroDenominator
}
else {
simplify()
normalize()
}
}
func add(other: Fraction) -> Fraction {
// 如果能够确保方法调用时不出异常那么可以在try关键字后加!
// 这样就可以在不写do...catch的情况下调用可能出状况的方法
return try! Fraction(num: _num * other._den + other._num * _den, den: _den * other._den)
}
func sub(other: Fraction) -> Fraction {
return try! Fraction(num: _num * other._den - other._num * _den, den: _den * other._den)
}
func mul(other: Fraction) -> Fraction {
return try! Fraction(num: _num * other._num, den: _den * other._den)
}
func div(other: Fraction) throws -> Fraction {
if other._num == 0 {
throw FractionError.DivideByZero
}
return try! Fraction(num: _num * other._den, den: _den * other._num)
}
func normalize() -> Fraction {
if _den < 0 {
_num = -_num
_den = -_den
}
return self
}
func simplify() -> Fraction {
if _num == 0 {
_den = 1
}
else {
let x = abs(_num)
let y = abs(_den)
let g = gcd(x, y)
_num /= g
_den /= g
}
return self
}
}
// 运算符重载(为自定义的类型定义运算符)
func +(one: Fraction, two: Fraction) -> Fraction {
return one.add(two)
}
func -(one: Fraction, two: Fraction) -> Fraction {
return one.sub(two)
}
func *(one: Fraction, two: Fraction) -> Fraction {
return one.mul(two)
}
func /(one: Fraction, two: Fraction) throws -> Fraction {
return try one.div(two)
}
func foo() {
// 如果能够保证代码不出错可以在try后面加!
// 如果不确定代码是否出错可以在try后面加?
// 需要注意的是有?的地方会产生Optional(可空类型)
// 稍后可能还需要对可空类型进行拆封, 拆封方式有二:
// 1. 不安全的做法: xxx!
// 2. 安全的做法: 用if let = xxx { }进行拆封
let f1 = try? Fraction(num: 3, den: 0)
let f2 = try? Fraction(num: 0, den: 9)
if let a = f1, b = f2 {
let f3 = a + b
print(f3.info)
}
else {
print("无效的分数无法进行加法运算")
}
}
foo()
// 对于可能出状况的代码要放在do...catch中执行
// 在可能出状况的方法前还要写上try表示尝试着执行
// 如果在do中没有出现任何状况那么catch就不会执行
// 如果do中出现了状况代码就不会再向下继续执行而是转移到catch中
// 在do的后面可以跟上多个catch用于捕获不同的异常状况 但是最多只有一个catch会被执行
//do {
// let f1 = try Fraction(num: 3, den: 4)
// let f2 = try Fraction(num: 0, den: 9)
//
// print(f1.info)
// print(f2.info)
//
// let f3 = f1 + f2
// print(f3.info)
// let f4 = f1 - f2
// print(f4.info)
// let f5 = f1 * f2
// print(f5.info)
// let f6 = try f1 / f2
// print(f6.info)
//}
//catch FractionError.ZeroDenominator {
// print("瓜西西的, 分母不能为0!!!")
//}
//catch FractionError.DivideByZero {
// print("卵球了, 除以0是不行的!!!")
//}
//catch {
// print("出错了! 我也不知道什么问题")
//}
对字符串的处理方式
/// 学生姓名隐去最后一个字符
public var name: String {
get {
let displayName = _name.substringToIndex(_name.endIndex.advancedBy(-1))
return displayName + "*"
}
网友评论