swift的重点语法实战 - 重难点摘录

swift所有的语法
https://blog.csdn.net/java_android_man/category_11447444.html

一) 字符串

简单来说,Swift 中提供了两种用于判等的操作符,一个是 == ,一个是 ===

== 通常是用于判定两个对象的内容是否相同 === 通常是用于判定两个对象引用的是否为同一块内存地址

问题

// 元组构造
// 列表构造
// 格式化字符串
// ... 运算符指定范围,从 startIndex 向后移动4位截取子串
// 插入单个字符到指定位置 Hello, William.# append string
// 插入一组字符 Hello, William.-#-# append string
// 替换指定范围的字符串 How are you.-#-# append string
// 删除指定位置的单个字符 How are you.-#-# append strin
// 删除指定范围 -#-# append strin

答案区域

// 元组构造
text = String(describing: (5, 5.0, true)) // "(5, 5.0, true)"
// 列表构造
text = String(describing: [1, 2, 3, 4]) // "[1, 2, 3, 4]"
// 格式化字符串
text = String(format: "Hello, %@", "William") // "Hello, William"
// ... 运算符指定范围,从 startIndex 向后移动4位截取子串
var subStr = indexStr[startIndex...indexStr.index(startIndex, offsetBy: 4)] 

// 插入单个字符到指定位置 Hello, William.# append string
indexStr.insert("#", at: indexStr.index(startIndex, offsetBy: 15))

// 插入一组字符 Hello, William.-#-# append string
indexStr.insert(contentsOf: ["-", "#", "-"], at: indexStr.index(startIndex, offsetBy: 15))

// 替换指定范围的字符串 How are you.-#-# append string
indexStr.replaceSubrange(startIndex...indexStr.index(startIndex, offsetBy: 13), with: "How are you")

// 删除指定位置的单个字符 How are you.-#-# append strin
indexStr.remove(at: indexStr.index(before: indexStr.endIndex))

// 删除指定范围 -#-# append strin
indexStr.removeSubrange(indexStr.startIndex...indexStr.index(indexStr.startIndex, offsetBy: 11))

二) 数组的

问题区域:

// 获取第一个元素
// 获取最后一个元素
//// 截取新数组 
//// 追加一组元素 
//// 在指定位置插入一组元素 
//// 移除一组元素
// 移除首个元素
//// 移除末尾元素
//// 移除前几个元素 
//// 替换指定范围的元素 // 获取数组最大值

答案区域

var firstEle = array.first // 1

// 获取最后一个元素
var lastEle = array.last // 8

// 当数组声明为可变时,才能使用增,删,改等方法,常量数组不能进行修改相关操作
var array = [1, 2, 3, 4, 5, 6, 7, 8]
print(array.count) // 8

// 判断数组是空数组
if array.isEmpty {
    print("array is empty")
} else {
    print("array is not empty")
}

// 通过下标访问元素
var ele = array[1] // 2

// 截取新数组
var subArray = array[1...2] // [2, 3]

// 获取第一个元素
var firstEle = array.first // 1

// 获取最后一个元素
var lastEle = array.last // 8

// 修改下标对应的元素
array[1] = 22
array // [1, 22, 3, 4, 5, 6, 7, 8]

// 修改指定范围的元素
array[0...2] = [1, 2, 3] // [1, 2, 3]
 
// 追加单个元素
array.append(9) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 追加一组元素
array.append(contentsOf: [10, 11, 12]) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入单个元素
array.insert(0, at: 0) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入一组元素
array.insert(contentsOf: [-3, -2, -1], at: 0) // [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除指定元素
array.remove(at: 1) // -2

// 移除一组元素
array.removeSubrange(0...2) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除首个元素
array.removeFirst() // 1

// 移除末尾元素
array.removeLast() // 12

// 移除前几个元素
array.removeFirst(3) // [5, 6, 7, 8, 9, 10, 11]

// 移除后几个元素
array.removeLast(3) // [5, 6, 7, 8]

// 替换指定范围的元素
array.replaceSubrange(0...3, with: [1, 2, 3, 4]) // [1, 2, 3, 4]

// 判断包含指定元素
if array.contains(3) {
    print("array contains 3")
}

// 移除所有元素
array.removeAll() // []

var sortArr = [2, 1, 3, -1]

// 从小到大排序
sortArr.sorted(by: <) // [-1, 1, 2, 3]

// 从大到小排序
sortArr.sorted(by: >) // [3, 2, 1, -1]

// 获取数组最大值
sortArr.min() // -1

// 获取数组最小值
sortArr.max() // 3

数组的7钟遍历

1. 区间遍历
  i的值 
2. for in 遍历 某个元素 
  
3.元组遍历 , 拿到元祖 
 
4.元组.,获取元祖在循环内部 
  
5. 元组遍历  - 拿到单个元祖 
 
6. 反序遍历 - 元祖的反序

7. 反序遍历 - 单个元素的反序 

https://www.jianshu.com/p/e5a6f1c5764e

