1.swift3.0以后,去除了自增(++)
和自减(--)
的运算符,替代成了-=
和 +=
func TestDemo() {
var num = 5
while num > 0 {
print("打印了了\(num)次")
num -= 1
}
}
2.区间运算符 注意对应的符号,一定是3个点, 2个点的是半区间,字符串也能使用区间运算符,但是不能再for in中使用
-
半区间运算符 1...3
-
闭区间运算符 1..<3
-
单侧(方向)区间:[...3] [3...]
3.区间类型
let range: ClosedRange<Int> = 1...3
let range2: Range<Int> = 1..<3
let range3: PartialRangeThrough<Int> = ...5
3.switch注意点
case default
至少要有一条语句,如果不需要,加个break
即可
如果能保证已处理所有情况,可以不必使用default
4.switch和enum的标准用法
enum虽然有其他简便写法,但是此种是最标准的
-
public enum SelectedBarVerticalAlignment {
case top
case middle
case bottom
}
switch selectedBarVerticalAlignment {
case .top:
selectedBarFrame.origin.y = 0
case .middle:
selectedBarFrame.origin.y = (frame.size.height - selectedBarHeight) / 2
case .bottom:
selectedBarFrame.origin.y = frame.size.height - selectedBarHeight
}
枚举相关概念:
1.枚举成员可以使用相同类型的默认值预先对应, 这个值默认叫做原始值
2.如果枚举的原始值类型是Int
String
,swift会自动分配原始值, 这个叫:隐式原始值
3.原始值不占用枚举变量的内存
5.swift中的访问控制关键字
-
private
私有访问控制 只允许当前类中调用 -
fileprivate
(不常用) 其修饰的属性或者方法只能在当前的 Swift 源文件里可以访问 -
public
修饰的属性或者方法可以在其他作用域被访问,但不能在override 和 继承方法中的 Extension 中被访问 -
open
在override 和 继承方法中的 Extension 中可以被访问
总结:原来 Swift 中有2种常用访问控制关键字(访问控制修饰符),分别为 private 和 public。而在 Swift 3.0+,又在原来的基础上新增了两种:fileprivate、open。它们可以看成是对 private 和 public 的进一步细分
6.空合并运算符(常用)
高级语法中的 两个问号?: 意思是:如果数据不存在,则为后面的值
var optionalInt: Int?
let result: Int = optionalInt ?10 result = 10
var optionalInt: Int= 20
let result: Int = optionalInt ?10 result = 20
// if let 与 结合使用
let a: Int= nil
let b: Int= 2
if let c = a ?b {
print(c)
}
6.guide语句
读几遍:
当guard
语句的条件为false
, 就会执行大括号里面的代码
当guard
语句的条件为true
, 就会跳过guard语句
当guard
语句进行可选性绑定时,绑定的let var能在外层作用域中使用
总结: 判断条件不符合的高级写法,优化了OC的判断特性
guard <#condition#> else {
<#statements#>
}
7.结构体和类
吐槽:初学感觉类和结构体傻傻分不清(忽略) 精简分析一下:不一笔带过
结构体分析:
- 在swift标准库中,
绝大多数的公开类型都是结构体
,而枚举和类只占很小一部分 - 比如
Bool, Int, Double, String, Array, Dictionary
等常见类型都是结构体 - 所有的结构体,编译器都会根绝情况为结构体
自动生成多个初始化器
,但是类是不会生成初始化器 -
自定义
初始化器时,编译器无法自动生成其他初始化器
类的分析:
- 类中所有成员在定义时,指定了初始值,编译器才会为类生成无参的初始化器
类和结构体的本质区别:
- 类是引用类型(指针类型),引用赋值给
var
,let
或者函数传参
是将内存地址拷贝一份,属于浅拷贝
- 结构体和枚举是值类型,值类型赋值给
var
,let
或者函数传参
是将所有内容,产生了新的副本,属于深拷贝
注意:
初始化器的作用:保证所有的成员都有初始值
枚举,结构体,类都可以定义方法
8.属性
swift 中实例相关的属性可以分类2大类
存储属性:
- 存储在实例的内存中
- 结构体,类可以定义存储属性,枚举不可以
- 延迟存储属性
lazy
:
1.第一次用到的时候,属性才会初始化,lazy
属性必须是var
,let
必须在初始化完成之前拥有初始值
2.如果多条线程同时第一次访问lazy
属性,无法保证属性只被初始化一次
计算属性: - 本质就是方法(函数)
- 计算属性不占用实例内存
- 定义计算属性只能用
var
,不能用let
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diamater:Double {
set {
radius = newValue / 2
}
get {
radius * 2
}
}
}
9.属性观察器 willSet 和 didSet 现身
可以为非lazy
地var
存储属性设置属性观察器
-
willSet
会传递新值,默认叫newValue -
didSet
会传递旧值,默认叫oldValue - 初始化器设置属性值不会触发
- 在属性定义时设置初始值也不会触发
- 可以应用在全局或局部变量身上
struct Circle {
// 存储属性
var radius: Double {
willSet {
}
didSet {
}
}
}
10.inout
关键字
inout的本质
: 引用传递(地址传递)
11. 类型属性
- 实例属性 :只能通过实例去访问,每个实例都有一份内存
- 类型属性 :整个程序运行过程中,就只有1份内存(类似于全局变量)
1.通过static
定义类型属性
, 也可以通过class
定义, 整个程序运行过程中,就只有一份内存(类似于全局变量)
2.存储类型属性必须设定初始值
细节:
存储属性分为: - 实例属性:
- 类型属性:
1.必须设定初始值(因为类型没有实例那样的init初始器来初始存储类型)
2.类型属性默认是lazy
,会在第一次使用的时候才初始化(就算被多个线程访问,也只会初始化一次)
3.类型属性可以是let
12. 方法
枚举 结构体 类都可以定义实例方法和类方法
- 实例方法:通过实例对象调用
- 类型方法:通过类型调用,用static 和 class 关键字定义
-
self
在实例方法中代表是实例对象, 在类型中代表是类 -
mutating
: 结构体和枚举是值类型, 值类型的属性不能被自身的实例方法修改, 如果要修改在fun关键字前加mutating
-
discardableResult
消除调用后返回值未被使用的警告
13.下标
下标是用于访问集合、列表或序列的成员元素的快捷方式。可以使用下标,设置和获取值,而不用单独的调用对应的存取
下标语法
subscript(index: Int) -> Int {
get {
// 返回一个适当的 Int 类型的值
}
set(newValue) {
// 执行适当的赋值操作
}
}
下标用法:
class Student: NSObject {
var name = ""
var age = 10
var height = 170
subscript(index:Int) -> AnyObject{
switch index{
case 0:
return name as AnyObject
case 1:
return age as AnyObject
case 2:
return height as AnyObject
default:
return name as AnyObject
}
}
}
//调用
let stu = Student()
stu.name = "海王"
print(stu[0]) //海王
print(stu[1]) //10
print(stu[2]) //170
14.继承
- 值类型不支持继承,只有类才可以继承
- 子类可以重写父类的
方法
下标
属性
,重写时加上关键字override
- 被
class
修饰的类方法,下标,允许
被子类重写 - 被
static
修饰的类方法,下标,不允许
被子类重写 - 子类可以把父类属性重写为计算属性
- 子类不可以把父类属性重写为存储属性
- 只能重写var属性,不能重写let属性
- 重写后的属性权限不能小于父类属性
-
final
修饰的方法, 下标, 属性 不能被重写 -
final
修饰的类不能被继承关系
15.初始化器
有两种:
- 便利初始化器
- 指定初始化器
required
修饰指定初始化器,表明所有子类都必须实现出初始化器,如果子类重写了requierd
初始化器,也必须加上requierd
,不用加override
属性观察器
:
父类的属性在自己的初始化器中赋值不会触发属性观察期,但是在子类的初始化器中赋值会触发属性观察期
可失败初始化器
(不常用):
类,结构体,枚举可以使用init?
定义可失败初始化器
- 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码都停止执行
- 可以用
init!
定义隐式解包的可失败初始化器
反初始化器
-
deinit
叫做反初始化器,类似于OC中的dealloc
方法 - 当类的实例对象被销毁内存时,就会调用实例对象的
deinit
方法 - 父类的
deinit
可以被子类继承 - 子类的
deinit
执行完毕后会调用父类的deinit
16.可选链
使用可选值有时会让人感到有点笨拙,所有的解包和检查会变得如此繁重,以至于会让你想要丢几个感叹号上去强制解包,好让你能继续工作下去。但是请小心:如果你强制解包一个没有值的可选值,你的代码就崩了
func albumReleased(year: Int) -> String{
switch year {
case 2006: return "Taylor Swift"
case 2008: return "Fearless"
case 2010: return "Speak Now"
case 2012: return "Red"
case 2014: return "1989"
default: return nil
}
}
let album = albumReleased(year: 2006)?.uppercased()
print("The album is \(album)")
- 注意这里有个“问号”,这就是可选链:在“问号”后的所有代码,只会在“问号”前的代码有值时,才会运行。
- Swift 将会从左到右检查它们,直到遇到一个 nil 后停止。如下
let album = albumReleased(year: 2006)?.someOptionalValue?.someOtherOptionalValue?.whatever
17.协议
协议可以定义属性,方法, 下标。
协议可以被类,结构体,枚举遵循
protocol YPPhotoCapture: class {
// Public api
func start(with previewView: UIView, completion: @escaping () -> Void)
func stopCamera()
func focus(on point: CGPoint)
func zoom(began: Bool, scale: CGFloat)
func tryToggleFlash()
var hasFlash: Bool { get }
var currentFlashMode: YPFlashMode { get }
func flipCamera(completion: @escaping () -> Void)
func shoot(completion: @escaping (Data) -> Void)
var videoLayer: AVCaptureVideoPreviewLayer! { get set }
var device: AVCaptureDevice{ get }
// Used by Default extension
var previewView: UIView! { get set }
var isCaptureSessionSetup: Bool { get set }
var isPreviewSetup: Bool { get set }
var sessionQueue: DispatchQueue { get }
var session: AVCaptureSession { get }
var output: AVCaptureOutput { get }
var deviceInput: AVCaptureDeviceInput{ get set }
var initVideoZoomFactor: CGFloat { get set }
func configure()
}
协议不指定是否该属性应该是一个存储属性或者计算属性,它只指定所需的属性名称和读写类型。属性要
求总
是声明为变量属性,用var关键字
做前缀要求该属性的读写权限(可读可写或者可读)
mutating关键字
:mutating
关键字放在方法func关键字之前,表明该方法允许修改所属实例的任何属性
@escaping关键字
:逃逸闭包协议SomeProtocol中不光可以声明方法/属性/下标,还可以声明构造器,但在Swift中,除了某些特殊情况外,构造器是不被子类继承的,所以SomeClass中虽然能够保证定义了协议要求的构造器,但不能保证SomeClass的子类中也定义了协议要求的构造器。所以我们需要在实现协议要求的构造器时,使用required关键字确保SomeClass的子类必须也得实现这个构造器。
Optional协议要求只有在你的协议被@objc属性标记时指定。
即使你不与Objective-C交互,如果你希望指定optional要求,你仍然需要使用@objc标记你的协议。
使用@objc标记的协议只能通过类调用
18.Any于AnyObject的区别
- 两者都可以表示OC中的id 任意类型
- AnyObject用于任何类(class)的
实例
,而Any可以用于表示任何类型
,包括基本类型、值类型以及实例 - 应该尽量
多使用泛型少用Any
,避免转换类型是发生的类型错误