Swift笔记
Any AnyObject NSObject
Int
Double
String
struct
都是结构体
-
Any
: 一个协议声明 -
AnyObject
: 一个具体的协议,协议里面没有内容,默认情况下,所有的类,都遵循了这个协议 -
NSObject
:NSObject
类
条件判断
swift中没有非零即真和零为假的说法,只有严格的
Bool(true/false)
1. if的使用
let a = 1
if a == 1 {
print("test")
}else if a == 2{
print("sss")
}else {
print("wwqw")
}
2. 三目运算符
a == 1 ? print("first") : print("hello")
3. guard
guard
条件成立,继续往下走,不成立执行else
里面的语句else
后要跳出语句,配合return
,continue
,break
,throw
等使用
func funcTest() {
let a = 1
guard a == 1 else {
print("a != 1")
return
}
print("a == 1")
}
4. switch的用法
1. switch与基本数据
// 必须要加default, 但是有些情况可以不加,例如枚举的各个情况都判断过了
// 判断类型 可以是浮点型,String,对象。
let a = 11
switch a {
case 1, 11:
print("1, 11")
fallthrough // 会有穿透效果,如果这个case为true,也执行下个case的内容
case 2:
print("2")
default:
print("不知道")
}
2. switch与区间
let score = 10.0
switch score {
case 0 ..< 60:
print("不及格")
case 60 ..< 90:
print("及格")
case 90 ..< 100:
print("优秀")
default:
print("default")
}
3. switch与枚举
enum Direction {
case up
case down
case left
case right
}
let rr = Direction.right
switch rr {
case Direction.up:
print("up")
case Direction.down:
print("down")
case Direction.left:
print("right")
case Direction.right:
print("right")
//default: // 此时可以省略default,因为各个情况都已经判断过,default也不会执行
// print("ss")
}
4. switch与元组
let point = (10, 25)
switch point {
case (0, 0):
print("坐标在原点")
case (1...10, 10...20): // 可以在元组中再加上区间
print("坐标的X和Y在1~10之间")
case (_, 0): // X可以是任意数
print("坐标的X在X轴上")
case (var x, var y): // x,y来接收参数 一定为true
print("333")
case var(x,y): where x > y // x,y来接收参数 x>y则一定为true
print("1111")
default:
print("Other")
}
循环
1. for循环
for i in 0 ..< 10 {
print(i)
}
// _代表忽略的意思
for _ in 0 ..< 10 {
print("test")
}
2. while循环
1. while循环
var i = 10
while i > 0 {
i -= 1
print(i)
}
2. repeat~while循环
// do 特殊含义,捕捉异常
repeat {
i += 1
print(i)
} while i < 10
字符串处理
let str2 = "123"
let int4 = 10
// 字符串拼接
str2 + "\(int4)"
// 遍历字符串
for i in str2.characters {
print(i)
}
str2.lengthOfBytes(using: String.Encoding.utf8)
str2.characters.count
String(format: "%02d", 1)
// 截取字符串
str2.substring(from: str2.startIndex)
str2.substring(to: str2.index(after: str2.startIndex))
str2.substring(from: str2.index(str2.startIndex, offsetBy: 2))
str2.substring(from: str2.index(str2.endIndex, offsetBy: -1))
let range = str2.startIndex ..< str2.endIndex
str2.substring(with: range)
str2.replaceSubrange(range, with: "sss")
// 转为OC的NSString处理
let str3 = (str2 as NSString).substring(to: 2)
数组
// 数组的声明和初始化
let arr1 = [2]
var arr2 : [Any] = [1, 2, "ii", 11, "ss", "w"]
let arr3 = [1, 1.1, "ss"] as [Any]
// 相同类型的数组可以直接相加
arr2 + arr3
// 获取
arr2.first
arr2.last
arr2[0]
// 追加
arr2.append(2.0)
// 修改
arr2[0] = 1
// 插入
arr2.insert("sww", at: 2)
// 删除
arr2.remove(at: 0)
arr2.removeFirst()
arr2.removeFirst(1)
// 操作数组的区间
let rang2 = 0 ..< 2
arr2.removeSubrange(0 ..< 2)
// 获取数组最大最小
var arr = [1, 2]
arr.min()
arr.max()
var arr4 = ["a", "3.3"]
// 比的ASCII码
arr4.min()
arr4.max()
// 数组的遍历
for i in 0 ..< arr2.count {
print(arr2[i])
}
// 利用元组获取数组的 角标+ 值
for (key ,value) in arr2.enumerated() {
print(key, value)
}
for i in arr2[0...2] {
print(i)
}
// 转为OC数组的遍历
(arr2 as NSArray).enumerateObjects({ (value, idx, stop) in
print(value, idx)
})
字典
var dict : [String : Any] = ["key" : 1, "key2" : "value"]
dict["key"] = 2
let index = dict.index(forKey: "key")
// index为nil报错
dict.remove(at: index!)
// 有则改,无则加
dict.updateValue("value2", forKey: "key2")
dict.removeValue(forKey: "key2")
dict.removeAll()
for value in dict.values {
print(value)
}
for (key ,value) in dict {
print(key, value)
}
// 类扩展---字典的相+
extension Dictionary {
static func +(dic : Dictionary, dic2 : Dictionary) -> Dictionary
{
var result = dic
for (key , value) in dic2 {
result[key] = value
}
return result
}
}
元组
// 元组类型 (name: String, Int, score: Int), 可以作为返回值
let yz = (name : "zhangsan", _ : 18, score : 2)
yz.0
yz.name
yz.score
let(name, age, score) = ("zhangsan", 2, 2)
可选类型
1. 非可选类型 (使用的时候必须有值)
let sum : Int
2. 可选类型 (才能赋值为nil)
Swift中的nil
!= OC中的nil
,Swift种的nil
就是一个特殊含义的字符,表示没有值
// let sum0 : Int?
// let sum0 : Optional = 2
let sum1 : Int!
sum = nil
// sum0 (Int?)需要解包才能使用
// sum1 (Int!)赋值后可以直接使用,不用解包
3. 四种方式使用可选类型的值
1. 判断 + 直接解包
if sum != nil {
sum!
}
2. 可选绑定
if let tmp = sum {
tmp
}
3. guard守护
func funcl(tmp : Int?) {
guard let test = tmp else {
return
}
test
}
4. 空合运算符
// 如果sum == nil,那么取 ?? 后面的值
// 如果 sum != nil, 取 sum! 强制解包后的值
let tmp2 = sum ?? 0
类型转换
var a = 8.8
a is Int
a is Double
let str = "123"
str as NSString
str as Any
// as! 代表,肯定可以转换成功,转换的结果,是非可选 不能为nil
// as? 代表,系统尝试帮你进行转换,转失败了,就为nil
函数
1. 函数的四种类型
1. 无参数,无返回值
func func1() {
}
func func2() -> Void {
}
func func3() -> () {
}
2. 无参数,有返回值
func func1() {
}
func func2() -> Int {
return 0
}
// 返回元组
func func3() -> (Int, String) {
return (1, "123")
}
3. 有参数,无返回值
func func1(age : Int) {
}
4. 有参数,有返回值
func func3(age : Int) -> (Int, String) {
return (1, "123")
}
函数其他注意
1. 省略第一个外部参数的名字
// 省略第一个内部参数(函数内部可以使用的参数)
// 从swift3.0开始默认第一个参数既是外部参数(函数调用时可以看到的参数)也是内部参数
func func2(_ name : Int, name2 : Int) -> Int {
return 0
}
2. 设置参数默认值
// 设置默认值,会生成几种组合(带不带第二个参数的)
func func2(name : Int, name2 : Int = 1) -> Int {
return 0
}
3. 设置可变参数
// 可变参数 类型...
// 函数内部,把这个参数,当做数组来处理
// 函数外部,直接可以传递多个值,用逗号隔开
func addNum(nums : Int...) -> Int {
var result = 0
for num in nums {
result += num
}
return result
}
addNum(nums: 1, 2, 3)
4. 修改内部参数的值
// 默认不能修改内部参数的值
func change(num : Int) {
// num 为常亮,不能修改
var num = num
num = 3
}
let a = 0
change(num: a)
5. 设置参数为地址传递
// inout设置第一个参数为地址传递
func func1(name : inout Int, name2 : Int) -> Int {
return 0
}
6. 函数嵌套
func test() {
func test2() {
print("sss")
}
}
7. 函数的类型
// (Int, Int) -> Int
// 函数的类型 : 参数类型 和返回值类型
func add(num : Int, num2 : Int) -> Int{
return num + num2
}
// (Int, Int) -> Int
// 函数的类型 : 参数类型 和返回值类型
func jian(num : Int, num2 : Int) -> Int{
return num - num2
}
func exec(n1 : Int, n2 : Int, fun : (Int, Int) -> Int) {
let result = fun(n1 ,n2)
print(result)
}
exec(n1: 3, n2: 2, fun: add)
exec(n1: 3, n2: 2, fun: jian)
8. 区分不同函数
func test() {
}
// 参数返回值不同
func test() -> Int {
return 0
}
// 参数类型不同
func test(age : Int) -> Int {
return 0
}
// 参数名字不同
func test(_ age : Int) -> Int {
return 0
}
// 参数名字不同
func test(name : Int) -> Int {
return 0
}
枚举
// 在swift里面,枚举类型,默认情况,不表示任何类型,就是一个标识
// 类型首字母大写,元素小写
enum Direction {
case east
case west
}
enum Direction2 {
case east, west
}
enum Direction3 : Int {
case east = 1
case west = 3
case north // Direction3.north.rawValue = 4 会自己累加
case south
}
// 只有后边指定类型才能敲出rawValue
// Direction4.left.rawValue 取出的值就是指定的类型
enum Direction4 : String {
case left = "left"
case right = "right"
case top
case down
func func1() {
print("wwwww")
}
static func func2() {
print("rrrr")
}
}
let rv = Direction3.north.rawValue
let rv2 = Direction3(rawValue: 1)
let rv3 = Direction4(rawValue: "left")
func test(path : Direction4) {
if path == .left {
print(path.rawValue)
}
}
结构体
1. 结构体基本使用
// 类型方法 static func
// 实例方法 func
// 无论是枚举,还是结构体,都可以写方法
struct Point {
// 实例属性
var x : Double
var y : Double
// 实例方法
func distance() -> Double {
return x - y
}
mutating func distance2() -> Double {
x += 2 // 修改实例属性 方法要加mutating
print(Point.z) // 访问类型属性
return x - y
}
// 类型属性
static var z : Double = 0 // 需要初始化
// 类型方法
static func dis() {
// print(x) 不能直接访问x
print(z)
print(Point.z)
}
}
2. 结构体扩充构造函数
struct Point {
// 实例属性
var x : Double
var y : Double
var z : Double?
// 自定义 “构造函数” != 普通函数
// 不加func,必须使用init作为名称
// 在构造函数内部,必须要保证,所有的非可选属性,必须有值
// 如果我们自定义了构造函数,那么系统生成的逐一构造器,就没有了
init(x : Double, y : Double) {
self.x = x
self.y = y
}
init(x : Double, y : Double, z : Double) {
self.x = x
self.y = y
self.z = z
}
}
类
1. 类的声明初始化
// swift类,是可以不继承父类,那它本身就是rootClass
// 可以写属性和方法
// 属性:实例属性,类型属性
// 方法:实例方法,类型方法
// 类,默认情况下,不会生成逐一构造器(目的,保证所有的非可选属性有值)
// 默认情况下,不能保证,所有的非可选属性有值
// 一个实例对象被创建好以后,必须保证里面所有的非可选属性有值
// 方案1:在构造函数中入手,给非可选属性初始化
// 方案2:把非可选 -> 可选
// 方案3:给非可选的属性赋值默认值
class Person {
var age : Int
init(age : Int) {
// 为了不与age参数冲突才使用self
self.age = age
}
init() {
age = 2
}
}
// 不会生成逐一构造器
let p = Person()
let p2 = Person(age: 3)
2. 类的属性和方法
class Person {
// 实例属性 - 存储属性(可以用来存储数值的属性)
var score = 1
var score2 : Int = 0 {
willSet {
score2 // old
newValue // new
}
// willSet(changeName) {
// score2 // old
// changeName // new
// }
didSet {
score2 // new
oldValue // old
}
}
// 实例属性 - 计算属性(并不是直接用来存储数值的,它是通过某些计算得来的数值)
var b : Int {
get {
return score + score2
}
set {
newValue
}
}
// 类型属性
static var c = 1
static var d : Int?
// 实例方法
func func1() {
self.score += 1
}
// 类型方法 - 不可以被子类重写override
static func func2() {
print("rrrr", c)
}
// 类型方法 - 可以被子类重写(结构体不能用class声明方法)
class func func3() {
print("sss")
}
// 结构体不能使用deinit析构函数
// 析构函数只能被定义在class类中,不能在extension中
deinit {
print("类死了")
}
}
3. 类的继承之KVC使用
class Person : NSObject {
var age : Int = 0
var name : String = ""
init(dic : [String : Any]) {
// KVC实现之前,必须调用父类的init方法初始化
super.init()
setValuesForKeys(dic)
}
}
let dic : [String : Any] = ["name" : "zhangsan", "age" : 33]
let stu = Person(dic: dic)
4. 类的循环引用
class Person {
var dog : Dog?
deinit {
print("人挂了")
}
}
class Dog {
// 使用weak来避免循环引用 (unowned也可以)
weak var master : Person?
deinit {
print("挂了")
}
}
var p : Person? = Person()
var d : Dog? = Dog()
p?.dog = d
d?.master = p
p = nil
d = nil
5. 结构体和类的区别
- 结构体有逐一构造器,类没有
- 结构体是值类型,类是引用类型
- 结构体不能继承(意味着没有多态)
6. OC中使用Swift的类和结构体
- OC与Swift混编,Swift中的函数名要符合OC的规范,重载的方法用
@objc
指定名字,避免冲突 - 如果是类,必须要继承自
NSObject
,而且用public
关键字对类、方法、属性等进行修饰 - 如果是协议最好继承自
NSObjectProtocol
(也就对应OC中的基协议),用标识符@objc
,而且声明为public
-
Build Settings
搜索-Swift
,找到Objective-C Generated Interface Header Name
里面的.h文件即为OC调用时,要包含的头文件#import "XXX-Swift.h"
public class Person : NSObject {
public var name : String = ""
public func getAge() {
print("hello age")
}
}
@objc
public protocol work : NSObjectProtocol{
func goWork()
}
7. Swift中调用OC
- Swift项目创建OC文件时回生成.h桥接文件,可以在
Build Settings
的Objective-C Bridging Header
中找到,也可以自己创建.h文件,路径跟系统生成的一致 - 在.h文件包含对应的OC头文件
三大特性
类的三大特性:封装、继承、多态
class Test : NSObject {
// 类继承NSObject后,下面两个方法会报错,说是转换到OC以后两个方法有冲突
func chongzai(a : Int) {
}
func chongzai(a : Double) {
}
}
解决办法:
public class Test : NSObject {
public func chongzai(a : Int) {
}
// 通过此标识,自定义生成的方法名,解决重复冲突
@objc(chongzai:)
public func chongzai(a : Double) {
}
}
// 项目名称为app,弹框选择的是不桥接
// Build Settings搜索-Swift,找到 Objective-C Generated Interface Header Name 里面的.h文件即为要包含的头文件
#import "app-Swift.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Test *t = [[Test alloc] init];
[t chongzaiWithA:(NSInteger)];
}
可选链
class Person {
var dog : Dog?
}
class Dog {
var name : String = "wangcai"
var toy : Toy?
}
class Toy {
var price : Double = 0.0
}
let p = Person()
// 如果可选链的结果是nil,就代表链条中间至少有一个环节断了
// () == Void != nil
p.dog?.toy?.price
协议
1.协议的基本使用
protocol work {
func run()
}
// 枚举也可以遵循协议
enum Direction : work {
case left
case right
// 实例方法
func run() {
print("Direction run")
}
}
Direction.left.run()
// 协议可以继承,这里不叫遵循,遵循work协议是要实现函数的
protocol work2 : NSObjectProtocol{
func run2()
}
// 如果遵循了协议,要求,必须实现协议里的所有方法
// 同时继承 + 遵循协议,不支持多继承
// NSObject 实现了NSObjectProtocol协议的所有方法
class Stu : NSObject, work2 {
func run2() {
print("Stu run2")
}
}
2. 协议中使用代理
-
weak
修饰类,work : class
,轻量级 -
work : NSObjectProtocol
也可以,但是遵循work
协议的类,都要继承要NSObject
了
protocol work : NSObjectProtocol{
func run()
}
//protocol work : class{
// func run()
//}
class Stu {
weak var delegate : work?
func run() {
print("Stu run2")
}
}
3. 协议中的可选
协议中的可选,仅仅是OC的特性,Swift是不支持的
解决方案:就是让Swift协议,拥有OC特性
@objc
protocol work {
@objc optional func run()
}
class Stu : work {
func run() {
print("Stu run2")
}
}
泛型
// 泛化的类型,不是某一个具体的类型, 中的T可以自定义名字
func exchange(num1 : inout T, num2 : inout T) {
let tmp = num1
num1 = num2
num2 = tmp
}
class Person {
}
// 限制 T 必须继承自 Person
func exchange2(num1 : inout T, num2 : inout T) -> Int where T : Person {
let tmp = num1
num1 = num2
num2 = tmp
return 0
}
var a = 3
var b = 9
var p1 = Person()
var p2 = Person()
exchange(num1: &a, num2: &b)
// 参数必须继承自 Person
exchange2(num1: &p1, num2: &p2)
闭包
闭包 == 特殊的函数
1.闭包的基本使用
// 函数
func add(num1 : Int, num2 : Int) -> Int {
return num1 + num2
}
// 简单的闭包
// 如果闭包,参数是没有的,可以省略,in和in前面的内容
var bibao : ()->() = {
// ()->() in // 可以省略
}
// 带参数的闭包
var bibao2 : (Int, Int)->(Int) = {
// (varg1, varg2) in
(varg1 : Int, varg2 : Int) in
return varg1 + varg2
}
bibao2(10, 20)
// 闭包当做参数
func exec(n1 : Int, n2 : Int, block : (Int, Int)->(Int)) -> Int {
return block(n1, n2)
}
exec(n1: 20, n2: 30, block: add)
exec(n1: 20, n2: 30, block: bibao2)
exec(n1: 20, n2: 30, block: {
(a : Int, b : Int)->(Int) in
return a * b
})
2. 尾随闭包和逃逸闭包
//尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签(此处是bb)
// 如果一个函数的参数,是一个闭包类型,那么默认情况下,是一个“非逃逸”闭包;(闭包,生命周期,是函数)
func test(a : Int, bb : (Int, Int)->(Int)) {
let result = bb(a, 3)
print(result)
}
test(a: 20) { (sum1, sum2) -> (Int) in
return sum1 - sum2
}
// @escaping : 代表,这个闭包,是逃逸闭包,以后,有可能,会被其他的闭包,延长生命周期(强引用)
func test2(bb : @escaping (Int, Int)->(Int)) {
bb(23, 3)
let queue = DispatchQueue(label: "xx")
let time = DispatchTime.now() + DispatchTimeInterval.seconds(2)
queue.asyncAfter(deadline: time) {
// 此处编译器会提示添加 @escaping
_ = bb(10, 3)
}
}
test2 { (sum, sum2) -> (Int) in
return sum * sum2
}
3. 闭包的循环引用(4中解决方式)
class Person {
class Person {
var resultBlock : (()->())?
var age : Int = 0
func test() {
// weak 对象最后会被置为 nil,所以var
weak var weakSelf = self
resultBlock = {
print("222",weakSelf?.age)
}
resultBlock?()
}
func test2() {
resultBlock = {
[weak self] in
print(self?.age)
}
resultBlock?()
}
func test3() {
// unowned == __unsafe_unretained 最后不会置为nil,编译器建议为let
unowned let weakSelf = self
resultBlock = {
print(weakSelf.age)
}
resultBlock?()
}
func test4() {
resultBlock = {
[unowned self] in
print(self.age)
}
resultBlock?()
}
deinit {
print("人被释放了")
}
}
var p : Person? = Person()
p?.test()
p = nil
懒加载
只是在第一次访问的时候,会调用相应的函数,获取实例,下次即使值为
nil
,也不会再次调用相应的函数,获取新的实例
class Dog {
var name : String = "wangcai"
init() {
print("创建了小狗")
}
}
// 懒加载
// 函数:构造函数,一般的函数,闭包
// = 后面可以跟的值:具体的指,构造“函数”
// 所谓的懒加载,是指,在用的时候,再通过后面的函数,获取相应的实例
class Person {
lazy var dog : Dog = Dog()
// 这样的懒加载可以对创建的对象进行修改
lazy var dog2 : Dog = Person.getDog()
// 懒加载内容放到闭包,最后调用闭包
lazy var dog3 : Dog = {
let d = Dog()
d.name = "dog3"
return d
}()
lazy var dog4 : Dog = {
$0.name = "dog4"
return $0
}(Dog())
static func getDog() -> Dog {
let d = Dog()
d.name = "getDog"
return d
}
}
注释
// MARK: - ARC
// TODO: - todo
// FIXME: 解决bug
访问权限
- Swift中的访问控制模型基于模块和源文件、类这三个概念
- Swift访问权限,作用于类,属性,方法等
- Swift中的访问级别遵循一个基本原则:不可以在某个实体中定义访问级别更高的实体(类如果都不能访问,里面的属性方法就算开放了也不能访问)
访问修饰符
-
internal
: 在本模块中都可以访问,(默认),子类也可以继承 -
private
: 当前类私有 -
fileprivate
: 在当前源文件中可以访问 -
public
: 跨模块时,如果修饰类,则无法继承。修饰方法,不能被override
-
open
: 跨模块时,如果修饰类,可以继承。修饰方法,可以被override
方法抛出异常
// Error 就是在告诉编译器,这个枚举,可以充当具体的异常值
enum FileError : Error{
case notExists
case notFormat
case notContent
}
// path 不存在 nil
// path存在,但是,路径对应的文件格式不对 .png
func readFile(path : String) throws -> String {
// 1. 判断文件路径是否存在
let isExists = FileManager.default.fileExists(atPath: path)
if !isExists {
// 在这里面,抛出,出现问题的原因
// 如果想要成为,异常值,必须要遵循一个协议Error
throw FileError.notExists
// return
}
// 2. 读取文件内容
// 判定,如果这个构造函数,出现了异常,一般都是格式不正确
var content : String = ""
do {
content = try String(contentsOfFile: path)
} catch {
// 捕捉到异常,会执行这个闭包
throw FileError.notFormat
}
if content.lengthOfBytes(using: String.Encoding.utf8) == 0 {
throw FileError.notContent
}
return content
}
Playground
1. Playground异步执行
// Playground中的代码会从上到下执行,并在执行完毕之后立即停止,如果想要测试异步处理(比如网络请求)
// 1. 导入PlaygroundSupport
// import PlaygroundSupport
// 2. 让Playground永远执行
//PlaygroundPage.current.needsIndefiniteExecution = true
// 3. 停止执行
// PlaygroundPage.current.finishExecution()
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let queue = DispatchQueue(label: "xx")
let time = DispatchTime.now() + DispatchTimeInterval.seconds(2)
queue.asyncAfter(deadline: time) {
print("finish")
PlaygroundPage.current.finishExecution()
}
print("first")
2. MarkDown语法
从Xcode右边文件属性,选中Render Documentation看渲染效果
//: [Previous](@previous)
//: [Next](@next)
//PageName为页名字不能有空格
//: [Go to AnyPage](PageName)
3. TimeLine使用
点击右上角双环(Show the Assistant editor),在代码中做的动画可以在TimeLine中预览
4. Playground的Sources目录
- 放到Sources目录下的源文件会被编译成模块(module)并自动导入到Playground中,只会编译一次
- 使用注意:需要使用
public
关键字修饰资源文建中,需要暴露给外界的内容