答案区域:

1. 区间遍历
    print("方法1************")
    for i in 0..

三) 字典

问题

//1 读取字典元素
//此处取字典中未定义的键 不会报错,取出来的值为nil
//2 获取字典元素的个数
 //3 增加字典的元素
//4 删除字典中的元素
//5 修改字典中的元素
//6 遍历
//6.1遍历所有的键值对
//6.2 遍历所有的键
//6.2 遍历所有的值
//7 字典转数组
//7.1 将所有的键转为数组
//7.1 将所有的值转为数组

答案区域

var dic1=[1:1,2:12,3:32,4:16,5:15]
var dic2:Dictionary=[:]
var dic3=Dictionary()
var dic4=[String : String]()

//1 读取字典元素
var test1Dic=["key1":"你好","key2":"Swift","key3":"正在学习","key4":"字典","key5":"取值",]
 
var test1Str=test1Dic["key2"]
println("\(test1Str)")
 
//此处取字典中未定义的键 不会报错,取出来的值为nil
var test1Str2=test1Dic["key"]
println("\(test1Str2)")
 
 
 
//2 获取字典元素的个数
 
println(test1Dic.count)
 
//3 增加字典的元素
 
test1Dic["key"]="test"
println(test1Dic)
 
//4 删除字典中的元素
 
test1Dic.removeValueForKey("key1")
println(test1Dic)
 
//5 修改字典中的元素
 
// 5.1 直接修改
test1Dic["key"]="testkey"
 
// 5.2 使用 updateValue
var oldStr=test1Dic.updateValue("testkeytest", forKey: "key")
println(oldStr)
println(test1Dic)
 
//6 遍历
//6.1遍历所有的键值对
 
for (key,value) in test1Dic{
    println("key:\(key) value:\(value)")
}
 
//6.2 遍历所有的键
for test6Str in test1Dic.keys{
    println(test6Str)
}
 
//6.2 遍历所有的值
for test6Str2 in test1Dic.values{
    println(test6Str2)
}
 
//7 字典转数组
//7.1 将所有的键转为数组
var test7Keys=Array(test1Dic.keys)
println(test7Keys)
 
//7.1 将所有的值转为数组
var test7Values=Array(test1Dic.values)
println(test7Values)

四) 集合

集合的问题

import UIKit

var greeting = "Hello, playground"

// 集合类型:集合 Set
// 不关注顺序,但不可以重复

// 创建Set
// set 获取最大值
// set 获取最小值
// 获取第一个元素,顺序不定

// 通过下标获取元素,只能向后移动,不能向前
// 获取第二个元素
// 获取某个下标后几个元素
// 获取元素个数
// 判断空集合
// 判断集合是否包含某个元素
// 插入
// 移除
// 移除指定位置的元素,需要用 ! 拆包,拿到的是 Optional 类型,如果移除不存在的元素,EXC_BAD_INSTRUCTION
// Set 取交集
// Set 取交集的补集
// Set 取并集
// 判断 Set 集合相等
// 判断子集
// 判断超集
// 遍历元素
// 遍历集合的枚举
// 下标遍历
// 从小到大排序后再遍历

集合的答案

import UIKit

var greeting = "Hello, playground"

// 集合类型:集合 Set
// 不关注顺序,但不可以重复

// 创建Set
var set: Set = [1, 2, 3]
var set2 = Set(arrayLiteral: 1, 2, 3)

// set 获取最大值
set.max()

// set 获取最小值
set.min()

// 获取第一个元素,顺序不定
set[set.startIndex]
set.first

// 通过下标获取元素,只能向后移动,不能向前
// 获取第二个元素
set[set.index(after: set.startIndex)]
// 获取某个下标后几个元素
set[set.index(set.startIndex, offsetBy: 2)]

// 获取元素个数
set.count

// 判断空集合
if set.isEmpty {
   print("set is empty")
}

// 判断集合是否包含某个元素
if (set.contains(3)) {
    print("set contains 3")
}

// 插入
set.insert(0)

// 移除
set.remove(2)
set.removeFirst()
// 移除指定位置的元素,需要用 ! 拆包,拿到的是 Optional 类型,如果移除不存在的元素,EXC_BAD_INSTRUCTION
set.remove(at: set.firstIndex(of: 1)!)

set.removeAll()


var setStr1: Set = ["1", "2", "3", "4"]
var setStr2: Set = ["1", "2", "5", "6"]

// Set 取交集
setStr1.intersection(setStr2) // {"2", "1"}

// Set 取交集的补集
setStr1.symmetricDifference(setStr2) // {"4", "5", "3", "6"}

// Set 取并集
setStr1.union(setStr2) // {"2", "3", "1", "4", "6", "5"}

// Set 取相对补集(差集),A.subtract(B),即取元素属于 A,但不属于 B 的元素集合
setStr1.subtract(setStr2) // {"3", "4"}

