- 苹果宣称Swift的特点是: 快速,现代,安全,互动 , 而且明显优于OC
- 界面可以使用
Cocoa
和Cocoa Touch
框架
- Swift中取消了预编译指令包括宏
- Swift取消了Objective-C的指针及其他不安全访问的使用
- 舍弃OC早期应用的Smalltalk的语法"
[]
",全面改为句点表示法
- 提供了类似Java的命名空间
(namespace)
, 泛型(gegeric)
, 运算对象重载(operator overloading)
- 在Swift中, 可以省略分号
;
, 使用换行代替,也可以添加. 但是有一种情况必须加分号:同一行代码上有多条语句时.
- 去除了
NS
前缀
- 将绝大部分
class
转换为struct
- https://swift.org
- https://developer.apple.com/swift/blog/
命名
- 标识符可以使用中文名称或表情, 因为Swift采用Unicode编码.
- 注意点:
不能包含数学符号, 箭头, 非法无效的Unicode字符(⚽️), 关键字;
不能以数字开头, 不能是单独一个下划线_; .......
- 采用"驼峰式"的命名规则.
注释
/* 外层注释
/* 内层注释
内层注释 */
外层注释 */
变量与常量
- 常量使用let关键字, 变量使用var关键字.
- Swift严格要求变量在使用之前必须进行初始化
var str = "Hello, playground"
let girl = " 少女"
//girl = "元气少女" // 报错, 常量重定义
var num = 1
//num = "哈哈" // 报错, num已经指定为Int类型, 无法重新设置为String类型.
var good:Int = 2 // 显示指定为Int类型, 编译提供联想类型功能.
var myTableView = UITableView()
public let titleArray = ["基本", "嵌套", "动画"]
类型推测
- 一般来说,没有必要明确指定变量\常量的类型.
- Swift自动推测所声明变量或常量的类型, 是强类型语言
- 不可以中途更换类型, 会在编译期检查类型, 也可以显示的指明变量的类型.(ps.赋值后再编译)
- typealias关键字定义类型的别名,相当于C语言的typedef
var age: Int = 2; // 显示指定为Int类型, 编译提供联想类型功能.
输出常量与变量
- 使用print( )在控制台输出数据, 使用
\( )
输出变量.
print(", 恭喜恭喜") // 在控制台输出数据
var name = "Sara"
print("Who is the girl? \(name)")
字符串的简单操作
let str1 = "Hello, playground"
let str2 = "123"
var str = str1 + str2
- 用反斜线 \ 和小括号 ( ) 做字符串插值 (把常量\变量插入到字符串当中)
var age = 10
var hand = 2
// 两种方式转为字符串
var dest = String(age)
var stt = "\(hand)"
// UTF8的编码(0~4个) 每个汉字是3个字节
print(st.lengthOfBytes(using: .utf8))
- 用characters.count计算字符串的长度
var str = "Hello, playground"
print(str.characters.count) // 17\n
let str = "Hello, playground"
let str1 = "hello"
if str == str1 {
print("字符串相等")
}
if str.isEmpty {
print("字符串为空")
}
基本数据类型
- 类型: 整型(Int), 浮点型(Float,Double), Bool型, 元组(Tuple), 可选型(Optional), Array, Dictionary, Character
- 在Swift中数据类型的首字母都是大写.
- 如果数值超出了取值范围,编译器直接报错
数字格式
// 可以增加额外的零
let money = 001234 // 1234
// 可以增加额外的下划线_
let money = 1_000_000 // 1000000
运算符
a..
// 按顺序从范围中取值赋值给index,每取一次值,就执行一次循环体
for index in 1...5{
}
// 遍历字符串
for character in "abcde".characters{
print(character)
}
// 如果不需要用到范围内的值,可以使用下划线_进行忽略
for _ in 1...5{}
}
- 溢出运算符: &+ , &-, &* , &/ ,&%
var x = UInt8.max; // 255
var y = x &+ 1; // 0
注意:
1.swift赋值运算符没有返回值,好处是防止误用=和==
if (x = y){} // 错误,因为x=y 没有返回具体的值, 相比C语言非0即为真
2.swiftd %支持浮点数的计算
8%2.5 // 0.5
整型
- 在Swift中, 提供Int与UInt两种类型, 后面携带数字表示整数位数, 如Int8, 默认使用Int即可, 在64位是Int64, 32位是Int32.
- 提供max和min属性来获取某个类型的最大值和最小值.
- 在定义变量时不用在意有无符号,数据长度的问题; 尽量使用Int,在意可以考虑代码的简洁和可复用性.
var max = Int.max
var min = Int.min
var maxu = UInt.max
var minu = UInt.min
1. 十进制数:没有前缀
let i1 = 10 //10
2. 二进制数:以0b为前缀
let i2 = 0b1010 //10
3. 八进制数:以0o为前缀
let i3 = 0o12 //10
4. 十六进制数:以0x为前缀
let i4 = 0xA //10
浮点型
- 常见的Float是32位浮点数, Double是64位浮点数.
- 精确度: Float:至少6位小数; Double:至少15位小数
- 如果没有明确说明类型,浮点数默认就是Double类型
let score: Float = 99.89
let score = 99.89f //错误,swift没有此写法
1.十进制(没有前缀)
没有指数: let d1 = 12.5
有指数: let d2 = 0.123e5
2.十六进制(一定要有指数)
let d3 = 0xC.8p0 // 12.5 p=2的N次方
Bool型
- 两个常量, true或者false, 不再接收数字类型转型.
var isGood = true
if (isGood) { //== if(true)
print("好的!")
}
//isGood = 0; // 错误,不接受数字类型转型
元组Tuple
- 元组把多个不同类型组成一个类型, 使用括号( )定义, 使用逗号,分隔. 使用元组定义确定形式的信息.
var student = ("张三", 18, "山西") // 定义类型
let name = student.0;
let age = student.1;
let home = student.2;
- 也可以给各个成员变量命名, 使用key:value的形式.
var student = (name:"张三", age:18, home:"山西")
let sname = student.name;
let sage = student.age;
let shome = student.home;
- 元组之间可以相互转换, 使用下划线_表示省略的属性.
var (showName, showAge, _) = student
print("我叫\(showName), 今年\(showAge)岁")
可选项Optional
定义使用 `?` 解包使用 `!` ,准备计算
变量可选项的默认值是 nil
常量可选项没有默认值,主要用于在构造函数中给常量设置初始数值
- 如下情况可以使用可选类型
- 它有值但不确定
- 它没有任何值 nil,不是任何数据类型,不能参与运算
- 在定义的常规类型后面添加?, 即表示可选型, 如
age:Int?
, 如果有值, 则返回常规类型, 否则, 返回nil(空值).
var ss: Int? = nil // 空值
var myAge:Int?
myAge = Int("Spike") //ps. 在Swift2.0中, 遗弃toInt( )方法, 使用Int( )转换.
- 在Swift中, 所有类都会被初始化, 即使没有赋值的可选型, 会默认初值nil.
- 在处理可选型时, 要注意解包, 即获取内部的值, 未解包时, 会显示Optional( ).
myAge = Int("15")
print("我今年\(myAge)岁") //输出:我今年Optional(15)岁
- 解包操作, 在确定变量含有正确值时, 使用叹号
!
进行解包, 对空值(nil)解包时, 会造成崩溃
一定需要注意!
正常解包前需要判空.*
myAge = Int("")
if (myAge != nil) {
print("我今年\(myAge!)岁")
}
流程控制
1.条件不需要()
2.语句必须有{}
3.()表示空执行
#### switch
1.可以对任意类型的值进行分支,不再局限在整数!
2.不需要 break,执行完case对应的代码后默认会自动退出switch语句;
3.所有分支至少需要一条指令语句
4.如果 case 多值,使用`,`
- case的多条件匹配
let score = 90
switch score/10 {
case 10,9:
print("优秀")
default:
break
}
- case的范围匹配
switch score {
case 90...100:
print("优秀")
default:
break
}
- case匹配元组
let student = ("小明", 18)
switch student {
case ("小红", 18):
print("是小红")
case (_, 18):
print("是18岁")
case (_, 16...19):
print("aaa")
default:
break
}
- fallthrough
执行完当前case后,会接着执行fallthrough后面的case或者default语句; 但是fallthrough后面的case条件不能定义常量和变量
#### 标签
可以用于明确指定退出那个循环
biaoqian:
循环
OC与Swift语法比较
/**
// 1.@UIApplicationMain 程序的入口 (没有main函数)
// 2.只有.swift 没有.h/.m 在Swift中默认全局共享
// 3.所有的代码,都包装在{}(函数) 默认方法都有一个缩进!
1. OC [[UIView alloc] initWithXXX:]
Swift UIView(XXX:)
UIView()
类名() == alloc / init 等价
2. 类方法
OC [UIColor redColor]
Swift UIColor.red
2.0 UIColor.redColor()
3. 访问当前对象的属性,可以不使用`self`
建议: 都不用,在编译器提示的时候再添加,会对`语境`有更好的体会
原因: 闭包(类似于OC-block),需要使用 self
4. 没有分号`;`
`;`目的是分隔语句,在Swift中默认不需要, 除非一行有多条语句,需要`;`间隔
5. 枚举类型
OC UIButtonTypeContactAdd
Swift `.`contactAdd
let btn = UIButton(type: .contactAdd)
6. 监听方法
OC @selector
Swift #selector 如果带参数,不需要使用`:`
2.0 直接使用`""`,需要使用`:`
btn.addTarget(self, action: #selector(clickme), for: .touchUpInside)
func clickme(btn: UIButton) -> () {
print(#function)
print("测试按钮")
print(btn)
}
7. 调试
OC NSLog
Swift print
- 没有时间
- 效率高于NSLog
OC __FUNCTION__
Swift #function
8. 注释
从 Xcode8.0 开始,目前所有的插件都无法工作
`///` option + cmd + / 增加文档注释
`vi.backgroundColor = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)`
->color + 回车 添加颜色块
// MARK: 视图生命周期
// TODO: 还需要做的事情
// FIXME: 需要完善修改的地方
9. 自动推导 变量\常量的类型会根据右侧的代码执行结果,推导对应的类型 `option + click`
强类型 不可以中途更换类型, 会在编译期检查类型, 也可以显示的指明变量的类型.(ps.赋值后再编译)
不存在基本数据类型 都是结构体
// 类型转换 as
Swift 中除 String 外,绝大多数使用as 需要?/!
as?/as! 直接根据前面的返回值来决定
注意: if let / guard let 判空语句,一律使用 as?
10. ??
是一个简单的三目 , 解决可选项解包时的代码"尴尬"
对于Swift项目中对Optional Value的安全有效处理;
- 如果有值,使用值
- 如果没有值,使用??后面的值替代
let str: String? = "hello"
print((str ?? "") + "world!") //helloworld!
print(str ?? "" + "world!") //hello
// 可见: ??操作符的优先级较`低`,在使用的时候最好用()括起来
11. if let/var 连用语法,目的是判断值(在赋值的时候判断值,不是单纯的 if,不需要解包)->作用域{}
guard let 和 if let 刚好相反,降低分支层次 ,守护一定有值,如果没有直接返回 ->作用域不止{}
// 使用同名的变量接收值,在后续使用的都是非空值,不需要解包
guard let name = name else {}
if let name = name {}
12. 传统 for 已被取消(3.0后)
++i i++ 已被取消(3.0后), 直接使用 i += 1
// 反序遍历
for i in (0...9).reversed() {
print(i)
}
13. 字符串
String 是一个结构体,性能更高; NSString 是一个 OC 对象,性能略差
- 目前具有了绝大多数 NSString 的功能
- 支持直接遍历! `for c in "hello".characters { }`
- as 类似于 OC 类型转换(NSString *)
// 使用`值 as 类型`类型转换
let ocStr = st as NSString
print(ocStr.length)
- 字符串截取
var st = "hello"
st = st.substring(from: "he".endIndex)
st = st.substring(with: st.range(of: "ll")!)
// 或
guard let range = st.range(of: "ll") else {
print("没有找到字符串")
return
}
print(st.substring(with: range))
14. 数组
func arrayDemo() {
// 初始化
var array = [String]()
let arr = ["aaa", "bbb", "ccc"] //[String]
let arr2 = [1,2,3,4,5] // [Int]
let p = CGPoint(x: 10, y: 100)// CG结构体
let arr3 = [p] //[CGPoint]
// 混合数组: 开发中几乎不用,因为数组是靠下标索引
// 如果数组中数据类型不一致,自定推导的结果`[NSObject]`
// 还有一种类型`[AnyObject]`-> 任意对象(Swift 中一个类可以没有任何`父类`)
// 在混合数组中, CG结构体需要包装 `NSValue()`,整个数组还要加`as [Any]`
let arr4 = ["张", 1, NSValue(cgPoint: p)] as [Any] //[Any]
// 按照下标遍历
for i in 0..元组
for e in arr.enumerated() {
print("\(e.offset) \(e.element)")
}
for (n, s) in arr.enumerated() {
print("下标:\(n) 元素:\(s)")
}
// 反序遍历
for (n, s) in arr.enumerated().reversed() {
print("下标:\(n) 元素:\(s)")
}
// 数组 增/删/改
array.append("bread") // apend函数追加元素
array += ["cheese"] // `+=` 操作符合并数组(要合并的两个数组类型必须一致)
array.insert("coffee", at: 0) // 数组插入元素
array.remove(at: 0) // 数组删除元素
array.removeAll()
arr.removeAll(keepingCapacity: true) // 并且保留空间
arr[0] = "q" // 修改,通过下标定位
}
15. 字典
// 字典初始化
var dict = ["a":"aa", "b":"bb"]
var dict: Dictionary = ["a":"aa", "b":"bb"]
var dict: Dictionary = ["a":"aa", "b":"bb"]
// 字典 增/删/改
dict["c"] = "cc" // 追加键值对
dict["a"] = "a" // 修改
dict.removeValue(forKey: "c") // 删除(直接给定 KEY)
**`hash哈希`就是将字符串变成唯一的`整数`,便于查找,提高字典遍历的速度.而 KEY 便是hash MD5一种**
let vv = dict.removeValue(forKey: "c") // "cc"
print(vv!) // "cc\n"-- 解包
// 字典遍历
for item in dict { //dict.count)= 字典元素个数
print(item)
}
for (key, value) in dict {
print("\(key):\(value) ")
}
let keyArr = Array(dict.keys)
let valueArr = Array(dict.values)
// 字典合并(遍历)--不能像数组一样直接相加
for (key, value) in dict1 {
dict[key] = dict[value]
}
16. 函数
/// 定义格式
func 函数名(形参列表) -> 返回值类型{
... // 函数体
}
/// 没有返回值的函数(+无参) --> 主要用于闭包
func printStar() ->Void {} // Void
func printStar() ->() {} // ()
func printStar() {} // 直接省略
/// 常量参数和变量参数
1.默认都是`常量参数`,不能在函数内部修改.不同于C语言
func test(num: Int) 相当于 func test(let num: Int)
2.`变量参数`:需要在函数内部修改参数的值 (var)
func test(var num: Int){ num = 10}
/// 返回元组类型.
func find(id: Int) -> (name: String ,age: Int){
return ("小明", 20)
}
var person = find(1)
print("name=\(person.name), age=\(person.age)")
/// 有可变数量的参数,把参数看成一个数组
func sumOf(numbers:Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf(42,597,12)
/// 外部参数名
1.写在形式参数名的前面,用空格隔开
func 函数名(外部参数名 形式参数名: 形式参数类型) -> 返回值类型{
// 函数体
}
func find(stu_id id: Int) -> (name: String ,age: Int){
return ("小明", 20)
}
find(stu_id: 1)
2.用`#` 简化外部参数名的定义(外部参数名与参数名相同)
func find(#id: Int) -> (name: String ,age: Int){
return ("小明", 20)
}
find(id: 1)
/// 默认参数值
1.带有默认参数值的形参,Swift会自动给它生成一个跟形参名相同的外部参数名
2.把带有默认参数名的参数名前面加个下划线`_`,调用函数时就不用写外部参数名
func addStudent (#name: String , age: Int = 20){ }
addStudent (name: "小明")
addStudent (name: "小红", age: 18)
/// 输入输出函数
1.在C语言中,利用`指针`可以在函数内部修改外部变量的值
void change (int * a){
*a = 20;
}
int num = 10;
change(&num);
2.在Swift中,利用`输入输出函数` ,也可以在函数内部改变`外部变量`的值
--> 在参数名前面加个`inout`关键字即可
func swap (inout num1: Int , inout num2: Int){
var temp = num1;
num1 = num2;
num2 = temp;
}
var a = 10
var b = 20
swap(&a, &b) // 传入的参数前面必须加 &
/// 嵌入函数 ( 函数嵌套,相当于函数指针)
函数可以被嵌套。里面的嵌套函数可以访问外面函数的变量。
func chooseStepFunction(backwards: Bool) -> ((Int) -> Int){
func stepForward(input: Int) -> Int{
return input + 1
}
func stepBackward(input: Int) -> Int{
return input - 1
}
return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backwards: currentValue > 0)
while currentValue != 0 {
print(currentValue)
currentValue = moveNearerToZero(currentValue)
}
/// 函数是一个引用类型,就是说一个函数可以返回另一个函数作为返回值。
func makeIncrementer() -> ( (Int)->Int ) {
func addOne(number:Int) ->Int {
return 1+number
}
return addOne
}
var increment = makeIncrementer() // 返回的是addOne函数
increment(7)
/// 一个函数也可以用其他函数作为参数。
func hasAnyMatches(list: [Int],condition:(Int)->Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number:Int) -> Bool {
return number < 10
}
var number = [20, 19, 7, 12]
hasAnyMatches(list: number,condition:lessThanTen)
17. 闭包
函数实际上是一种特殊的闭包,它是一段能之后被调取的代码。
闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的。
你可以使用{ }来创建一个匿名闭包。使用in将参数和返回值类型声明与闭包函数体进行分离。”
- 类似于OC 的 block,但比block 应用面更广
在 OC中 block 是匿名的函数
在 Swift 中函数是特别的闭包
- 应用场景
异步执行完成回调
控制器间回调
自定义视图回调
- 回调特点
以参数回调处理结果
返回值为 Void
func closure() {
// 1> 最简单的闭包
// () -> () 没有参数和返回值的函数
// 省略`() -> () in`
let b1 = {
print("hello")
}
// 执行闭包
b1()
// 2> 带参数的闭包
// 闭包中,参数和返回值,实现代码写在{}里
// 需要使用一个关键字`in`分隔定义和实现
// {形参列表 -> 返回值 // 实现代码}
let b2 = { (x: Int) -> () in
print(x)
}
b2(100)
// 3> 带参数/返回值的闭包
let b3 = { (x: Int) ->Int in
return x + 10
}
print(b3(100))
}
18. GCD
/**
在异步执行任务,获取结果,通过 block/闭包 回调
闭包的应用场景与 block 完全一致
*/
func loadData(completion: @escaping (_ result: [String])->()) -> (){
// 将任务添加到队列,指定执行任务的函数
// 队列调度任务(block/闭包),以 同步/异步 的方式进行
DispatchQueue.global().async {
print("耗时操作\(Thread.current)") //耗时操作{number = 3, name = (null)}
// 休眠
Thread.sleep(forTimeInterval: 1.0)
let json = ["aaa", "bbb"]
// 主队列回调
DispatchQueue.main.async {
print("主线程更新 UI \(Thread.current)") //主线程更新 UI {number = 1, name = main}
// 回调 -> 执行闭包(通过参数传递的)
// 传递异步获取的结果
completion(json)
}
}
}
// `尾`随闭包
// 如果函数的`最后一个参数`是闭包,函数参数可以提前结束,最后一个参数直接使用{}包装闭包的代码
loadData(completion: {(result)->() in
//原本的样式
})
loadData { (result) in
print("获取的数据\(result)")
}
19.循环引用
`循环引用` 单方向对引用是不会产生循环的
- 闭包对 self 进行了 copy,闭包执行完成后会自动销毁,同时释放对 self 的引用
- 同时 self 对闭包引用
1> 哪种情况下会遇到循环引用:
- 闭包中使用 self. 对当前对象强引用
- 控制器(可以接收闭包的任意对象)以属性记录闭包(强引用)
2> 三种解除闭包循环引用的方式
(1) OC 的方式: weak var weakSelf = self
- var, weak 只能修饰 var,不能修饰let
weak可能会在运行时被修改 -> 只指向的对象一旦被释放,会被设置为 nil
- ? 可选解包 - 如果self 已经被释放,不会向对象发送 getter 的消息,更安全
- ! 强行解包 - 如果self 已经被释放,强行解包会导致崩溃 (需要计算的时候需要强行解包,因为可选项不能参与计算)
(2) Swift 推荐: [weak self]
loadData2 {[weak self] in
print(self?.view as Any)
}
- 表示闭包中的 self 是弱引用,如果 self 被释放,会自动设置为 nil
- 与 OC 的__ weak 等效
(3) [unowned self]
loadData2 {[unowned self] in
print(self.view)
}
- 表示闭包中的 self 是 assign 的,不会强引用.但是,如果 self 被释放,指针地址保持不变,如果继续调用,会出现野指针的错误
- 与 OC 的__ UN萨芬) unretained 等效
20.命名空间 & 反射机制
- 命名空间
// 从 info.plist 中加载命名空间的名称
let name = Bundle.main().infoDictionary?["CFBundleName"] as? String ?? ""
在Swift 中,默认同一个项目中(同一个命名空间下),所有的类都是共享的,可以直接访问,不需要 import
所有对象的属性 var, 也可以直接访问到
ps.第三方框架使用 Swift 如果直接拖拽到项目中,从属于同一个命名空间,很有可能冲突!----以后尽量都用 cocoaPod
- 反射机制-----目的:为了解耦!
对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性
这种动态获取的信息以及动态调用对象的方法的功能称之为 Java 语言的反射机制
eg. NSClassFromString、isMemberOfClass、isKindOfClass、conformToProtocol、respondsToSelecter、performSelect或 objc_msgSend间接调用
// AnyClass? -> 视图控制器的类型
// 添加命名空间
let cls = NSClassFromString("MaiXiaoJian_Swift.TabBarController") as? UITabBarController.Type
let vc = cls?.init()
window?.rootViewController = vc
21.面向对象
1> 构造函数基础(重写/重载)
构造函数 (子类的构造过程与 OC 过程相反:子类 init-> 父类init)
1. 构造函数的目的: 给自己的属性分配空间并设置初始值
2. 调用父类构造函数之前,需要给本类的属性设置初始值
3. 调用父类的`构造函数`,给父类的属性分配空间设置初始值
NSObject 没有属性,只有一个成员变量`isa`
4. 如果重载了构造函数,并且没有实现父类 init 方法,系统不再提供 init() 构造函数(默认是有的)
因为:默认的构造函数不能给本类的属性分配空间
* 不用写 func *
* override重写(-父类存在相同的方法 -子类重新编写父类方法的实现) *
* overload重载(-函数名相同 -参数类型和个数不同) *
2> KVC构造函数
1. 定义模型属性的时候,如果是对象,通常都是可选的 var name: String?
- 在需要的时候创建(延迟加载)
- 避免写构造函数,可以简化代码
2. 如果是基本数据类型,不可设置成可选的,而且要设置初始值,否则 KVC 会崩溃,运行时同样获取不到 var age: Int = 0
3. 如果需要使用 KVC 设置数值,属性不可能是 private 的
4. 使用 KVC 方法之前,应该调用 super.init 保证对象实例化完成!
// 重载构造函数,使用字典为本类设置初始值
init(dict: [String: AnyObject]) {
super.init() // 使用 self 的方法`setValuesForKeys`之前,应该调用 super.init
setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forUndefinedKey key: String) {
// 防止崩溃
}
3> 便利构造函数
。。。。。。
- 默认情况下
4> 析构函数
。。。。。。
22.运行时
使用`运行时`获取`当前类`所有属性的数组
/// 获取 ivar 列表是所有第三方框架字典转模型的基础!
class func propertyList() -> [String] {
var count: UInt32 = 0
let list = class_copyPropertyList(self, &count)
print("属性的数量\(count)")
// 使用 guard 语法,一次判断每一项是否有值,只要有一项为 nil,就不再执行后面的代码
for i in 0..只实现 getter 方法
//本身不保存内容,都是通过计算获得结果
//类似于一个函数/闭包(没有参数,一定有返回值)
在 Swift 中只重写 getter 方法,不重写 setter 方法
var no: String? {
get {
return _no //简写: 省略 get {} 直接 return
}
}
- `计算型`属性:执行函数返回其他内存地址(属性本身不占用内存空间;不可以给其设置数值)
`存储型`属性:需要开辟空间,以数据存储
- 计算型属性 & 懒加载属性 对比
* 计算型属性
/**
不分配独立的存储空间保存计算结果
每次调用时都会被执行
更像一个函数,不过不能接收参数,同时必须有返回值
var title: String {
return "Mr"+(name ?? " ")
}
// MARK:计算型属性返回
var namespace1: String {
return infoDictionary?["CFBundleName"] as? String ?? ""
}
*/
* 懒加载属性
/**
在第一次调用时,执行闭包并且分配空间存储闭包返回的数值
会分配独立的存储空间
与 OC 不同的是,lazy 属性即使被设置为 nil也不会被再次调用
lazy var title:String {
return "lazy"+(self.name ?? " ")
}
*/
25.重写 setter 方法 :`didSet`
var person: Person? {
didSet {
text = person?.name
}
}
26.MVVM(引用规则:View|Controller → ViewModel → Model,反向不行)
- view 和 viewController 正式联系在一起,我们把它视作一个组件
- view 和 viewController 都不能直接引用 model,而是引用viewModel
- viewModel 是一个封装业务逻辑处理、视图显示逻辑、网络处理、数据缓存
三方库:
pod 'SnapKit', '~> 0.20.0' ## 自动布局
pod 'Alamofire', '~> 3.3.1' ## 网络请求, swift版的AFN
pod 'Kingfisher', '~> 2.3.1' ## 轻量级的SDWebImage