网站建设及推广的书,在线代理网页版,学校网站构建,深圳企业有限公司17. 字面量协议、模式匹配、条件编译
字面量#xff08;Literal#xff09;
var age 10
var isRed false
var name Jack上面代码中#xff1a;10、false、Jack就是字面量
可以看到#xff0c;初始化过程很简单#xff0c;直接赋值即可 Swif…
17. 字面量协议、模式匹配、条件编译
字面量Literal
var age 10
var isRed false
var name Jack上面代码中10、false、Jack就是字面量
可以看到初始化过程很简单直接赋值即可 Swift自带的绝大部分类型都支持直接通过字面量进行初始化 而当是一个对象的时候却需要使用下面方法去初始化
var p Person()
var p Person.init()问为何自带类型可以直接初始化而对象却不可以
是因为Swift自带类型遵守了对应的 字面量协议 var num: Int true 当直接写上述代码时是错误的❌ 因为把bool值类型赋值给了int类型类型不匹配
但当做下列操作即可变为编译正确
//extension扩展协议
//Int类型遵守ExpressibleByBooleanLiteral协议
extension Int: ExpressibleByBooleanLiteral{//协议要实现的方法public init(booleanLiteral value: BooleanLiteralType) {self value ? 1 : 0}
}var num: Int true
print(num)var num2: Int 100
print(num2)1
100在扩展中添加协议成员 我们可以通过扩展来扩充已存在类型( 类结构体枚举等)。 扩展可以为已存在的类型添加属性方法下标脚本协议等成员。 其中
//ExpressibleByBooleanLiteral是一个协议
public protocol ExpressibleByBooleanLiteral {associatedtype BooleanLiteralType : _ExpressibleByBuiltinBooleanLiteral//协议要实现的方法init(booleanLiteral value: Self.BooleanLiteralType)
}作业
var num: Int? Int(123)
print(num)//123var num2: Int? Int(fff)
print(num2)//nil扩展出
var num: Int? 123
print(num)//123var num2: Int? fff
print(num2)//nil或0参考答案
extension Int: ExpressibleByStringLiteral
{public init(stringLiteral value: String) {self (Int(value) ! nil ? Int(value)! : 0)}
}var num: Int? 123
print(num)var num2: Int? fff
print(num2)模式Pattern
什么是模式 模式是用于匹配的规则比如switch的case、捕捉错误的catch、if\guard\while\for语句 的条件等
let age 2
if case 0...9 age {print(in)
}相当于switch写法
switch age{case 0...9:print(in)default:break
}也就是if case相当于只有一个case的switch
for case let
let points [(1, 0), (2, 1), (3, 0)]
for case let(x, 0) in points{print(x)
}
//1
//3let ages: [Int?] [nil, 2, 3, nil, 5]
for case let age? in ages {print(age)
}
等价于
for item in ages {if let age item {print(age)}
}可以看出可选模式上面的这种写法比下面的简单
表达式模式Expression Pattern
表达式模式用在case里面
利用这个可以自定义case的匹配规则 比如学生里面有score和名字当做switch比较的时候就可以只比较score
~ 重写模式
// MARK标记 // TODO: 将要做 // FIXME: 需要修复 18. 从OC到Swift
Swift调用OC
新建一个桥接头文件文件名格式默认为{targetName}-Bridging-Header.h 该文件是OC暴露给Swift使用的在{targetName}-Bridging-Header.h文件中# importOC需要暴露给Swift的内容
如果C语言暴露给Swift的函数名跟Swift中其他函数名冲突了
可以在Swift中使用_silgen_name修改C函数名
例如
OC调用Swift
Xcode已经默认生成一个用于OC调用Swift的头文件文件名格式是{targetName-Swift.h}
不同于上面那种直接在.h文件中手动写导入#import 方法 OC调用Swift不需要自己手动导入{targetName-Swift.h} 而是暴露出来即前面加上特定关键词系统会自动加入到{targetName-Swift.h}文件中去
swift中的class需要暴露出来才能被OC引用。 swift中的class继承NSOjbect即是暴露操作
问1为什么要暴露给OC的 Swift中的类 一定要继承NSObject
因为是在OC里面调用走的是runtime那一套也就是需要isa指针因此在Swift中必须使其继承NSObject
问2OC中的方法在Swift中去调用比如person.run()(run方法定义在OC中)那么此时的底层调用是OC的runtime机制还是Swift中虚表机制同理在OC中调用Swift方法比如[car run];run方法定义在Swift中底层调用又是如何调用的
通过汇编打断点可以看出
在Swift中调用OC的方法还是使用的runtime那一套 在OC中调用Swift的方法函数由于已经是继承NSObject因此还是走的runtime那一套
问3在Swift中class已经被暴露出去那么此时再调用已经被暴露出去的函数方法底层又是如何呢
如果没暴露调用函数方法必定是走Swift调用那一套
仅仅是暴露还是在Swift中调用的话没有走runtime那一套走的是Swift自己虚表那一套
如果实在是想让方法走runtime那一套的话可以在方法前加上dynamic关键字则就走的是runtime那一套 以上图片方法是指 方法一使用objc给每一个需要暴露的属性添加修饰 方法二使用objcMembers修饰类里面所有的属性都可以访问不需要一个一个加
同样防止两个属性、类同名也可以对Swift的调用修改名字 objc(name)
OC调用Swift中的#selector(方法名1) 方法名1前面需要加objc修饰将其暴露出去 因为本身#selector方法在OC中就是runtime那些所以在swift中需要暴露 String
Swift中的String与OC中的NSString可以互相桥接转换String不能 桥接转换成 NSMutableStringNSMutableString继承NSString因此可以 桥接转换成String
其他Siwft、OC桥接转换 19. 从OC到Swift、函数式编程
问下列p对象占多少个字节
class Person{var age 10var weight 20
}var p Person()
//8 8 8 8
//metadata指针 引用计数相关 age weight8个字节是metadata指针 8个字节是 引用计数相关 8个字节是age 8个字节是weight 因此一共占32个字节
问下列p对象继承NSObject占多少个字节
class Person: NSObject{var age 10var weight 20
}var p Person()8个字节是isa指针 8个字节是age 8个字节是weight 字节对齐需要是16的倍数需要8个字节 因此一共占32个字节 虽然都是32个字节但是存储内容不一样 只能被class继承的协议
某个协议只允许被class继承不允许被struct继承如何操作
//定义一个协议
protocol Runnable {
}//struct遵守协议
struct Person: Runnable {}
//class遵守协议
class Student: Runnable {}当协议被这样修改的时候就可以实现想要的效果
protocol Runnable: AnyObject {}protocol Runnable: class {}objc protocol Runnable {}被objc修饰的协议还可以暴露给OC去遵守实现 即OC可以调用swift中的协议方法
dynamic
被objc dynamic修饰的内容会具有动态性比如走Runtime那一套流程
KVC\KVO
swift支持KVC\KVO 有几个要求
属性所在的类、监听器需要最终继承NSObject属性前加objc dynamic修饰
关联对象Associated Object
在swift中只有Class可以使用关联对象 默认情况下extension(扩展)是不可以添加存储属性的可以扩展计算属性 此时使用关联对象就可以实现类似类实现存储属性的效果
class Person {}extension Person {//定义一个keyprivate static var AGE_KEY: Bool falsevar age: Int {get{//Person.AGE_KEY 或者 Self.AGE_KEY// Self就相当于当前类(objc_getAssociatedObject(self, Person.AGE_KEY) as? Int) ?? 0}set{//newValue把传进来的值存进去(10)objc_setAssociatedObject(self, Person.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN)}}
}var p Person()
p.age 10
print(p.age)资源名管理
直接赋值图片名不太好因为有一张图片100处在用如果现在要修改图片则需要全局修改 可以做类似安卓的图片名赋值方式写一个全局的名称用的时候直接用全局字符串名修改的时候只需要修改一处地方即可。
多线程开发
DispatchQueue.main.async {print(主队列异步执行)
}DispatchQueue.global().async {print(Thread.current, 全局并发队列)
}延迟执行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() 10) {print(do something)
}只执行一次
dispatch_once在swift中已经被废弃
可以使用lazy代替
//fileprivate只访问当前文件
//全局变量的初始化默认也是lazy的
fileprivate var initTask: Void {print(----init-----)
}()class ViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()let _ initTasklet _ initTask//只会打印一次}
}Array的常见操作
mapfilterreduceflatMapcompactMap
map
map的作用会将array里面的元素全部拿出来遍历然后处理元素处理完毕后再组成一个新数组array2
var array [1, 2, 3, 4]
//map的作用会将array里面的元素全部拿出来遍历然后处理元素处理完毕后再组成一个新数组array2
var array2 array.map { i inreturn i*2
}
//或者简写
//var array2 array.map{ $0 * 2}print(array2)
//[2, 4, 6, 8]映射一遍可以改变输出元素的类型比如Int变成String
filter
filter也会遍历数组的每一个元素但它会有过滤的效果 array.filter(isIncluded: (Int) throws - Bool) 里面的返回值是Bool类型 如果是true则放入到新数组里 如果是false则不要
找出数组里面元素为偶数的元素组成新数组
var array3 array.filter { i inreturn i % 2 0
}
print(array3)
//[2, 4]reduce
reduce也会遍历array里面所有的元素 然后对元素做有关联 的操作 有关联下次用到的数据与上次运行的结果有关 //0是初始值第一次遍历的时候partialResult 0
//当第二次遍历的时候partialResult就是partialResult i(初始值已经没用了)
//i就是遍历的元素
var array4 array.reduce(0) { partialResult, i inreturn partialResult i
}print(array4)
//10
大致过程是
0 1 1
1 2 3
3 3 6
6 4 10
或者是
((((0 1) 2) 3) 4) 10reduce还可以简写 var array4 array.reduce(0) { $0 $1 }
reduce就是解决遍历数组对里面所有元素进行有关联操作的问题
flatMap
首先了解下Array.init(repeating: 2, count: 3) 代表创建一个数组数组3个元素每个元素的值都是2
var array [1, 2, 3]var array2 array.map { Array.init(repeating: $0, count: $0)}
var array3 array.flatMap { Array.init(repeating: $0, count: $0)}print(array)
print(array2)
print(array3)
打印结果
[1, 2, 3]
[[1], [2, 2], [3, 3, 3]]
[1, 2, 2, 3, 3, 3]也就是flatMap会将数组里面的元素放在新的数组里面
compactMap
var array [123, test, jack, -30]
var array2 array.map{Int($0)}
var array3 array.compactMap{Int($0)}
print(array)
print(array2)
print(array3)
打印结果
[123, test, jack, -30]
[Optional(123), nil, nil, Optional(-30)]
[123, -30]当使用map的时候里面转换有可能转换成功也可能转换失败因此数组里面存放的是可选类型或nil 当使用compactMap的时候返回结果里面会将nil去掉并且将可选类型解包