var eqSet1: Set = [1, 2, 3]
var eqSet2: Set = [3, 1, 2]
// 判断 Set 集合相等
if eqSet1 == eqSet2 {
    print("集合中所有元素相等时,两个集合才相等,与元素的顺序无关")
}

let set3: Set = [0, 1]
let set4: Set = [0, 1, 2]
// 判断子集
set3.isSubset(of: set4) // set3 是 set4 的子集,true
set3.isStrictSubset(of: set4) // set3 是 set4 的真子集,true
// 判断超集
set4.isSuperset(of: set3) // set4 是 set3 的超集,true
set4.isStrictSuperset(of: set3) // set4 是 set3 的真超集,true

// 遍历元素
for ele in set4 {
    print(ele)
}

// 遍历集合的枚举
for ele in set4.enumerated() {
    print(ele)
}

// 下标遍历
for index in set4.indices {
    print(set4[index])
}

// 从小到大排序后再遍历
for ele in set4.sorted(by: <) {
    print(ele)
}

critical 规则传入

class ReorderArray21: NSObject {
    func test() {
        let s = sort(arrOld:[1,2,3,4,5,6],critical: { $0 % 2 == 0 ? true : false} )
        
    }
    
    func sort(arrOld:[Int] ,critical: (Int) -> Bool ) -> [Int] {
        var arr = arrOld
        if arr.count == 0 {
            return []
        }
        
        var headIndex = 0
        var lastIndex = arr.count - 1
        while headIndex < lastIndex {
            while headIndex < lastIndex && critical(arr[headIndex]) {
                headIndex += 1
                break
            }
            
            while headIndex < lastIndex  && !critical(arr[lastIndex]){
                lastIndex -= 1
                break
            }
            
            if headIndex < lastIndex {
                let temp = arr[headIndex]
                arr[headIndex] = arr[lastIndex]
                arr[lastIndex] = temp
            }
        }
        return arr
    }
  
}

1.3.1 使用 stride(from,to,by)

顺序循环 0 至 10(不包括10),依次递增 2

for index in stride(from: 0, to: 10, by: 2) {
print(index)
}
1
2
3
输出:

0
2
4
6
8
1
2
3
4
5

1.3.2、使用 stride(from,through,by)

逆序循环,10 至 0(包括0),依次递减 2

for index in stride(from: 10, through: 0, by: -2) {
print(index)
}
1
2
3
输出:

10
8
6
4
2
0

查漏补缺的

https://juejin.cn/post/6844903557733285896#heading-32
数据结构 & 算法 in Swift (一):Swift基础和数据结构
阅读记录

问题

  • 传入的数组改为引用类型
  • 可变数组和非可变数组的方法
  • 数组的闭包遍历
  • 集合 的遍历 forEach
  • 集合包含 和数组包含
  • 字典包含某个key值

答案:

  • 传入的数组改为引用类型
  • 可变数组和非可变数组的方法 - 使用var let
  • 数组的闭包遍历
// iteration 2
mutableNumbers.forEach { value in
    if let index = mutableNumbers.index(of: value) {
        print("Index of \(value) is \(index)")
    }
}
  • 集合 的遍历 forEach
numbersSet.forEach { value in
    print(value)
}
  • 集合包含和数组包含
    contains(item)

  • 字典包含某个key值

// excerpt from method that determines if dict contains key
if let _ = dict[key] {
    return true
}
else {
    return false
}

// excerpt from method that obtains first value from dict
for (_, value) in dict {
    return value
}

这两周学习数据结构和算法让我收获很多,除了强化了Swift语法以外,感觉自己看代码的感觉变了

来一次有侧重点的区分Swift与Objective-C
https://juejin.cn/post/6844903776860520462
阅读笔记

一、Objective-C与Swift的异同

1.1、swift和OC的共同点:

  • OC出现过的绝大多数概念,比如引用计数、ARC(自动引用计数)、属性、协议、接口、初始化、扩展类、命名参数、匿名函数等,在Swift中继续有效(可能最多换个术语)。
  • Swift和Objective-C共用一套运行时环境,Swift的类型可以桥接到Objective-C(下面我简称OC),反之亦然

1.2、swift的优点:

  • swift注重安全,OC注重灵活
  • swift注重面向协议编程、函数式编程、面向对象编程,OC注重面向对象编程
  • swift注重值类型,OC注重指针和引用
  • swift是静态类型语言,OC是动态类型语言
  • swift容易阅读,文件结构和大部分语法简易化,只有.swift文件,结尾不需要分号
  • swift中的可选类型,是用于所有数据类型,而不仅仅局限于类。相比于OC中的nil更加安全和简明
  • - swift中的泛型类型更加方便和通用,而非OC中只能为集合类型添加泛型
  • - swift中各种方便快捷的高阶函数(函数式编程) (Swift的标准数组支持三个高阶函数:mapfilterreduce,以及map的扩展flatMap)
  • swift新增了两种权限,细化权限。open (new) > public > internal(默认) > fileprivate (new) > private
  • swift中独有的元组类型(tuples),把多个值组合成复合值。元组内的值可以是任何类型,并不要求是相同类型的。

