Swift当中的函数
关于Swift中的函数的叫法来自于官方文档function(函数)和method(方法),和OC中的方法C中的函数基本差不多,大体可以分为无参无返、有参无返、无参有返和有参有返四中类型。
Swift中函数的简单定义
// Swift中的函数定义格式为:
// func 函数名(参数列表) -> 返回值类型 {
// 代码块
// return 返回值
// }
// 无参无返回
func eat() ->Void { // 当没有返回值时 -> void 可以省略
print("正在吃饭");
}
// 有参无返
func eatWith(food : String) {
print("正在吃\(food)");
}
// 无参有返
func receiveMessage() -> String {
return "10086"
}
// 有参有返
func getSumWith(num1 : Int, num2 : Int) ->Int {
return num1 + num2
}
// 函数调用
eat()
eatWith(food: "油条")
print(receiveMessage())
print(getSumWith(num1: 10, num2: 20))
Swift中函数参数的使用
import UIKit
// 外部和内部参数
// 外部参数:可以在函数外部看见参数名称的参数叫做外部参数
// 内部参数:只能在函数内部看见参数名称的参数叫做内部参数
// 在Swift3.0中,函数中的参数默认既是外部参数又是内部参数
func calculate(num1 : Int, num2 : Int) -> Int
{
return num1 + num2
}
func calculate1(tmp1 num1 : Int, tmp2 num2 : Int) -> Int
{
return num1 + num2
}
func calculate2(_ num1 : Int, _ num2 : Int) -> Int
{
return num1 + num2
}
calculate(num1: 10, num2: 20) // 此时num1、num2既是外部参数也是内部参数
calculate1(tmp1: 20, tmp2: 10) // 此时tmp1、tmp2是外部变量,相当于num1和num2起的别名,num1和num2为内部变量
calculate2(30, 20) // 在函数参数名前面加上下划线,可将函数参数变为内部变量
// 可变参数
// 在参数类型后面加上 ... 可将参数变为可变参数,参数的个数由一个到多个不等,此时的nums是数组类型
func calculateWithNums(_ nums : Int...) ->Int {
var sum = 0
for num in nums {
sum += num
}
return sum
}
calculateWithNums(20,10,11,32)
// 默认参数
func getCoffeeWithName(_ name : String = "雀巢") -> String {
return "获得一杯\(name)"
}
getCoffeeWithName() // 获得一杯雀巢 如果不传参数 就是用参数默认值
// 指针参数
// 函数的参数传入的是内存地址 在参数的类型前面加上 inout 关键词
func exchangeNums(_ num1 : inout Int, _ num2 : inout Int ) {
let tmp = num1
num1 = num2
num2 = tmp
}
var num1 = 10; var num2 = 20
print("num1 = \(num1) num2 = \(num2)") // num1 = 10 num2 = 20
exchangeNums(&num1, &num2)
print("num1 = \(num1) num2 = \(num2)") // num1 = 20 num1 = 10
Swift中的枚举
枚举是定义了一个通用类型的一组相关的值,可以在代码中以安全的方式使用这些值。
在C和OC当中枚举是指定相关名称为整型值(有名字的常量)。
在Swift中枚举更加的灵活,不必给每一个枚举成员提供一个值,也可以提供值为字符串,字符,或是整型和浮点型。
Swift中如何定义一个枚举
import UIKit
// 定义一个枚举
enum GenderType {
case male
case female
case unknown
}
// 如何使用枚举的值
let gender_01 : GenderType = .male // 根据上下文能够推导出枚举类型,可以直接写.真实类型
let gender_02 : GenderType = GenderType.female // 也可以写 枚举类型.真实类型
// 如何给枚举成员赋值
enum DirectionType : String { // 赋值前 必须在枚举类型后面标注枚举成员赋值的类型
case east = "east"
case south = "south"
case west = "west"
case north = "north"
}
let dir = DirectionType(rawValue: "south") // DirectionType?
if let dir = DirectionType(rawValue: "west") { // 由于返回值可能为nil 所以是可选类型
print(dir.rawValue)
}
// 枚举类型的定义方式二
enum CoffeeType : Int {
case nescafe = 0, cappuccino, mocha
}
print(CoffeeType.cappuccino.rawValue) // 枚举成员值的类型若为Int型,赋值一个,后面的自动自增
Swift中的结构体
结构体(struct)是由一系列相同类型或不同类型的数据构成的数据集合,结构体是值类型,在方法和函数中是值传递。
Swift中结构体的基本使用
import UIKit
// 1.定义一个结构体
struct HWLocation {
// 属性
var x : Double
var y : Double
}
// 2.结构体的创建
let struct_01 = HWLocation(x: 10, y: 20)
// 3.给结构体扩充方法
struct Location {
// 属性
var x : Double
var y : Double
// 扩充方法一
// 打印坐标方法
func printLocation() {
print("Location x : \(self.x) y : \(self.y)")
}
// 扩充方法二
// 更改坐标方法 (扩充函数中涉及修改属性值时 函数前必须添加 mutating 关键字)
// mutating 变化
mutating func changeLocation(_ x : Double, _ y : Double) {
self.x = x
self.y = y
}
}
var struct_02 = Location.init(x: 20, y: 100)
struct_02.printLocation() // 结构体扩充方法调用
struct_02.changeLocation(9, 9) // 结构体扩充方法 修改属性的值 (PS: 结构体必须由var可变修饰才能更改)
print(struct_02) // Location(x: 9.0, y: 9.0) 修改成功
// 4.给结构体扩充构造函数 (系统默认会给结构体扩充全属性赋值构造函数)
// 注意 : 构造函数必须以init开头;构造函数不需要写返回值;在构造函数中必须对每一个属性进行初始化
struct Registration {
var userName : String
var userAge : Int
var ID_card : String
var email : String
// 扩充构造方法
init(_ userName : String = "用户名", _ userAge : Int = 18, _ ID_card : String = "666", _ email : String = "123.qq.com") {
self.userName = userName
self.userAge = userAge
self.ID_card = ID_card
self.email = email
}
}
let register = Registration("香格里拉", 20)
print(register)
Swift中的类
Swift也是一门面向对象的语言,若想实例化对象必须要用到类,在Swift当中我们用关键词class来定义一个类。
Swift中类的定义
import UIKit
// 定义一个简单的Person类,Prson类可以不继承任何类,主要看是否需要用到父类的属性和方法。
// 如果用不到父类的属性和方法,那就可以不继承,使得当前类更轻量级。
// 系统会自动给类扩展一个构造方法,但是并不会自动初始化类中的属性。
// 所以对于类中的属性 1.可以定义时进行初始化、2.定义成可选类型、3.在init构造方法中进行手动初始化。
// 个人经验 : 如果属性是对象类型(UIView、NSUrl等)写成可选类型;如果是值类型(Int、 String等)直接定义时进行初始化。
class Person : NSObject {
var name : String
var age : Int
// 重写构造方法 在方法前面加上 override
override init() {
name = "陈冠希"
age = 18
}
}
// 创建Person对象
let p = Person()
print("姓名:\(p.name) 年龄:\(p.age)")
Swift中多种属性
在Swift中类的属性大体分为三种:存储属性、计算属性、类属性。
import UIKit
class Person : NSObject {
// 存储属性 : 用来存储实例的常量和变量
var name : String = "张柏芝"
var age : Int = 0
var mathScore : Double = 59.9
var englishScore : Double = 70.0
// 计算属性 不同点是赋值的是一个大括号 大括号内可以有计算属性的get和set方法
// 一般不会用到set方法 若只有返回值(get方法or只读属性) 可以省略set和get方法 直接return
// 计算属性完整写法
var averageScore : Double {
set {
}
get {
return (mathScore + englishScore) * 0.5
}
}
// 计算属性省略写法
var printInfo : String {
return "姓名:\(name) 年龄:\(age) 数学成绩:\(mathScore) 英语成绩:\(englishScore)"
}
// 类属性 是和整个类相关的属性 并且使用类名进行访问
static var courseCount : Int = 2
}
// 创建人类的对象
let p = Person()
// 访问存储属性
print(p.name)
// 访问计算属性
print(p.averageScore)
// 访问类属性
print(Person.courseCount)
Swift中的属性监听器
import UIKit
// 在Swift中对类中属性的监听操作可以用属性监听器 在OC当中一般利用属性的get方法进行监听
class Person : NSObject {
var name : String = "" {
// 属性即将发生改变
// willSet {
// print("属性即将发生改变 旧值:\(name) 新值:\(newValue)")
// }
willSet(newName) { // 在即将发生变化监听器中 可将系统提供的 newValue 起别名
print("属性即将发生改变 旧值:\(name) 新值:\(newName)")
}
// 属性发生改变
// didSet {
// print("属性发生改变 旧值:\(oldValue) 新值:\(name)")
// }
didSet(oldName) { // 在发生变化监听器中 可将系统提供的 oldValue 起别名
print("属性发生改变 旧值:\(oldName) 新值:\(name)")
}
}
}
// 创建人类对象
let p = Person()
// 属性发生变化 触发属性监听器
p.name = "小布什"
p.name = "奥巴马"
Swift中的构造函数
import UIKit
// 定义人类
class Person : NSObject {
var name : String = ""
var age : Int = 0
var height : Double = 0.0
// 被覆盖的自带构造函数 可手动再扩展一个
// override init() {
//
// }
// 自定义构造函数(一旦自定义构造函数,编辑器自动给类扩展的构造函数将会被覆盖)
// 构造函数中,要用到属性时,可以省略self. 但是若出现命名冲突时,必须加上self.
init(name : String, age : Int, height : Double) {
self.name = name
self.age = age
self.height = height
}
// 复杂版的字典转模型构造方法 (但是灵活性不好)
// init(dict : [String : Any]) {
//
// if let tmpName = dict["name"] as? String {
// self.name = tmpName
// } else {
// self.name = ""
// }
//
// if let tmpAge = dict["age"] as? Int {
// self.age = tmpAge
// } else {
// self.age = 0
// }
//
// if let tmpHeight = dict["height"] as? Double {
// self.height = tmpHeight
// } else {
// self.height = 0.0
// }
//
// }
// 利用KVC方式字典转模型赋值 (KVC属于NSObject的方法,所以必须继承自NSObject才能使用)
init(dict : [String : Any]) {
super.init() // 在调用KVC方法前 调用super的init方法
setValuesForKeys(dict)
}
// 避免KVC找不到对应的key值报错 空实现系统以下方法
override func setValue(_ value: Any?, forUndefinedKey key: String) {}
}
// 创建人类对象
//let p = Person(name: "陈冠希", age: 18, height: 1.88)
//print(p.name, p.age, p.height)
// 通过字典给Person对象赋值
let dict : [String : Any] = ["name" : "谢霆锋", "age" : 19, "height" : 1.88]
let p2 = Person(dict: dict)
print(p2.name, p2.age, p2.height)
Swift中的析构函数
Swift中的析构函数其实就是OC中的dealloc方法,在ARC下不可手动调用。Swift通过自动引用计数来处理实例对象的内存管理,当一个实例对象的引用计数为0时,系统会自动调用该对象的析构函数。通常在西沟函数中,会释放一些资源,如监听通知的对象等。
mport UIKit
class Person : NSObject {
deinit {
print("人被释放了")
}
}
var p : Person? = Person()
p = nil // 当一个实例对象被释放时,系统自动调用该对象的析构函数
Swift中的循环引用问题
在Swift中系统也是通过实例对象的引用计数来管理内存的,当一个对象被强引用时,引用计数+1,当强引用消失时,引用计数-1;在实例对象的引用计数为0时,对象被释放。但是在ARC下也有可能发生内存泄漏问题,比如出现循环引用。
import UIKit
// 创建Person类
class Person : NSObject {
var name : String = ""
// var book : Book?
weak var book : Book? // 改为若引用
deinit {
print("人已经被销毁")
}
}
// 创建Book类
class Book : NSObject {
var name : String = ""
var owner : Person?
deinit {
print("书已经被销毁")
}
}
// 创建书对象 & 人对象
var book : Book? = Book()
var p : Person? = Person()
p?.book = book
book?.owner = p
// 当我们把实例对象的强应用去掉后 发现被没有调用两个对象的析构函数 证明此时两个对象都未被释放
// 解决方案一 : 在其中一个对象中将引用另一个对象的属性前面添加 weak 变为若引用
// 解决方案二 : 使用 unowned 关键词修饰也可以 但是不安全 基本不用
p = nil
book = nil
Swift中的可选链
import UIKit
// 可选链 : 多个可选类型组成的链条叫做可选链
// ?.就是可选链,系统会对可选链中的每个环节进行自动判断,当可选类型有值就进行解包,
// 当可选类型为空时就赋值为nil,所以对于整个可选链语句的返回值不是nil就是一个可选类型。
// 运用可选连当其中的某个环节为nil时,整个可选链条就会失效
class Person : NSObject {
var name : String = ""
var dog : Dog?
}
class Dog : NSObject {
var name : String = ""
var toy : Toy?
}
class Toy : NSObject {
var name : String = ""
var price : Double = 0
func flying() {
print("飞盘在非")
}
}
// 实例化对象
let person = Person()
let dog = Dog()
let toy = Toy()
dog.toy = toy
person.dog = dog
// 1.取值
// 1.1危险方式(强制解包)
print(person.dog!.toy!.price)
// 1.2复杂层层可选绑定方式
if let d = person.dog {
if let t = d.toy {
print(t.price)
}
}
// 1.3可选链方式取值
if let price = person.dog?.toy?.price {
print(price);
}
// 2.通过可选链赋值 (强制解包和层层绑定方式大体如上)
person.dog?.toy?.price = 9.99
if let price = person.dog?.toy?.price {
print(price);
}
// 3.通过可选链调用函数 (强制解包和层层绑定方式大体如上)
person.dog?.toy?.flying()
Swift中协议的基本使用
import UIKit
// 1. 定义一个协议
protocol TestProtocol {
// 协议中提供的函数声明
func eat()
func sleep()
}
// 1.1 如果一个类不继承自任何其他类 那么在类名后面加上 : 协议名称 来遵守协议
class person : TestProtocol {
// 实现协议中的方法
func eat() {
print("吃饭")
}
func sleep() {
print("睡觉")
}
}
// 1.2 如果一个类有父类 就在继承的父类后面加上 ,协议名称 来遵守协议
class Student : NSObject, TestProtocol {
// 实现协议中的方法
func eat() {
print("吃饭")
}
func sleep() {
print("睡觉")
}
}
// 2. 协议在代理模式中的使用 协议后面加上 :class 规定此协议只能被类遵守(避免被枚举、结构体等遵守与类中代理属性weak修饰词冲突)
protocol DriverProtocol : class {
func drivering()
}
class Boss : NSObject {
// 定义代理属性 用weak修饰 防止循环引用
weak var driver : DriverProtocol?
func goHome() {
driver?.drivering() // 让遵守协议的人开车送老板回家
}
}
class Driver : NSObject, DriverProtocol {
func drivering() {
print("开车")
}
}
let boss = Boss()
let driver = Driver()
boss.driver = driver
boss.goHome() // 开车
// 3. 将协议中的方法设置成 optional 类型
// 可以使用 optional 关键字修饰协议中可选择实现的方法,但是optional是OC中的特性,所以必须在协议的前面
// 加上@objc(Swift2.0)、在方法的 optional 关键字前面也加上 @objc(Swift3.0)
@objc protocol Test2Protocol : class {
func RequiredFunc() // 必须实现的方法
@objc optional func optionalFunc() // 可选择实现的电方法
}
class Teacher : NSObject, Test2Protocol {
func RequiredFunc() {
print("必须实现的方法")
}
}