学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

Swift语法基础

变量

let定义常量 不可修改

let代表不可变对象,即对象首地址不能发生变更,也就是说内存地址不能改变,但也可以修饰可变对象

1
2
3
4
let a: String = "sb"
a = "sbc"    // 会报错误 ,因为let修饰的变量不能改变指针指向
let array: NSMutableArray = NSMutableArray()
array.add("123"//不会报错,因为数组a 的指针没有重新指向

var 定义变量

1
2
var b: String = "sb"
b = "sbc" // 不会报错,因为var修饰的变量指针可以重新指向

 let 和 var 的区别就是在于对象内存地址可不可以改变

函数

1
2
3
4
5
6
7
8
func funcname(形参) -> returntype
{
Statement1
Statement2
……
Statement N
return parameters
}

eg:

1
2
3
4
func runoob(site: String) -> String {
return (site)
}
print(runoob(site: "www.runoob.com"))

打印常量和变量
print(_:separator:terminator:)

控制流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// if
let s = "hi"
if s.isEmpty {
print("String is Empty")
} else {
print("String is \(s)")
}

// if let-else
func f(s: String?) {
if let s1 = s {
print("s1 is \(s1)")
} else {
print("s1 is nothing")
}
// nil-coalescing
let s2 = s ?? "nothing"
print("s2 is \(s2)")
}
f(s: "something")
f(s: nil)

if guard, guard let

guard let is designed to exit the current function, loop, or condition if the check fails, so any values you unwrap using it will stay around after the check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func getMeaningOfLife() -> Int? {
42
}

func printMeaningOfLife() {
if let name = getMeaningOfLife() {
print(name)//返回为int时才会执行
}
}

//That uses if let, so that the result of getMeaningOfLife() will only be printed if it returned an integer rather than nil.

//guard let
func printMeaningOfLife() {
guard let name = getMeaningOfLife() else {
return
}

print(name)
}

use if let if you just want to unwrap some optionals, but prefer guard let if you’re specifically checking that conditions are correct before continuing

更好地处理异常情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// guard
func f1(p: String) -> String {
guard p.isEmpty != true else {
return "Empty string."
}
return "String \(p) is not empty."
}
print(f1(p: "")) // Empty string.
print(f1(p: "lemon")) // String lemon is not empty.
// guard let
func f2(p1: String?) -> String {
guard let p2 = p1 else {
return "Nil."
}
return "String \(p2) is not nil."
}
print(f2(p1: nil)) // Nil.
print(f2(p1: "lemon")) // String lemon is not nil.

可选类型

可能会是 nil 的变量就是可选变量。当变量为 nil 通过??操作符可以提供一个默认值

1
2
var o: Int? = nil
let i = o ?? 0

Optional类型的本质实际上就是一个带有泛型参数的enum类型,这个类型和Swift中的Result类有异曲同工之妙

eg:

var num: Int?

声明了一个Optional类型,它可能包含一个Int值,也可能什么都不包含

Int!或者Int?这种写法,只是一种Optional类型的糖语法写法

在OC中我们的对象都可以赋值为nil,而在Swift中,能赋值为nil只有Optional类型!

  • 解包

解包的基本思路,使用if let或者guard let,而非强制解包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
///强制解包
func getHeight(_ height: Float?) -> Float? {

if height != nil {

return height! / 100

}

return nil

}

///安全解包 1

func getHeight(_ height: Float?) -> Float? {

if let unwrapedHeight = height {

return unwrapedHeight / 100

}

return nil

}

///安全解包 2

func getHeight(_ height: Float?) -> Float? {

guard let unwrapedHeight = height else {

return nil

}

return unwrapedHeight / 100

}

更倾向于使用guard let

对于一个以返回结果为目的的函数,函数主体展示正常返回值,而将异常抛出在判断中,这样不仅逻辑更清晰,而且更加易于代码阅读。

guard let与if let不仅可以判断一个值的解包,而且可以进行连续操作

闭包

语法

1
2
3
{	(parameters) -> (return type)   in
statements
}

尾随闭包

如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,将这个闭包替换成为尾随闭包的形式很有用。尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

1
2
3
4
5
6
7
8
9
// fn 就是一个尾随闭包参数
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}

// 调用
exec(v1: 10, v2: 20) {
$0 + $1
}

扩展Extension

ObjC中有分类和扩展
Swift中只有扩展(更类似ObjC中的分类)
与ObjC不同的是,在Swift 的 extension 中 不可以直接添加属性。编译会报错。
和ObjC一样,我们可以用关联方法来添加属性。

swift中扩展(Extensions)

扩展就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能(functionality)。
这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。
扩展和 Objective-C 中的分类(categories)类似。(不过更加强大,而且与Objective-C 不同的是,Swift 的扩展没有名字。)

遵守代理或者数据源的时候使用Extension

Extension可以用于遵守代理与数据源使用,同时建议每遵守一个协议就另起一个分类,因为Extension的用途之一就是用来分隔不同的业务。

1
2
3
4
5
6
7
// MARK: - 网络请求处理模块
extension ViewController {
}

// MARK: - 按钮点击事件的处理模块
extension ViewController {
}

私有API与对外API使用Extension