1.3、swift的不足:

  • 版本不稳定
  • 公司使用比例不高,使用人数比例偏低
  • 有些语法其实可以只规定一种格式,不必这样也行,那样也行。像Go一样禁止一切(Go有点偏激)耍花枪的东西,同一个规范,方便团队合作和阅读他人代码。

二、swift类(class)和结构体(struct)的区别

同: 属性方法下标构造扩展协议
不同: 继承引用转换析构

  • 继承 子类父类, 引用计数类才有, 转换也是类的特性, 析构也是类的特性和引用技术一样的
image.png

2.2 值 VS 引用

问题:

struct enum String, Array和 Dictionary,NSString, NSArray 和NSDictionary 分别是啥类型 ?

答案:

1、结构体 struct 和枚举 enum 是值类型,类 class 是引用类型。
2、String, Array和 Dictionary都是结构体,因此赋值直接是拷贝,而NSString, NSArray 和NSDictionary则是类,所以是使用引用的方式。
3、struct 比 class 更“轻量级”,struct 分配在栈中,class 分配在堆中。
4 (Int、Bool、String、Array、Dictionary 都是值类型

指针

如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。

使用struct 和class 那些场景

答案:

使用struct:任何情况下,优先考虑使用struct,如果满足不了,再考虑class

  • 比如数据被多线程使用,而且数据没有使用class的必要性,就使用struct
  • 希望实例被拷贝时,不收拷贝后的新实例的影响
  • 几何图形的大小,可以封装width和height属性,都是Double类型
  • 指向连续序列范围的方法,可以封装start和length属性,都是Int类型
  • 一个在3D坐标系统的点, 可以封装x, y和z属性,都是Double类型

使用class

  • 需要继承
  • 被递归调用的时候(参考链表的实现,node选用class而不是struct)
  • 属性数据复杂
  • 希望引用而不是拷贝

三、Objective-C中的protocol与Swift中的protocol的区别

相比于OC,Swift 可以做到protocol协议方法的具体默认实现(通过extension)相比多态更好的实现了代码复用,而 OC 则不行。

面向协议相对于面向对象来说更具有可伸缩性和可重用性,并且在编程的过程中更加模块化,通过协议以及协议扩展替代一个庞大的基类,这在大规模系统编程中会有很大的便捷之处。

3.1、协议和协议扩展比基类有三个明显的优点:
1、类型可以遵守多个协议但是只有一个基类。 这意味着类型可以随意遵守任何想要特性的协议,而不需要一个巨大的基类。
2、不需要知道源码就可以使用协议扩展添加功能
3、协议可以被类、结构体和枚举遵守,而类层级约束为类类型

3.2) 面向对象的特点

优点:

  • 封装

数据封装、访问控制、隐藏实现细节、类型抽象为类;
代码以逻辑关系组织到一起,方便阅读;
高内聚、低耦合的系统结构

  • 继承

代码重用,继承关系,更符合人类思维

  • 多态

接口重用,父类指针能够指向子类对象
当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。
向上转型的好处是隐藏了子类类型,提高了代码的扩展性。

3.3、OneV's Den提到的面向对象的三个困境:

1、动态派发的安全性(这应该是OC的困境,在Swift中Xcode是不可能让这种问题编译通过的)
类型的安全的 ,可以检查类型是否父

// Cannot convert value of type 'String' to expected argument type 'NSNumber'
var array: [NSNumber] = []
array.append(1)
array.append("a")
SafeObj *v1 = [[SafeObj alloc] init];
UnSafeObj *v2 = [[UnSafeObj alloc] init];
// 由于v2没有实现协议SafeProtocol,所以此处Xcode会有警告
// Object of type 'UnSafeObj *' is not compatible with array element type 'id'
NSArray> *array = @[v1, v2];
for (id obj in array) {
    [obj func];
}

2、横切关注点

UIViewcontroller和tableViewController之间的数据无法共用

  • 1、Copy & Paste
  • 2、引入 BaseViewController
  • 3、依赖注入
  • 4、多继承

在Swift的面向协议编程中,针对这种问题的解决方案(使用协议扩展添加默认实现):

3、菱形问题

@discardableResult
associatedtype T: Equatable

因为协议不允许使用泛型参数,想要协议使用泛型,请使用关联类型代替,正确的写法如下:
只是把protocol<泛型类型> 改成了在{添加 associatedtype 泛型类型}
protocol Stackble {   //定义一个栈的协议
    associatedtype Element// 在协议中用来占位的类型是关联类型,声明一个关联类型Element
    mutating func push(_ element:Element)
    mutating func pop()->Element
    func top() ->Element
    func size() ->Int
}

https://juejin.cn/post/6844903470005223431
答卓同學 Swift 面試題

问题 :

閉包是引用類型。 ?
2. 不通过继承,代码复用(共享)的方式有哪些
3. Set 独有的方法有哪些?
4. 实现一个 min 函数,返回两个元素较小的元素
5. map、filter、reduce 的作用
11. defer 使用场景
13. 怎么获取一个 String 的长度
14. 如何截取 String 的某段字符串
16. try? 和 try!是什么意思
18. 什么时候使用 final
19. public 和 open 的区别
20. 声明一个只有一个参数没有返回值闭包的别名
21. Self 的使用场景
22. dynamic 的作用
23. 什么时候使用 @objc
24. Optional(可选型) 是用什么实现的
25. 如何自定义下标获取
26. ?? 的作用
30. Error 如果要兼容 NSError 需要做什么操作
31. 下面的代码都用了哪些语法糖 / [1, 2, 3].map{ $0 * 2 }
32. 什么是高阶函数
33. 如何解决引用循环
34. 下面的代码会不会崩溃,说出原因
35. 给集合中元素是字符串的类型增加一个扩展方法,应该怎么声明
36. 定义静态方法时关键字 static 和 class 有什么区别
37. 一个 Sequence 的索引是不是一定从 0 开始?
38. 数组都实现了哪些协议
39. 如何自定义模式匹配
40. autoclosure 的作用
41. 编译选项 whole module optmization 优化了什么
42. 下面代码中 mutating 的作用是什么
43. 如何让自定义对象支持字面量初始化
44. dynamic framework 和 static framework 的区别是什么
45. 为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃。
46. 一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示。


答案:

2 . 不通过继承,代码复用(共享)的方式有哪些

                 protocol + extension: protocol where Self: ... 。
                 extension SomeClass / SomeStruct / SomeEnum。
                如果繼承自 NSObject,還可以用 runtime,不過如果和原 Foundation 庫命名重複的話,則不確定會先執行那個,遇到 bug 會無語。
  1. Set 独有的方法有哪些?
    Set 可以有交集並集等數學方法,同時如果使用類似 let set = Set([1, 1, 1, 2]) 的方式可以過濾掉重複的項目,也就是說 set 中最終只會有 1, 2,使用的前提條件是 Element 要遵循 Hashable。

  2. 实现一个 min 函数,返回两个元素较小的元素

func myMin(a: T, b: T) -> T {
    return a < b ? a : b
}

  1. map、filter、reduce 的作用
  1. defer 使用场景
func myDefer() {
    defer { print("3") }
    defer { print("2") }
    defer { print("1") }
    print("0")
}

myDefer()
// Result:
// 0
// 1
// 2
// 3
  1. 怎么获取一个 String 的长度
let length1 = "string".characters.count
let length2 = "string".data(using: .utf8).count
let length3 = ("string" as NSString).length
  1. 如何截取 String 的某段字符串
    let a = "string".characters[0..1]

  2. try? 和 try!是什么意思

                 try? 用來修飾一個會拋出錯誤的函數,其返回結果會被包在 Optional 中,用來表示“我不想要管這次拋出的錯誤是什麼”,所以有說會拋錯的函數就不要返回 Optional 對象。
                 try! 用來表示“我還是不想管拋出的錯誤,但是如果拋錯了,就崩潰程序好了”,其結果會被表示為 T!。
    
  3. 什么时候使用 final
    聲明該 class 不可被繼承。
    聲明該函數或屬性不可被 override。
    聲明為 final 時可以讓編譯器知道該類型不會被繼承,因此可以做一小點優化。

  4. public 和 open 的区别
    public 用來聲明該函數或屬性只有在這個 module 中可以被覆寫和繼承,而 open的則權限最高,不僅可以在其他 module 中被訪問,而且可以被繼承和覆寫。

  5. 声明一个只有一个参数没有返回值闭包的别名
    Typealias Callback = (Int) -> Void

  6. Self 的使用场景

    Self 與 self 的差異在於,一個表明類,一個表明實例。
     協議中聲明: procotol MyProtocol: class {
    func then() -> Self
}
          擴展協議: protocol MyProtocol { }

extension MyProtocol where Self: UIView { }
  1. dynamic 的作用
    宣告其屬性可被 KVO 的,該類必須繼承 NSObject,最經常用到的就是繼承 Realm.Object 的時候。
  2. 什么时候使用 @objc