Swift中只有一个.swift文件,不能一眼看出那些是私有API那些是对外API。需要看private 和 fileprivate关键字才行

通过Extension进行私有API与对外API的分离

1
2
3
4
5
6
7
8
9
10
11
12
13
// MARK: - 私有API
private extension ViewController {

}

fileprivate extension ViewController {

}

// MARK: - 对外API
extension ViewController {

}

或者在声明类class的大括号中全部写对外API函数,在Extension中写私有API函数

1
2
3
4
5
6
7
8
// MARK: - 对外API
class ViewController: UIViewController {
}

// MARK: - 私有API
extension ViewController {

}

Protocol与Extension配合使用,变相实现多继承

关键词:Open, Public, Internal, File-private, Private

Swift 有五个级别的访问控制权限,从高到底依次为比如 Open, Public, Internal, File-private, Private
遵循的基本原则是:高级别的变量不允许被定义为低级别变量的成员变量。比如一个 private 的 class 中不能含有 public 的 String。反之,低级别的变量却可以定义在高级别的变量中。比如 public 的 class 中可以含有 private 的 Int。

1.Open 具备最高的访问权限。其修饰的类和方法可以在任意 Module 中被访问和重写;它是 Swift 3 中新添加的访问权限。

2.Public 的权限仅次于 Open。与 Open 唯一的区别在于它修饰的对象可以在任意 Module 中被访问,但不能重写。

3.Internal 是默认的权限。它表示只能在当前定义的 Module 中访问和重写,它可以被一个 Module 中的多个文件访问,但不可以被其他的 Module 中被访问。

4.File-private 也是 Swift 3 新添加的权限。其被修饰的对象只能在当前文件中被使用。例如它可以被一个文件中的 class,extension,struct 共同使用。

5.Private 是最低的访问权限。它的对象只能在定义的作用域内使用。离开了这个作用域,即使是同一个文件中的其他作用域,也无法访问。

copy-on-write

值类型(比如:struct),在复制时,复制对象与原对象实际上在内存中指向同一个对象,当且仅当修改复制的对象时,才会在内存中创建一个新的对象,

为了提升性能,Struct, String、Array、Dictionary、Set采取了Copy On Write的技术
比如仅当有“写”操作时,才会真正执行拷贝操作
对于标准库值类型的赋值操作,Swift 能确保最佳性能,所有没必要为了保证最佳性能来避免赋值

  • 举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var str1 = "hi"
var str2 = str1

print(str1)
print(str2)
/*
str1和str2指针地址相同
打印结果:
hi
hi
*/

str1.appendContentsOf("xixi")
print(str1)
print(str2)

/*
str1和str2指针地址不相同
打印结果:
hixixi
hi
*/

swift为什么将String,Array,Dictionary设计为值类型

  • 值类型和引用类型相比,最大优势可以高效的使用内存
  • 值类型在栈上操作,引用类型在堆上操作。
  • 栈上操作仅仅是单个指针的移动,而堆上操作牵涉到合并,位移,重链接
  • Swift 这样设计减少了堆上内存分配和回收次数,使用 copy-on-write将值传递与复制开销降到最低

属性观察

属性观察是指在当前类型内对特性属性进行监测,并作出响应,属性观察是 swift 中的特性,具有2种, willsetdidset

1
2
3
4
5
6
7
8
9
var title: String {
willSet {
print("willSet", newValue)

}
didSet {
print("didSet", oldValue, title)
}
}
  • willSet会传递新值,默认叫newValue
  • didSet会传递旧值,默认叫oldValue
  • 在初始化器中设置属性值不会触发willSet和didSet

将Swift协议(protocol)中的部分方法设计为可选(optional)

1.在协议和方法前面添加 @objc,然后在方法前面添加 optional关键字,改方式实际上是将协议转为了OC的方式

1
2
3
@objc protocol someProtocol {
@objc optional func test()
}

2.使用扩展(extension),来规定可选方法,在 swift 中,协议扩展可以定义部分方法的默认实现

1
2
3
4
5
6
7
8
9
protocol someProtocol {
func test()
}

extension someProtocol{
func test() {
print("test")
}
}

Swift和ObjC中的protocol有什么不同

相同点: 两者都可以被用作代理;
不同点: Swift中的 protocol还可以对接口进行抽象,可以实现面向协议,从而大大提高编程效率,Swift中的protocol可以用于值类型,结构体,枚举;

Swift和ObjC中的自省

自省在OC中就是判断某一对象是否属于某一个类的操作,有以下2中方式

1
2
[obj iskinOfClass:[SomeClass class]]
[obj isMemberOfClass:[SomeClass class]]

在 Swift 中由于很多 class 并非继承自 NSObject, 故而 Swift 使用is来判断是否属于某一类型, is 不仅可以作用于class, 还是作用于enum和struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ClassA { }
class ClassB: ClassA { }

let obj: AnyObject = ClassB()

if (obj is ClassA) {
print("obj 属于 ClassA")
}

if (obj is ClassB) {
print("obj 属于 ClassB")
}

let string = "String"
if string is String {
//
}

swift 支持函数重载

函数重载是指: 函数名称相同,函数的参数个数不同, 或者参数类型不同,或参数标签不同, 返回值类型与函数重载无关

参考

Swift系列面试题总结
“if let”和”guard let”