宣告某個協議方法是可以不被實現的,比如 UITableViewDelegate 中的所有方法都是。
                另外,使用 Target-Action 時為了不暴露類中 selector 所指的方法而將該方法宣告為 fileprivate 或 private 時,因為需要被 runtime 時可以觸發,因此加上 @objc: class MyViewController: UIViewController {
    func myMethod() {
        let _ = UIMenuItem(target: self, action: #selector(theAction))
    }

    @objc private func theAction(sender: Any) { 
    }
}
  1. Optional(可选型) 是用什么实现的
    Optional 本身是一個 enum 對象,裡面只有兩個枚舉成員,一個是 .some(Wrapped),一個是 .none。另外還符合了 Nil.... 什麼的協議表明可以被指向 nil。

  2. 如何自定义下标获取

class MyClass {
    private let a = ["a", "b"]

    subscript(index: Int) -> String {
        return a[index]
    }
}

26
語法糖。用於嘗試取得 Optional 中的值時,可以聲明當 Optional 為 .none 時,使用 ?? 右邊的值。另外右邊值為閉包時,有被 @autoclosure 優化,確保需要時才計算得到結果。

  1. 一个类型表示选项,可以同时表示有几个选项选中(类似 UIViewAnimationOptions ),用什么类型表示

  2. Error 如果要兼容 NSError 需要做什么操作

error as NSError

extension Error {
    func toNSError(
        domain: String = "myDomain",
        code: Int = 0, 
        userInfo: [String: Any]? = nil) 
        -> NSError 
    {
        return NSError(domain: domain, code: code, userInfo: userInfo)
    }
}

enum MyError: Error {
    case some
}

let _ = MyError.some.toNSError()
let error = MyError.some.toNSError(domain: "myError", code: 0)

  1. 下面的代码都用了哪些语法糖 / [1, 2, 3].map{ $0 * 2 }
[1, 2, 3] 是陣列的語法糖,意義為字面上表示為 Array 的陣列。
Trailing Closures。如果一個函數的最後一個參數是閉包時,則可以不寫 ()。
省略閉包參數的類型聲明,其被推斷為 Int。
$0 表明是陣列中的每一個元素,省略了閉包參數的命名。
省略了閉包回傳值類型的聲明,其被推斷為 Int。
若閉包只有一行,則可以省略 return。
  1. 什么是高阶函数
    基本上可以定義為 map、flatMap、filter、reduce 等函數式的方法。

  2. 如何解决引用循环

引用循環問題主要發生為:引用類型對象和另外一個引用類型對象相互引用,解決方法為:
其中一個引用對象的屬性聲明為 weak 或 unowned。
閉包的捕獲列表中聲明 [weak someObject, weak self, unowned self] 等,再次論證閉包是引用類型。
  1. 下面的代码会不会崩溃,说出原因
var mutableArray = [1,2,3]
for _ in mutableArray {
  mutableArray.removeLast()
}

不會。原因:
大致上來說是因為 Sequece 中的迭代器吧,記得有在《Swift 進階》中提到,詳細原因說不清。

  1. 定义静态方法时关键字 static 和 class 有什么区别
    class 關鍵字對象可以被 override,static 則不行。

  2. 一个 Sequence 的索引是不是一定从 0 开始?
    不一定。比如:

let array = [0...100]
let arraySlice = array[50..<80] // arraySlice 的 startIndex 應該是50
  1. 数组都实现了哪些协议
    Collection, RandomAddress...(忘記了)。
  1. 如何自定义模式匹配
    不太理解題目,是說 if case .some(let value) = 3? 之類的嗎?

  2. autoclosure 的作用
    額,恰好前面有提到,用來進行一些優化,可以當必要時候再計算閉包中的數值,因此要求類型為 () -> T。

  3. 下面代码中 mutating 的作用是什么

// 題目
struct Person {
  var name: String {
      mutating get {
          return store
      }
  }
}
  1. 如何让自定义对象支持字面量初始化

只要讓某個類或結構體遵循 ExpressibleByStringLiteral 協議就行。

  1. dynamic framework 和 static framework 的区别是什么
    static framework 是 .a 檔,需要在運行時就被載入,無法被共用記憶體。
    dynamic framework 是 iOS 8 之後提出的,本質上是個 Bundle,如果已經被加載過,則可以共享使用。

  2. 为什么数组索引越界会崩溃,而字典用下标取值时 key 没有对应值的话返回的是 nil 不会崩溃。
    使用 Index Swift 認為你很自信并肯定取值的過程是不會有異常的,所以越界就給你崩;而 Dictionary 的 key 需要遵循 Hashable 協議才可以使用,因此在查找值的時候進行了算法提高,但也表明取值結果的不一定?總覺得這話自己說的都怪怪的。

  3. 一个函数的参数类型只要是数字(Int、Float)都可以,要怎么表示。

func myMethod(_ value: T) where T: ExpressibleByIntegerLiteral, T: ExpressibleByFloatLiteral {

}

iOS面试题:常问Swift问题
https://juejin.cn/post/7006273689675120677

问题:

2. Swift 和OC 如何相互调用?
6.Swift,什么是泛型?
7. 访问控制关键字 open, public, internal, fileprivate, private 的区别?
8.关键字:Strong,Weak,Unowned 区别?
9. 如何理解copy-on-write?
10.什么是属性观察?
11. swift 为什么将 String,Array,Dictionary设计为值类型?
12.如何将Swift 中的协议(protocol)中的部分方法设计为可选(optional)?
13.比较Swift 和OC中的初始化方法 (init) 有什么不同?
14.比较 Swift和OC中的 protocol 有什么不同?
15.swift 和OC 中的自省 有什么区别?
16.什么是函数重载? swift 支不支持函数重载?
17.swift 中的枚举,关联值 和 原始值的区分?
18. swift 中的闭包结构是什么样子的?
19. 什么是尾随闭包?
20. 什么是逃逸闭包?
21. 什么是自动闭包?
22. swift中, 存储属性和计算属性的区别?
23. 什么是延迟存储属性(Lazy Stored Property)?
24. 什么是属性观察器?
25. swift中什么类型属性(Type Property)?
26. swift 中如何使用单例模式?
27.swift 中的下标是什么?
27.简要说明Swift中的初始化器?
28.什么可选链?
29. 什么是运算符重载(Operator Overload)?

答案

2. Swift 和OC 如何相互调用?

Swift 调用 OC代码
需要创建一个 Target-BriBridging-Header.h 的桥文件,在乔文件导入需要调用的OC代码头文件即可
OC 调用 Swift代码
直接导入 Target-Swift.h文件即可, Swift如果需要被OC调用,需要使用@objc 对方法或者属性进行修饰

6.Swift,什么是泛型?

泛型主要是为增加代码的灵活性而生的,它可以是对应的代码满足任意类型的的变量或方法;
泛型可以将类型参数化,提高代码复用率,减少代码量

// 实现一个方法,可以交换实现任意类型
func swap(a: inout T, b: inout T) {
(a, b) = (b, a)
}

7. 访问控制关键字 open, public, internal, fileprivate, private 的区别?

Swift 中有个5个级别的访问控制权限,从高到低依次是 open, public, internal, fileprivate, private
它们遵循的基本规则: 高级别的变量不允许被定义为低级别变量的成员变量,比如一个 private 的 class 内部允许包含 public的 String值,反之低级变量可以定义在高级别变量中;

open: 具备最高访问权限,其修饰的类可以和方法,可以在任意 模块中被访问和重写.
public: 权限仅次于 open,和 open 唯一的区别是: 不允许其他模块进行继承、重写
internal: 默认权限, 只允许在当前的模块中访问,可以继承和重写,不允许在其他模块中访问
fileprivate: 修饰的对象只允许在当前的文件中访问;
private: 最低级别访问权限,只允许在定义的作用域内访问

8.关键字:Strong,Weak,Unowned 区别?

Swift 的内存管理机制同OC一致,都是ARC管理机制; Strong,和 Weak用法同OC一样

9. 如何理解copy-on-write?

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

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

10.什么是属性观察?

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

var title: String {
    willSet {
        print("willSet", newValue)

    }
    didSet {
        print("didSet", oldValue, title)
    }
}

willSet会传递新值,默认叫newValue
didSet会传递旧值,默认叫oldValue
在初始化器中设置属性值不会触发willSet和didSet

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

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

12.如何将Swift 中的协议(protocol)中的部分方法设计为可选(optional)?

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

@objc protocol someProtocol {
  @objc  optional func test()
}

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

protocol someProtocol {
    func test()
}

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

13.比较Swift 和OC中的初始化方法 (init) 有什么不同?

swift 的初始化方法,更加严格和准确, swift初始化方法需要保证所有的非optional的成员变量都完成初始化, 同时 swfit 新增了convenience和 required两个修饰初始化器的关键字

  • convenience只提供一种方便的初始化器,必须通过一个指定初始化器来完成初始化
  • required是强制子类重写父类中所修饰的初始化方法

14.比较 Swift和OC中的 protocol 有什么不同?

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

15.swift 和OC 中的自省 有什么区别?

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

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

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

16.什么是函数重载? swift 支不支持函数重载?

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

17.swift 中的枚举,关联值 和 原始值的区分?

关联值--有时会将枚举的成员值跟其他类型的变量关联存储在一起,会非常有用

// 关联值
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}

原始值--枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值

// 原始值
enum Grade: String {
  case perfect = "A"
  case great = "B"
  case good = "C"
  case bad = "D"
}

18. swift 中的闭包结构是什么样子的?

{
    (参数列表) -> 返回值类型 in 函数体代码
}

19. 什么是尾随闭包?

将一个很长的闭包表达式作为函数的最后一个实参
使用尾随闭包可以增强函数的可读性
尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式

// fn 就是一个尾随闭包参数
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}

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

20. 什么是逃逸闭包?

当闭包作为一个实际参数传递给一个函数或者变量的时候,我们就说这个闭包逃逸了,可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。

非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明

// 定义一个数组用于存储闭包类型
var completionHandlers: [() -> Void] = []

//  在方法中将闭包当做实际参数,存储到外部变量中
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

如果你不标记函数的形式参数为 @escaping ,你就会遇到编译时错误。

21. 什么是自动闭包?

自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号。

func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
  • 为了避免与期望冲突,使用了@autoclosure的地方最好明确注释清楚:这个值会被推迟执行
  • @autoclosure 会自动将 20 封装成闭包 { 20 }
  • @autoclosure 只支持 () -> T 格式的参数
  • @autoclosure 并非只支持最后1个参数
  • 有@autoclosure、无@autoclosure,构成了函数重载

如果你想要自动闭包允许逃逸,就同时使用 @autoclosure 和 @escaping 标志。

22. swift中, 存储属性和计算属性的区别?

Swift中跟实例对象相关的属性可以分为2大类

存储属性(Stored Property)

类似于成员变量这个概念
存储在实例对象的内存中
结构体、类可以定义存储属性
枚举不可以定义存储属性

计算属性(Computed Property)

本质就是方法(函数)
不占用实例对象的内存
枚举、结构体、类都可以定义计算属性

struct Circle {
    // 存储属性
    var radius: Double
    // 计算属性
    var diameter: Double {
        set {
            radius = newValue / 2
        }
        get {
            return radius * 2
        }
    }
}

23. 什么是延迟存储属性(Lazy Stored Property)?

image.png
class PhotoView {
    // 延迟存储属性
    lazy var image: Image = {
        let url = "https://...x.png"        
        let data = Data(url: url)
        return Image(data: data)
    }() 
} 

24. 什么是属性观察器?

可以为非lazy的var存储属性设置属性观察器,通过关键字willSet和didSet来监听属性变化

struct Circle {
    var radius: Double {
        willSet {
            print("willSet", newValue)
        } 
        didSet {
            print("didSet", oldValue, radius)
        }
    } 
    init() {
        self.radius = 1.0
        print("Circle init!")
    }
}

25. swift中什么类型属性(Type Property)?

严格来说,属性可以分为
实例属性(Instance Property): 只能通过实例对象去访问

  • 存储实例属性(Stored Instance Property):存储在实例对象的内存中,每个实例对象都有1份
  • 计算实例属性(Computed Instance Property)

类型属性(Type Property):只能通过类型去访问

  • 存储类型属性(Stored Type Property):整个程序运行过程中,就只有1份内存(类似于全局变量)
  • 计算类型属性(Computed Type Property)

可以通过static定义类型属性 p如果是类,也可以用关键字class

struct Car {
    static var count: Int = 0
    init() {
        Car.count += 1
    }
}

不同于存储实例属性,你必须给存储类型属性设定初始值

  • 因为类型没有像实例对象那样的init初始化器来初始化存储属性

存储类型属性默认就是lazy,会在第一次使用的时候才初始化

  • 就算被多个线程同时访问,保证只会初始化一次
  • 存储类型属性可以是let

枚举类型也可以定义类型属性(存储类型属性、计算类型属性)

26. swift 中如何使用单例模式?

可以通过类型属性+let+private 来写单例; 代码如下如下:

 public class FileManager {
    public static let shared = {
        // ....
        // ....
        return FileManager()
}()
    private init() { }
}

27.swift 中的下标是什么?

使用subscript可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为:下标脚本
subscript的语法类似于实例方法、计算属性,本质就是方法(函数)

class Point {
    var x = 0.0, y = 0.0
    subscript(index: Int) -> Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue }
        }
        get {
            if index == 0 {
                return x
            } else if index == 1 {
                return y
            }
            return 0
        }
    }
}

var p = Point()
// 下标赋值
p[0] = 11.1
p[1] = 22.2
// 下标访问
print(p.x) // 11.1
print(p.y) // 22.2

27.简要说明Swift中的初始化器?

  • 类、结构体、枚举都可以定义初始化器
  • 类有2种初始化器: 指定初始化器(designated initializer)、便捷初始化器(convenience initializer)
 // 指定初始化器 
init(parameters) {
    statements 
}
// 便捷初始化器
convenience init(parameters) {
    statements 
} 

规则:

  • 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
  • 默认初始化器总是类的指定初始化器
  • 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器

初始化器的相互调用规则

  • 指定初始化器必须从它的直系父类调用指定初始化器
  • 便捷初始化器必须从相同的类里调用另一个初始化器
  • 便捷初始化器最终必须调用一个指定初始化器

28.什么可选链?

可选链是一个调用和查询可选属性、方法和下标的过程,它可能为 nil 。如果可选项包含值,属性、方法或者下标的调用成功;如果可选项是 nil ,属性、方法或者下标的调用会返回 nil 。多个查询可以链接在一起,如果链中任何一个节点是 nil ,那么整个链就会得体地失败。

  • 多个?可以链接在一起
  • 如果链中任何一个节点是nil,那么整个链就会调用失败

29. 什么是运算符重载(Operator Overload)?

类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载

struct Point {
    var x: Int
    var y: Int

    // 重载运算符
    static func + (p1: Point, p2: Point) -> Point   {
        return Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
}

var p1 = Point(x: 10, y: 10)
var p2 = Point(x: 20, y: 20)
var p3 = p1 + p2

for index in stride(from: 10, through: 0, by: -2) {print(index)}

https://blog.csdn.net/weixin_42339759/article/details/113075211

你可能感兴趣的:(swift的重点语法实战 - 重难点摘录)