Swift5.1 https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html
1、swift没有隐式转化
Swift中没有隐式转化,不会将整形自动转成浮点型
let m = 32
let n = 3.14
let result = m + n 错误写法
2、省略; 省的是变量后的类型,可以类型推导
3、类型推导 option+鼠标左键
4、
//1、if的使用
//if后的()可略
//判断句不再有非0即真.必须有明确bool值 true/false
5、guard的用法
07示例代码写的什么玩意
guard只能用在函数中,不能像if一样用在函数外;和if用法相似,后边都加条件表达式;和else连用,有guard必会带else;与if不同的是紧跟if的大括号是条件语句成立时,而guard后紧跟的大括号里的大括号是条件语句不成立才走,条件语句成立时则走大括号外边;guard最近的大括号里需要和return,break,continue,throw联用
6、08-switch特出用法
Switch
Switch后的小括号可以省
默认每个case是包含break,如果想case穿透用fallthrough
default不能写在最上边,只能写在最下边
switch 后边的变量不仅可以是整型,也可以是double,字符串,对象类型;
可以用区间 let range = 0…10 [] 可计数 0.01…0.5 不可计数 0..<10 [) 可计数才能遍历;元组做条件;where;case后可以写两个;
//fallthrough case穿透
//case后跟多个条件
//不可以不写default
//default不能随便放
//能判断对象,浮点型,字符串等
元组;
区间;
//switch
//一、区间匹配
//二、元组匹配 即一对数据
//三、值绑定
//四、根据条件绑定,where,只有where后的条件表达式为真才赋值并执行case后的语句
7、Swift下划线的作用:_忽略 https://www.jianshu.com/p/fcdfc8306a94
//1、格式化数字,增加阅读性
//2、忽略元组的元素值;使用元组时,如果有元素不需要使用,可以使用下划线将元素忽略
//3、忽略区间值
//4、忽略外部参数名
//1》忽略外部参数名
//方法(类或实例)中,方法的第二个参数名及后续参数名,默认既是内部参数名又是外部参数名,如果不想提供外部参数名,可以在参数前加下划线来忽略外部参数名
//2》忽略具有默认值的参数的外部参数名
//当函数的参数有默认值时,swift自动为参数提供与参数名一致的默认外部参数名,因此在进行参数调用时,要提供默认参数名,可以使用下滑线进行忽略默认外部参数名
//从for循环开始,用qq课堂的小码哥视频,视频代码地址:/Users/yangyangzi/Desktop/yangyangzi/2019/swift(五部分)
11、while循环:
While i < 10 {
}
pepeat{
}while i < 10
repeat {
i += 1
print(i)
} while i < 10
12、字符串基本使用
string不是类而是结构体,性能更高,没有存储地址类似于int等,是一个值类型而oc的nsstring为指针类型
类,存储的字符串在堆里,有寻址操作
let len = str.lengthOfBytes(using: String.Encoding.utf8)
let ff = str.lengthOfBytes(using: .utf8)//枚举前边的类型是可以省略掉
1》字符串拼接
字符串间拼接:+ ; "\()"
字符串和其它进行拼接,例如和数字:+ ; "\()" ; String()
字符串格式化:String(format:"% “)和oc的%拼接相似
2》字符串长度
字符串长度:str.count
字符串字节长度:str.lengthOfBytes(using: .utf8)
13、
Oc有可变和不可变string
Swift的字符串可变就是var,不可变就是let;let 和 var相当于有无const
字符串截取:注index类型不是int了变成String.index了
//替换字符串会改变原字符串
//截取不会改变原字符串
//把字符串换成let看那个报错就说明此处会改变原字符串
15、
1》创建数组时设立某元素的重复次数
2》count与capacity不是同一概念,数值不一定相同
3》遍历数组:
便利索引而遍历元素
遍历元素:for value in arr {
数组迭代器::相当于遍历元组组成的数组 for (i,v) in arr.enumerated() { //arr.enumerated()相当于遍历元组组成的数组
遍历某区间元素:for v in arr[1...3] {
4》数组间操作:数组的合并
数组的加(注意:不存在减法);
数组的价只能在数组元素类型相同的情况才可相加;
//一、count与capacity
arr2.count//数组真正的元素个数
arr2.capacity//数组的容量,一般情况是2的个数,如果在存元素时超过了容量,容量值capacity就会 * 2(翻倍)
//二、遍历
//便利索引而遍历元素
//遍历元素
//迭代器;相当于遍历元组组成的数组:[(1,””),(2,””)]
//区间;arr[1...3]
//三、数组间操作
加法://arr + arr3 //不同种类型不能件单加,都变成Any类型即可相加;数组间没有系统减法
16、
17、
18、可选类型
1》oc与swift的nil不同,oc的nil指的空指针特殊的地址;swift的nil就是一个特殊含义的字符,表示没有值
2》swift中只有可选类型才能赋值为nil,nil是个特殊的字符
3》可选代表的含义是有可能有值也可能没有值;非可选代表无论在什么时候,在哪里都有值
let num:Optional<Int> = 2//num的是Optional类型的,不是Int
var num2:Int? = 2//num2是可选类型,此可选类型里包装了Int类型;?说明是可选类型
var num3:Int! = 2//num3是可选类型,此可选类型里包装了Int类型;!说明是可选类型;
//加?和!都表示是可选类型,?和!的区别:
//?表示可选类型:在任何时候有可能有值也有可能没值
//!标示可选类型:能保证在用到时候不为nil
/**
1、何为可选类型?
一个变量要么有值,要么没值
2、为何产生可选类型?
比如:oc中基本数据类型,如果不赋值,都会存在默认值;所以不会产生没有值的情况
但是:没有值和默认值是完全不同的两概念
所以在swift中引入了可选类型
区分了“有值”和“没有值”两种状态
*/
var n:Int = 0//n是一个非可选值
//n = nil //❌ 因为只有可选类型才能赋值为nil
n += 1
var m:Int? = 0
m = nil//m可以为nil,因为m为可选类型
//print(m) //报错
//m += 1 optional类型不能直接运算
m! += 1
var p:Int! = 0
//p += 1
p = nil;
//p += 1;
var q:Int? = 0
q = 1//可选类型可以直接赋值 ***
q
// ! :一个含义:强制解包
// ? 表示可选类型(包装了其它类型) 例如:Int?表示包装了Int类型;String?表示包装了String类型
//================== !也可以表示可选类型
var a:Int! = 12
print(a)//能正常打印
a = nil
print(a)//报错:原因:上边提到的?和!表示可选类型的区别即!表示可选类型必须能保证在用是时候有值,不用再判断ji
a = 1
print(a)//这时候又能正常打印了,因为上行赋值了,满足!表示可选类型的要求
//想要使用可选类型的值需要怎么做:1、直接判断+强制解包;2、可选绑定;3、guard守护 4、使用空合运算符??
//1、直接判断+强制解包
if q != nil {
var r = q!
r += 1
}
//2、可选绑定
//首先判断 q是否为nil,如果是,那么 if 后面的值为false
//如果发现q != nil,if后面的值为true,还会把q强制解包,并把解包后的值,赋值给result
//
if let r = q {
r + 1
}
//3、guard守护(注意guard只能用在函数里边,因此此处要写个函数)
func test(num:Int?){
guard let r = num else {
return
}
r + 1
}
//4、使用空合运算符??
var a:Int?
var b = a ?? 2 //如果a为nil则赋默认值为2
19、类型转换
/**
is :用于判断一个实例是否是一种类型
as :将实例转化为某一种类型,比如子类转父类(即很合理正常的类型间的转换)
as? :将某个类型转换为可选类型,通过判断可选类型是否有值,来决定是否转化成功
as! :将某个类型转化成具体的类型,但是注意:如过不是该类型,会崩溃,所以尽量少用
*/
// is ========
var a = 8.8
let bool = a is Int
// as ========
var sdd:String = "123"
sdd as String
// as! ======== 肯定可以转化成功,转换的结果是非可选类型 不能为nil
var str:Any = "1d24"
let ss = str as! String
ss + "adb"
// as? ====== 系统尝试进行转换,如果转换失败则为nil,转换结果是可选类型(这里用的是直接判断+强制解包)
let sadde = str as? String
if sadde != nil {
let cfg = sadde! + "ssd"
print(cfg)
}
20、空合运算符
//空合运算符用法
//可选类型的变量 ?? “为空时的代替值”
//代表,如果可选类型为nil,则使用默认值,如果有值,直接解包使用
//类似于使用三目运算符判断一个变量是否为空
//空合运算符 ??
//如果a可选类型的值为nil,那么,取??后面的值,如果,a不等于nil,则a取强制解包后的值
所以使用可选类型现在变成了五种处理方法如下:
//使用可选类型的值
//直接判断+强制解包
//可选绑定
//guard守护
//空合运算符给默认值(处理可选类型为nil的情况,即为nil给个默认值)
第二天
1、函数的基本使用
//有参,有返回值
//无参,无返回
//有参,无返回
//无参,有返回
//特例,返回多个参数(即返回一个元组)func five() ->(String,Int,Double{}
//函数 类型方法:类调用的方法,用static修饰的方法,而且不能直接写在外边
//函数 实例方法:对象调用的方法
2、函数使用注意一
//一:内部参数;外部参数 函数
//内部参数:在内部能看到能用的参数
//外部参数:在外边看到的参数叫外部参数
//3.0开始,第一个参数开始,既是内部又是外部
func one(num1:Int,num2:Int)//num1和num2都是内部和外部参数
func two(_ num1:Int,_ num2:Int)//_ 省略外部参数,num1 num2 是内部参数
func three(as num1:Int,cc num2:Int)//as cc 外部参数 //num1 num2 内存参数
//二、默认参数函数
//必填参数和可填参数混合
//作用:可以写兼容性很强的接口
func four(num1:Int,dis:Int = 0,disd:Int = 0) -> Int{} //num1必填 dis为可填 disd为可填
//三、可变参数函数
//可变参数类型: 类型...
//函数内部当数组处理
//函数外部,直接可以穿多个值
func five(nums:Int...) -> Int
func six(_ nums:Int...) -> Int
3、函数使用注意二
//1、函数内部,默认接收到的参数被赋值给一个常量,在swift3.0中,如果想改变,需要在函数内部,使用var修饰
func changeNum(a:Int){ var a = a a = 666}// 使用var进行修饰,尽量少用这种方式 ;注:这里是值传递
//2、将值传递转换为指针传递 inout关键字 //inout 地址
func oneOne(a: inout Int) 调用传地址 oneOne(a: &b)
//3、函数嵌套(oc是不能进行函数嵌套的)
func testA(){
print("a")
func testB(){
print("b")
}
testB()
print("c")
}
4、函数的类型
//函数是有类型的
//函数类型:由参数类型和返回值类型共同组成
查看函数类型:
func add(a:Int,b:Int) ->Int {
return a + b
}
let a = add//选中a,option+左键看函数类型为 (Int,Int) -> Int
//1、函数是有类型的,函数作为另一个函数的参数
func execute(num1:Int,num2:Int,fun:(Int,Int) -> Int){
fun(num1,num2)
}
execute(num1: 9, num2: 3, fun: cheng)
//2、函数是有类型的,函数作为另一个函数的返回值
func getFun(ff:String) -> (Int,Int)->Int{
if ff == "add" {
return add
}
return cheng
}
//系统如何区分不同的函数
//1、靠函数名称
//2、靠外部参数名称
//3、参数的类型 + 返回值类型 = 函数类型
//swift支持函数重载,而oc不支持,因为swift可以通过函数类型区分不同函数
5、枚举
Oc的枚举
//NS_ENUM表示状态,同一时刻只能取一个
typedef NS_ENUM(NSUInteger, Direction) {
DirectionA,
DirectionB,
DirectionC,
};
//NS_OPTIONS表示选项,同一时刻能取多个
typedef NS_OPTIONS(NSUInteger, DirectionDirection) {
DirectionDirectionA = 1 << 0,
DirectionDirectionB = 1 << 1,
DirectionDirectionC = 1 << 2,
};
//oc的枚举值只能是整型值
//swift中枚举值可以是字符串、字符、整型值、浮点值;swift枚举类型,默认不表示任何类型,就是一个标识;如果想绑定原始数据,需要在枚举定义时设置,并指定枚举类型
//枚举定义:如下;
//注意:开头字母小写
enum Direction{//此枚举值是没有类型的,默认不表示任何类型
case east
case sourth
case west
case north
}
enum DirectionTwo: Int{case east = 1 case sourth}//此枚举是Int类型的
enum DirectionThree: String{case east = "east" case sourth = "sourth"}//此枚举是String类型的
//枚举的元素值rawvalue
//枚举值-》原始值
//原始值-》枚举值
let z = DirectionThree(rawValue: "east")//z是可选类型
//枚举中可以写方法
//实例方法
//类型方法
6、结构体
是由一组相同或不同类型的数据组成的集合,是值类型,值传递
//实例属性
//类型属性,必须赋值
//实例方法,调实例属性和类型属性
//类型方法,调类型属性
7、结构体构造函数扩充
扩充构造函数
init,不能func
一旦扩充(无论扩充的是几个参数的构造器),结构体存在的系统自带的 逐一构造器,就没了
****** 一定要注意:在构造体内部,一定要保证,所有非可选属性,必须被初始化 ******
****** 且在调用构造器时,一定要保证所有非可选属性,必须有值 ******
//结构体中系统默认的构造函数,此构造函数默认以所有非可选类型为构造器参数,保证了所有非可选类型在结构体初始化后都有值,即 也叫 逐一构造器
//逐一构造器:在构造体内部,才能保证,所有非可选属性,必须被初始化
8、结构体函数的扩充
Swif叫类型扩展
Oc叫类扩展
//swift的static类型属性和java类似,在内存中是唯一的,是该类所有实例对象所共享的存储单元
(自总结)
mutating func shiliFunc() {//在实例方法中是不能修改实例属性值个,除非在实例函数前加mutating关键字,把self可以改变
print("在x轴上移动了",x+=1)
}
//类型扩展
//扩充系统结构体进行扩展
//oc中扩展只能用于类(int等非类的类型就不支持扩展了)
//swift中的扩展可以用于任何类型,比oc中的扩展范围广
extension CGPoint{
mutating func shiliFunc(xD:CGFloat){
self.x += xD
}
}
var pc = CGPoint(x: 3, y: 4)
pc.shiliFunc(xD: 3)
9、类的基本使用
//一、类的非可选属性注意点 =====================
//逐一构造器:保证非可选有值
/**
类的非可选属性注意点:
类和结构体看似一样,但结构体有 逐一构造器,而类没有逐一构造器;但是类和结构体一样在实例对象被创建好之后,保证,里面所有的非可选属性,必须有值,因此可以通过以下方法打到目的
1、把所有的非可选,改成可选
2、在构造函数里面,给所有非可选属性赋值
3、给非可选属性,赋值默认值
*/
// 属性:对象属性,类属性
// 方法:对象方法,类方法
//二、类可以不继承其他类,他本身就是根类 =====================
//如果,想用NSObject里的方法,则需要继承NSObject
10、字典转模型
kvc
和oc差不多用模型类
setValuesForKeys(dic)
override func setValue(_ value: Any?, forUndefinedKey key: String) { }//防止不存在的数据导致崩溃
11、类的析构函数,deinit相当于dealloc
deinit {//相当于oc的delloc方法
print("对象被释放了")
}
var p:Person? = Person()
p = nil
12、类的属性和方法
/**
属性:实例属性: 存储属性
计算属性(重写get方法)
类型属性: (static修饰的)
方法:实例方法: (可以被重写)
static类方法:(不能被重写)
class类方法: (可以被重写)
*/
//属性===========================
// 存储属性
//计算属性:需要重写get方法;不用重写set方法
//类型属性 static修饰
//方法===========================
//实例方法(可以被重写)
//类方法(类型方法,不能被子类重写) static
//类方法(能被子类重写)class
14、监听属性改变
/**
oc中通过重写set方法监听属性改变
swift中可以通过属性观察者来监听和响应属性值的变化
通常是监听存储属性和类属性的改变 willSet方法(newValue) didSet方法(oldValue)
计算属性不需要定义属性观察者,可以在计算属性的get方法中监听属性变化
*/
15、swift中自动引用计数
weak
//swift是ARC,没有MRC一说;
//swift循环引用
16、三大特性
1》注意swift的重载,当根类继承自NSObject,则重载的方法可能会报错,因为继承自NSObject后,说明这个类有可能和oc进行混编,而oc是不支持重载的某些情况例如:方法名相同+参数个数相同 + 参数类型不同+返回值相同的情况,此时需要在此重载的函数上加 @(newMethodName:) 即在混编为oc时此方法变为newMethodName:
// 重载
func chongzai(a:Int) -> Int {//1
return a
}
@objc(chongzaiTwo:)
func chongzai(a:Int) -> Double {//返回值不同 2
return 0.2
}
@objc(chongzaiThree:)
func chongzai(a:Double) -> Int {//参数类型不同 3
return 1
}
@objc(chongzaiFour::)
func chongzai(a:Int,b:Int) -> Int {//参数个数不同 4
return 1
}
//根类Person继承自NSObject后,说明此类及此类相关类以后可能会混编成oc,故2和3都要加通过@objc()为函数添加别名,注意参数,4处没必要加,这里只是看下俩参数的情况是两个冒号
2》注意swift的重载,当两个重载的函数 函数名相同+参数数目相同+参数类型相同+参数名相同+返回值类型不同的情况,在调用这种类型的函数时,一定要标明变量的类型
let b:Double = s1.chongzai(a: 2)//需要加类型 :Double
let c:Int = s1.chongzai(a: 3)//需要加类型 :Int
16、三大特性-多态
17、结构体和类的区别
1》结构体有逐一构造器,类没有
结构体和类都有static类型方法,结构体没有class类方法而类有;
2》结构体是值类型,类是引用类型
3》结构体不能继承(即没有多态)
枚举结构体都可以遵守协议,但不能继承
18、Any,NSObject,AnyObject区别
/**
public typealias Any = protocol<> //一个协议声明
@objc public protocol AnyObject{} //一个具体的协议,协议里面没有内容;默认情况下,所有的类,都遵守这个协议;注意所有的类
open class NSObject : NSObjectProtocol //所有继承自NSObject的类及NSObject类
范围从大到小:Any > AnyObject > NSObject
*/
//注意Int,Double,String在swift中都是结构体类型
20、可选链的使用和练习
//以下就是一个可选链,即在包含有多个可选类型的表达式中的任意一个可选类型的值为nil,则会导致整个表达式为nil
//反之,如果可选链的结果是nil,就代表,调用失败(说明链条中至少有一环节,断了,即表达式中的任意一个可选类型的值为nil)
// () == Void != nil void类型的函数
let res = per.dog?.toy?.price()
21、APP讲解
架构:
MVVM + RAC
MVP
VIPPER
组件化
数据结构:二叉树
第三天
05、协议的基本使用
/**
swift的类,枚举,结构体都能遵守协议;oc只能类遵守协议
*/
//1=========类遵守协议
swift中的协议被遵守后,协议方法必须实现
class Graduate:Animal,Work{//继承父类,并遵守协议,swift的类不支持多继承
//2============枚举遵守协议
//3============结构体遵守协议
//5============协议之间的继承
protocol momanwork:Work {
//类中使用协议方法:一般设置代理使用
//枚举中使用协议方法:直接用枚举中的实例调用 One.left.run()
//结构体中使用协议方法:直接用结构体的实例调用 let tw:Two = Two() tw.run()
06、协议中代理的使用
//协议在类中的使用:(注:05中介绍了枚举和结构体中协议的使用,而类以代理的方式使用协议)
delegate用weak修饰防止死循环:
// 1、这样写会造成死循环,故要加weak修饰
// 2、加weak修饰后报错(weak只能修饰类): 'weak' must not be applied to non-class-bound 'Work'; consider adding a protocol conformance that has a class bound
// 3、weak解决方案一:让Work继承自class类
// 4、weak解决方案二:让Work继承自NSObjectProtocol协议,由于swift中必须实现协议的所有方法,因为NSObject已经遵守了NSObjectProtocol协议,因此让遵守此协议的所有类都继承自NSObject类即可
// 还是觉得方案一比较好
07、协议中的可选
//协议的可选,仅仅是oc的特性,swift不支持
//解决方案:让swift协议,拥有oc特性
@objc
protocol Work{
@objc optional func work()
}
08、泛型
Swift强类型语言,必须类型匹配
/**
泛型:泛泛的类型,不是一个具体的类型
一旦函数内部,确定泛型的具体类型,那么所有的泛型都是该类型
*/
//应改为地址交换
func exchangeNum(a: inout Int,b:inout Int){
let temp = a
a = b
b = temp
}
var a1 = 1212
var b1 = 3223
exchangeNum(a: &a1, b: &b1)
func exchangeFanXing<T>(a:inout T,b:inout T){//一般用T表示泛型//一般用T表示泛型,写其他任意的代号都行
let temp = a
a = b
b = temp
}
//var dd = 1.3
//var ff = 3
//exchangeFanXing(a: &dd, b: &ff)//a和b类型不一致时就会报错,因为使用泛型时,当第一个泛型类型的值的类型确定后,则所有使用此泛型类型的值都和第一个值相同;即当此处第一个参数的类型为1.3,则默认后边的b也为Double类型,故不能传其他类型的值
08、泛型二
/**
泛型的使用
1、与结构体集合
struct Point<T>{
var x:T
var y:T
}
2、与类结合
class Stack<T>{
var nums:[T] = []
var nud:Array<String> = []
func push(value:T) {
print("进栈")
nums.append(value)
}
func pop() -> T{
print("出栈")
return nums.removeLast()
}
}
3、与协议结合
protocol work{
associatedtype T
func eat() -> T
func run() -> T
}
public protocol View : _View {
associatedtype Body : View
var body: Self.Body { get }
}
SwiftUI 的 View 实现成协议,使用 associatedtype 关联了一个 Body 类型。根据 Swift 的语法,带有 associatedtype 的协议不能直接返回,只能作为类型约束。
class Person:work{
func eat() -> Person {
print("人吃饭")
return self
}
func run() -> Person {
print("人跑步")
return self
}
}
class Dog:work{
func eat() -> Dog {
print("🐶吃肉")
return self
}
func run() -> Dog {
print("狗跑跑")
return self
}
}
Person().eat().eat().run().run() 链式语法
Dog().eat().run().run().run().eat() 链式语法
*/
/**
泛型与where语句结合
*/
func test<T>(a:T) where T:Person {
print("where与泛型")
}
test(a: person)
//test(a: Dog()) //❎
09、闭包的基本使用
//闭包 = 特殊函数
//1、无参闭包
var bibaoOne:()->() = {
}
bibaoOne()//闭包调用
var bibaoTwo:()->(Int) = {
return 10
}
let aa = bibaoTwo()//闭包调用
//2、有参闭包
var bibaoThree:(Int)->(Int) = {
(a:Int)->(Int) in
return 23
}
let b = bibaoThree//此表达式只是查看类型,并非闭包的调用(Int) -> (Int)
let bb = bibaoThree(2)//闭包的调用
/**
无参闭包 不用写 in及in前边的东西
有参闭包 要写 in及in前的东西
(a:Int,b:Int)->(Int) in 或 (a,b)->(Int) in 等价
*/
//1、当函数使用:上边都是直接当参数的例子
//2、当参数使用:
func exec(a:Int,b:Int,block:(Int,Int)->(Int)) -> Int{
return block(a,b)
}
exec(a: 10, b: 3, block: bibaoFour)//qqq
exec(a: 2, b: 9) {//ooo 尾随闭包
(m,n)->(Int) in//(m:Int,n:Int)->(Int) in
return m + n
}
//闭包的{}是闭包的具体实现
//qqq中的bibaoFour相当于ooo中的{}及其内部内容
//非尾随闭包,下边细讲
func execOne(a:Int,block:(Int,Int)->(Int),b:Int) -> Int{
return block(a,b)
}
execOne(a: 2, block: { (a, b) -> (Int) in
}, b: 4)
10、参数闭包-尾随闭包和逃逸闭包
尾随闭包:
/**
闭包做参数时,分尾随闭包和非尾随闭包
尾随闭包:在函数中做参数时,是最后一个参数
非尾随闭包:在函数中做参数时,非最后一个参数
*/
另外 Swift 中,假如函数调用中只有一个参数,并且这个参数是个 closure(闭包),可以省略函数调用的圆括号,例如
VStack {
Text("Hello World")
Text("Hello SwiftUI")
Text("Hello Friends")
}
实际上也是调用了 VStack 的 init 函数。注意 VStack 的 init 函数,某些参数有默认值,因而某些参数可以省略不写
同理
ForEach(romes) { rom in
xxx
}
这种语法也是调用了 ForEach 的 init 函数,生成一个 ForEach 对象,ForEach 也是个 View。
//设计函数时,闭包尽量放在最后
逃逸闭包:
/**
oc里
block是对象
在block中使用强指针,会额外的生成一个该对象的强引用,闭包也是
*/
//逃逸闭包==========================
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
使闭包逃逸出函数的方法:使闭包被函数外部的某个变量持有即可。https://www.jianshu.com/p/d386392fa8c0
/**
如果一个函数的参数,是一个闭包类型,那默认情况此闭包是”非逃逸闭包“,此闭包的生命周期是函数的生命周期,即其生命在函数的右大括号结束
在闭包前加 @escaping
*/
11、在10里写了
12、闭包的循环引用及解决方案
oc:weak与unsafe_unretained 的区别
Weak在指针置为nil时,直接释放地址,地址变为0x00;而unsafe_unretained不会释放释放地址,还是以前的地址,所有后者会造成野指针错误
weak
unowned
[self weak];
/**
通过组一组二比较:
了解闭包做属性即非可选闭包做属性,闭包属性的类型和闭包属性返回值的类型
闭包为可选类型,则闭包的返回值也为可选类型
可选型闭包做属性的比较非可选型闭包做属性
闭包循环引用的处理方法:
循环引用原因:强指针在被block内部调用时,都会生成一个强引用,早成循环引用,因此block或闭包内需要引用的强指针都要弱化
1> 闭包外使用weak weak var weakSelf = self 注意此方式闭包内的weakSelf是可选类型
2> 闭包外使用unowned unowned let weakSelf = self 注意此方式闭包内的weakSelf是 非 可选类型
3> 闭包内使用[weak self] in 使用self是可选类型 let dd = self//let dd: Person?
4> 闭包内使用[unowned self] in 使用self是非可选类型 let dd = self//let dd: Person
*/
1> 闭包外使用weak
(weakSelf?.num)! + 10//在用weakSelf调用num时,系统会自动加?,原因是:weak修饰的对象,在对象释放后会自动转为nil,因此weak修饰的对象后,此对象会变成可选类型,故系统默认加?
// weakSelf?.num + 10//报错,因为weakSelf是可选类型,其属性num也会是可选类型,用可选类型直接不能和非可选类型直接运算,需要将num转为非可选类型
2> 闭包外使用unowned
//unowned 相当于oc的 __unsafe_unreatined,(注:一般情况当用__unsafe_unreatined有野指针风险)
3>闭包内使用[weak self] in
[weak self] in//相当于把下边的self全部改成weakSelf
self?.num = 1//可选类型self?.num可以直接赋值
self?.num = 1//可选类型self?.num可以直接赋值
(self?.num)! + 2//可选类型self?.num不能直接运算需要解包才行即!,这里没有判断用的是强制解包(有危险)
//(self?.num)! += 3//可选类型self?.num不能直接运算需要解包才行即!,这里没有判断用的是强制解包(有危险)//直接 表达式 += 或-= 等会出问题,要用变量接收下此表达式再进行 += 或-=运算
var ds = (self?.num)!
ds += 3
print(self?.num ?? 0)//空合运算符当self.num为nil时,给默认值0
4>闭包内使用[unowned self] in
[unowned self] in//相当于把下边的self全部改成weakSelf
13、懒加载
lazy var dog:Dog
懒加载方式:
1、构造函数获取实例
2、一般函数获取实例
3、闭包获取实例:一般情况:省略变量名,用$0
懒加载的nil
oc的懒加载,在懒加载属性置为nil后,再次调用此属性,会重新走懒加载逻辑,为此属性赋值
swift的懒加载,只在第一次访问此懒加载属性时,会调用相应的函数,获取实例,下次即使为nil,也不会再次调用相应的函数获取新的实例
swift的注释:
1、文档注释:///;文档注释的快捷键option + cmd + /
2、分块注释:// #pragma -mark-
3、待做://TODO: - 需要接着做
4、待解决bug:// FIXME:解决bug
注意swift中要想3、4两种注释有警告提醒,需要写js脚本,而oc则不用写脚本
14、swift的访问权限
oc的访问权限:
@private:作用范围只能在自身类
@protected:作用范围在自身类和继承自己的子类,什么都不写,默认是此属性
@public:作用范围大,在任何地方
@package:本包内使用,跨包不可以
注意:以上只是用来修饰成员变量;无法修饰方法
.h中@interface中声明的成员变量默认是@protected;
.m中@interface中声明的成员变量和@implementation中声明的变量默认是@private
oc权限标记符的作用域是所有后边的成员变量,除非下边又明确指明权限
oc的权限符只能用于成员变量,不能用于方法
swift中的访问控制模型基于:模块、源文件、类 这三个概念
interval:在本模块中都可以进行访问(默认;子类也可以继承)
private:当前类私有;修饰方法;只能访问当前源文件中的实体,切不能访问扩展中对象(注意Swift中的private和其他语言不太一样,它是基于源文件的,作用范围是整个源文件,如果一个源文件中有两个类,那么一个类可以访问另外一个类的私有成员).
fileprivate:在当前文件私有;修饰方法 可访问当前源文件中实体,包括扩展.
public:如果修饰类,则无法继承;修饰方法
open:如果修饰类,可以继承;修饰方法
同一工程就是一个模块;工程中代码和工程中framework是不同的模块
swift只有module(模块)才需要import
swift权限标记符的作用域只有一行
16、异常处理
//Error
//是一个内容为空的协议,主要是让编辑器知道,是可以表达异常的
//如果用异常则可以不用可选类型,因为一般抛异常会处理这些为nil的场景
//如果想成为异常值,必须遵守Error协议
//如果用到了能抛异常的方法,则需要在do下try尝试捕捉异常,如果try捕捉到异常则执行下边的catch,然后再抛异常
do{
content = try String(contentsOfFile: path)
}catch{
throw FileError.UnAvailable
}
处理异常:
注意:如果用playground测试资源,需要把资源拖进工程
处理方式一:do catch 抛异常,关心异常的具体内容
do {
let content1 = try readFile(path: path)
} catch {
print(error)
}
处理方式二:try? 知道这里可能会抛异常,但是就是不想处理,如果有问题则为nil可选类型,仅仅关心结果
let content2 = try? readFile(path: path)
处理方式三:try! 确信及肯定没有任何异常,任何能导致异常的条件,均不满足
let content3 = try! readFile(path: path)
14、swift工程中调用oc文件:
需要创建桥接文件,将oc文件导入到桥接文件中(此过程将oc代码转变为swift代码),即可在swift中使用
15、oc工程中调用swift文件
在调用oc的文件中导入一个头文件:项目名字-Swift.h
使用注意:1、如果想让Swift类/方法/属性,在oc中使用;需要使用public关键字对类/方法/属性/协议等等进行修饰;swift代码是一个module
2、如果是类,必须继承自NSObject
3、如果是协议,协议必须加@objc,让swift协议拥有oc特性,即让协议的方法可选;并让协议继承NSObjectProtocol
15、playground的高级用法
Quick Look:快速查看运行结果,例如颜色,图片尺寸等
Sources目录:1、在playground中写的代码,会被事实编译,并运行将结果显示出来
2、每次只要修改一个字符,都会重新编译
3、解决方案:放到Sources目录下的源文件会被编译成module并自动导入到playground中,只会编译一次
4、使用注意:需要使用public关键字修饰资源文件中,需要暴露给外界的内容
资源即Resources目录:
1、单一的layground并不是一个完整的APP,所以并没有使用沙盒机制
2、如果在playground中使用某些资源例如图片资源,需要放在Resources目录下
3、Resources分为:
独立资源:放在Resources目录,放到此目录下的资源每个Playground是独立的,可以通过mainBundle访问获取
共享资源:共享资源是用户放在Documents目录下的,通过XCPlaygroundSharedDataDirectoryURL来获取共享资源目录的URL;注意要先导入XCPlayground模块
异步执行:
Playground代码自上而下执行,并在执行完之后立即停止
所以,如果想在playground中测试一些异步处理(比如网络请求)一般情况,就无法实现
解决方案:
1》导入模块 PlaygroundSupport
2》让Playground永远执行
3》在异步让Playground停止执行
//不加这句PlaygroundPage.current.needsIndefiniteExecution = true只打印ooo不打印dd
//加PlaygroundPage.current.needsIndefiniteExecution = true之后,则会走异步方法,打印dd
多页面:把不同代码,放在不同界面;界面间跳转;注:这里的跳转是代码之间不同文件的切换
File-》new-》Playground Page-》Render Documentation 则创建page和打开跳转
正常跳转代码://: [Previous](@previous) //: [Next](@next)
也可以自定义跳转 //: [随便写](页码数注意没有@) //: [Next](@next)
注意:playground支持markdown语法,所以默认是以markdown语法的格式展示//: [Previous](@previous)即为markdown语法
TimeLine:时间轴 xcode10看不见这个选项了
链式编程
综合项目:
6.12
Swift无宏用全局常量
Swift不支持非同种基本数据类型的运算
// itemSize = CGSize(width: width, height: height) //xcode11beta的itemSize属性不好使了,换用estimatedItemSize就好使了,坑死个人
// xcode11的collectionViewcController的cell多了一层contentView
estimatedItemSize = CGSize(width: width, height: height)
6.13
cocoapods:
如果用swift最好在创建podfile时,打开useframeworks选项,第三方以.a的形式加进工程,因为swift是可以import module直接使用的,如果不打开useframeworks选项,则第三方以.a的形式加进工程,而.a需要.h才能使用,所以使用时还需要搞桥接文件,在桥接文件中import 此第三方的.h文件(在swift工程中创建任意的oc文件就会产生桥接文件,在这我们只需要使用桥接文件,此处生成的oc文件直接删除即可)
http://api01.idataapi.cn:8000/hotel/idataapi?city=北京&brandName=希尔顿欢朋&apikey=XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog
UICollectionViewFlowLayout的parepare()方法:
当collectionview,想要使用布局对象,布局里面的cell的时候,会自动调用布局对象的prepare方法
6.14
封装请求及布局文件
viper设计模式
设计模式:开发了很多个项目,总结出来的模式,用起来方便耦合性低
// func loadData() {//版本一
// let manager = AFHTTPSessionManager()
//
// let param:[String:Any] = ["city":"北京","brandName":"希尔顿欢朋","apikey":"XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog"]
//
// manager.get("http://api01.idataapi.cn:8000/hotel/idataapi", parameters: param, progress: nil, success: {
// (task: URLSessionDataTask, responseObj: Any?) -> Void in
//
// print("responseObj:\(String(describing: responseObj))")
// var models = [HomeListModel]()
// if let resonse = responseObj as? [String:Any]{
// if let dicArray = resonse["data"] as? [[String:Any]] {
// for dic in dicArray {
// let model = HomeListModel(dic: dic)
// model.imageUrls = dic["imageUrls"] as? [String]
// models.append(model)
// }
// }
// }
// self.modelsArray = models
//
// }) {
// (task: URLSessionDataTask?, error: Error) -> Void in
//
// print("error:\(error)")
//
// }
//
// }
// func loadData(){//版本二
// let param:[String:Any] = ["city":"北京","brandName":"希尔顿欢朋","apikey":"XzBrYodGPYbqR7cCfXrAm2MrXo6WfTKcxVtZbsgkGyEDwpJbiNdsT5q48eCt5Cog"]
//
// NetworkTool.request(type: .GET, url: "http://api01.idataapi.cn:8000/hotel/idataapi", param: param) { (responseObj:Any?, error:Error?) in
//
// print("responseObj:\(String(describing: responseObj))")
// var models = [HomeListModel]()
// if let resonse = responseObj as? [String:Any]{
// if let dicArray = resonse["data"] as? [[String:Any]] {
// for dic in dicArray {
// let model = HomeListModel(dic: dic)
// model.imageUrls = dic["imageUrls"] as? [String]
// models.append(model)
// }
// }
// self.modelsArray = models
// }
// if let lfail = error {
// print("error:\(lfail)")
// }
//
// }
// }
func loadData(){//版本三
HomeNetworkTool.homeRequest {
(modelsArray : [HomeListModel]?) in
if let newmodelArray = modelsArray {
self.modelsArray = newmodelArray
}
}
}
@available的用法:
@available放在函数(方法),类或者协议前面。表明这些类型适用的平台和操作系统。
6.17
新版的mac10.14.5安装ruby失败,也能正常使用cocoapods
swift中用Masonry
https://dimg04.c-ctrip.com/images/230s13000000v2sl4B9DD_W_1280_853_R5_Q70.jpg
http://dimg04.c-ctrip.com/images/230s13000000v2sl4B9DD_W_1280_853_R5_Q70.jpg
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
不能再通过storyboard得到视图控制器时赋值,说明通过storyboard得到的视图控制器并未初始化。
6.19 模拟器的debug-slow animation减缓动画效果
Xcode11创建的工程(注意用旧xcode创建出来的,用xcode11打开不行),present(push的暂时未知)出来的控制器在看层级图时,能看见将此控制器推出来的之前的控制器,这两个控制器之间有个Transitionview,并且被present出来的控制器包在此Transitionview内
旧版Xcode只有在detailCollecVc.modalPresentationStyle = .custom情况下才会出现此Transitionview,因此我们在做复杂的推出动画时要设置modalPresentationStyle为custom
6.20
colelctionview的坐标转换:
把cell的frame转换到window上,这是UIView的功能
let fromFrame = collectionView.convert(curentCell.frame, to: window)
6.29:Swift函数的return可以省略,直接写返回的数据即可。
7.2:
Attribute
Attribute 是指 @ 字符开头的,类似 @available 这种语法。
Swift 的 Attribute 语法可以放到类型定义或者函数定义的前面,是对类型和函数的一种标记。
Attribute 的原理:
编译 Swift 源代码时,在解析阶段(Prase), 会生成一个抽象语法树(AST,Abstract Syntax Tree)。语法树生成时,所有的 Attribute 统一处理,生成 Attribute 节点。之后在语义分析阶段(semantic analysis),会有可能触发 Attribute 节点,使其对语法树本身产生影响。
不同的 Attribute 对语法树可以有不同的影响。比如 @available 会根据系统对语法树的函数调用进行可行性检查,不修改语法树本身。而 @dynamicMemberLookup,@dynamicCallable 进行检查后,可能会直接修改语法树本身,从而转调某些根据规则命名好的类或者函数。
Attribute 是种元编程(Metaprogramming)手段,Attribute 语法会被编译成语法树节点,而 Attribute 又可以反过来修改语法树本身。在类定义或函数定义前添加不同的 Attribute,可以不同的方式修改语法树,从而实现某些常规方式难以实现的语法。其实很好理解,既然都可以修改语法树了,自然就可以通过 Attribute 实现神奇的语法。
假如修改 Swift 的源码,可以根据不同的场合,很容易添加自定义 Attribute。比如 @UIApplicationMain 就是一个自定义 Attribute 扩展,为语法树添加了入口 main 函数。因而用 swift 写 iOS App, 是不用自己写 main 函数的。
12.15:
逃逸闭包、自动闭包 https://www.jianshu.com/p/d386392fa8c0
闭包是引用类型
无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用 incrementByTen和incrementBySeven 是一个常量,而并非闭包内容本身。
将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self(即调用属性要用self.的格式)普通的闭包则不用不用self.的形式调用属性,即可以隐式的调用属性.
传递到 someFunctionWithNonescapingClosure(:) 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用 self
@noescape @escaping @autoclosurehttps://www.ucloud.cn/yun/16463.html
非逃逸闭包:@noescape:函数结束后,这个闭包的生命周期也将结束;
当闭包作为参数传递进函数时,如果这个闭包只在函数中被使用,则开发者可以将这个闭包声明成非逃逸的,即告诉系统当此函数结束后,这个闭包的生命周期也将结束,这样做的好处是可以提高代码性能,将闭包声明成非逃逸的类型使用@noescape关键字。
(1) 默认,swift 3.0 弃用,函数结束后,这个闭包的生命周期也将结束。
(2) 在其内部如果需要使用self这个关键字,self可以被省略
逃逸闭包:@escaping 逃逸的闭包常用于异步的操作,这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用;
例如这个闭包是异步处理一个网络请求,只有当请求结束后,闭包的生命周期才结束。当闭包作为函数的参数传入时,很有可能这个闭包在函数返回之后才会被执行。
自动闭包:@autoclosure 默认非逃逸;闭包也可以被自动的生成;不接受任何参数;自动闭包能够延迟求值;
(1)默认非逃逸
(2)闭包也可以被自动的生成,这种闭包被称为自动闭包,自动闭包自动将表达式封装成闭包。
(3)自动闭包不接收任何参数,被调用时会返回被包装在其中的表达式的值。
(4)当闭包作为函数参数时,可以将参数标记 @autoclosure 来接收自动闭包。
(5)自动闭包能够延迟求值,因为代码段不会被执行直到你调用这个闭包。
(6)自动闭包默认是非逃逸的,如果要使用逃逸的闭包,需要手动声明: @autoclosure @escaping 旧版本:@autoclosure(escaping)
12.18
https://www.cnblogs.com/comsokey/p/Swift1.html
自定义运算符:/ = - + * % < >!& | ^。~
前置运算符 prefix
中间运算符 infix
后置运算符 postfix
12.21
Swift与集合有关的高阶函数:https://www.jianshu.com/p/7f4472c1b039
高阶函数:仅仅是一个函数,可以接收函数做参数或者返回一个函数来操作其他其他函数
Swift自动为内联函数提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序调用闭包参数
如果在闭包表达式中使用参数名称缩写,则可以在闭包参数列表中省略对其定义,并且对应参数名称缩写类型会通过函数类型进行推导。in关键字也可以被省略。
map() 函数:
filter() 函数:
reduce() 函数:
flatmap() 函数:
高阶函数的意义:阅读和理解复杂的函数式编程;编写更优美、更易于维护的代码,具有更好的可读性;提高我们的Swift语言能力
Swift iOS 13 启动兼容13以前低版本UI state restoration for a scene in iOS 13 while still supporting iOS 12. No storyboards
https://stackoverflow.com/questions/57129668/ui-state-restoration-for-a-scene-in-ios-13-while-still-supporting-ios-12-no-sto
12.22
1、swift多态:https://www.jianshu.com/p/02bb7658a998
Swift引用变量有两个类型:
编译时类型:等号左边,声明变量的类型
运行时类型:等号右边,实际赋值给该变量的类型
多态性:
编译时多态:方法重载;
运行时多态:p.toString(),不能确定是父类的toString()方法,还是子类的,运行时再确定
is检测类型:判断前边的引用类型变量是否是后面的类或子类、实现类的实例
使用as向下转换类型:
as强制将运算符前面的引用变量转换成后面的类型,转换失败,则程序出现运行错误
as? 可选类型的向下转换,可选值包含nil
1、向下转换只能在具有继承关系的两个类型之间进行。
2、考虑到进行强制类型转换时可能出现异常,因此进行类型转换之前可先通过is运算符来判断是否可以成功转换。这样可以使程序更加健壮。
3、当把子类实例赋给父类引用变量时,被称为向上转型,这种转型总是可以成功的。
向下转好像没啥意义;向上转是自动的
Any和AnyObject:
swift为不确定类型提供了两种特殊的类型别名
1、AnyObject:可代表任何类的实例。
2、Any:可代表任何类型,包括Int、Double等值类型。
嵌套类型:
swift允许在一个类型的内部(即{}内部)定义另一个类型,这种机制被称为嵌套类型。
需要说明的是,嵌套类型不允许使用static或class修饰,也就是说,嵌套类型没有所谓的“类型相关的嵌套类型”。
扩展:
扩展是swift的一种动态特征,swift允许使用扩展为现有的类添加新方法,并且不需要创建子类,不需要访问原有类的源代码。但因为扩展不是派生子类,所以扩展不支持重写。
协议:
用于定义多个类型应该遵守的规范。swift协议的作用,就相当于其他语言中接口的作用。
合成协议:
swift允许将多个协议合成一个临时的类型,这种用法被称为合成协议。
唯类协议:
这种协议只能被类实现,不能被枚举、结构体实现。其定义方式,只要在定义协议的协议名后的冒号后面添加class关键字即可(class放在所有父协议的第一位)。
可选协议:
可以选择不实现的协议。可选协议必须添加@objc修饰,协议成员前添加optional关键字即可定义可选协议。可选协议一定是唯类协议(因为它只能被类实现,不能被枚举、结构体实现)
注意:
1. 协议支持形参个数可变的方法,但不支持为形参指定默认值。
2. 如果协议中定义的方法没有使用mutating修饰(没有变成可变方法),枚举、结构体实现该协议,并实现该方法是就不能添加mutating。简单的说,可变、非可变方法都可实现协议中的可变方法;但只有非可变方法才能实现协议中的非可变方法。
3. 协议能要求实现者必须提供哪些下标,也能要求该下标是否有setter部分和getter部分。
4. 只有使用类实现协议中定义的构造器时,才需要使用required;使用枚举、结构体实现协议中的构造器则没有这些要求。
2、convenience关键字:
3、Final关键字:https://blog.csdn.net/zkdemon/article/details/90265451
Swift中,final关键字可以在class、func和var前修饰。表示 不可重写 可以将类或者类中的部分实现保护起来,从而避免子类破坏。
4、subscript
在Swft中,subscript关键字表示下标,可以让class、struct、以及enum使用下标访问内部的值。其实就可以快捷方式的设置或者获取对应的属性, 而不需要调用对应的方法去获取或者存储
• 下标是方法的一种,是访问集合、列表或者序列中的元素的快捷方式。用法:实例名[索引],可以访问或设置其中元素 https://www.jianshu.com/p/0335ebf8f7d5
• 定义形式:一个名为subscript的计算属性,可以忽略set(只读)
subscript(index: 参数类型) -> 返回类型 {
get{}
set{}
}
常见用法:访问字典、数组、集合的某一个元素
var topProvinces = ["GD", "JS", "SD", "ZJ"]
topProvinces[1] //"JS"
var gdpRank = ["JS":70116, "SD":63002, "GD":72813 ,"ZJ":42886]
gdpRank["GD"] //72813
5、static
Swift中,用static关键字声明静态变量或者函数,它保证在对应的作用域当中只有一份, 同时也不需要依赖实例化。注意:(用static关键字指定的方法是类方法,他是不能被子类重写的)
6、mutating
Swift中,mutating关键字指的是可变即可修改。用在structure和enumeration中,虽然结构体和枚举可以定义自己的方法,但是默认情况下,实例方法中是不可以修改值类型的属性。为了能够在实例方法中修改属性值,可以在方法定义前添加关键字mutating
7、extension
1》定义实例方法和类型方法 2》添加计算型属性和计算静态属性 3》定义下标 4》提供新的构造器 5》定义和使用新的嵌套类型 6》使一个已有类型符合某个接口
8、convenient https://www.jianshu.com/p/3d0507addb30
swift中,使用convenience修饰的构造函数叫做便利构造函数 。便利构造函数通常用在对系统的类进行构造函数的扩充时使用。便利构造函数有如下几个特点
1》便利构造函数通常都是写在extension里面 2》便利函数init前面需要加载convenience 3》在便利构造函数中需要明确的调用self.init()
9、fallthrough
swift语言特性switch语句的break可以忽略不写,满足条件时直接跳出循环.fallthrough的作用就是执行完当前case,继续执行下面的case.类似于其它语言中省去break里,会继续往后一个case跑,直到碰到break或default才完成的效果
10、open
在Swift中,open修饰的对象表示可以被任何人使用,包括override和继承。
11、public
在Swift中,public表示公有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。但在其他模块(一个App就是一个模块,一个第三方API, 第三等方框架等都是一个完整的模块)不可以被override和继承,而在本模块内可以被override和继承
12、internal
在Swift中,public表示内部的访问权限。即有着internal访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在Swift中默认就是internal的访问权限
13、required
在Swift里,required是用来修饰init方法的,说明该构造方法是必须实现的。如果子类需要添加异于父类的初始化方法时,必须先要实现父类中使用required修饰符修饰过的初始化方法,并且也要使用required修饰符而不是override
注意: (1)required修饰符只能用于修饰类初始化方法(2)当子类含有异于父类的初始化方法时(初始化方法参数类型和数量异于父类),子类必须要实现父类的required初始化方法,并且也要使用required修饰符而不是override(3)当子类没有初始化方法时,可以不用实现父类的required初始化方法。
对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写。https://www.jianshu.com/p/09c6c88ed61e
14、open
在Swift中,open修饰的对象表示可以被任何人使用,包括override和继承
声明为open的类可以在本文件内被其他类继承,也能在其他文件被其他类继承
15、public
在Swift中,public表示公有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。但在其他模块(一个App就是一个模块,一个第三方API, 第三等方框架等都是一个完整的模块)不可以被override和继承,而在本模块内可以被override和继承
声明为public的类可以在本文件内被其他类继承,不能在其他文件被其他类继承
16、internal
在Swift中,public表示内部的访问权限。即有着internal访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在Swift中默认就是internal的访问权限
17、private
在Swift中,private私有访问权限。被private修饰的类或者类的属性或方法可以在同一个物理文件中访问。如果超出该物理文件,那么有着private访问权限的属性和方法就不能被访问
18、fileprivate
在Swift中,fileprivate访问级别所修饰的属性或者方法在当前的Swift源文件里可以访问
19、private(set) https://www.jianshu.com/p/9fcb13990f09
//声明一个String类型,名字为helloWord的可读可写属性。
var helloWord: String
//声明一个String类型,名字为helloWord的外部只读属性,内部可读可写。
private(set) var helloWord: String
20、rethrows https://xiaozhuanlan.com/topic/3174568902 https://www.jianshu.com/p/802ff8969952
针对的不是函数或者方法本身,而是他携带的闭包类型的参数,当他的闭包类型的参数throws的时候,我们要使用rethrows将这个异常向上传递
有点蒙蔽
21、then https://www.jianshu.com/p/f893a0041b07
https://www.cnblogs.com/weiboyuan/p/10551744.html
Then是一个语法糖
Then是一个开源的swift框架,使懒加载的写法更美观和方便
swift的then库
let label = UILabel().then {
$0.textAlignment = .center
$0.textColor = .black
$0.text = "Hello, World!"
}
let label: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.textColor = .black
label.text = "Hello, World!"
return label
}()
12.23
1、Scanner字符串扫描类
https://blog.csdn.net/ShmilyCoder/article/details/77948798
Scanner继承自NSObject,遵守NSCopying协议。是一个用于扫描指定字符串的抽象类;可以创建Scanner时制定他的String属性,然后scanner会按照你的要求从头到尾扫描这个字符串的每个字符;扫描操作从上次扫描的位置开始,并且继续往后扫描直到指定的内容出现为止(如果有的话)
2、 swift运行时
1》Swift 运行时机制https://www.jianshu.com/p/b982f4c7b9fc
纯Swift类的函数调用已经不是OC那样的运行时消息。而是类似C++的vtable,在编译的时候,就决定调用哪个函数了。不像OC在运行时才确定调用哪个函数。对于纯的Swift类来说,无法通过objc runtime替换方法,拿不到这些方法和属性。对于继承自NSObject类(比如NSObject)的Swift来说,将会自动被编译器插入@objc标志。@objc标志是用来将Swift的API到处给Objective-C和Objective-C runtime使用
加了@objc标识的方法、属性无法保证都会被运行时调用,因为Swift会做静态优化。要想完全被动态调用,必须使用dynamic修饰。使用dynamic修饰将会隐式的加上@objc标识。
2》Swift是否和OC一样有runtime机制 https://blog.csdn.net/judy_luo/article/details/51025210
可以知道@objc是用来将Swift的API导出给Objective-C和Objective-C runtime使用的,如果你的类继承自Objective-c的类(如NSObject)将会自动被编译器插入@objc标识
3》HOOK技术https://www.jianshu.com/p/1ba44f9ea71a
fishhook是facebook出品的一个开源库。利用mach-o文件加载原理,通过rebind_symbols函数修改__DATA Segment的符号指针指向,来动态的Hook C函数。hook OC函数;hook C函数
Method Swizzle:SEL;id;Class;Method;Ivar(成员变量);IMP;Cache
UIViewController+swizzling.m 交换 viewdidload方法和自定义的swizz_viewdidload方法;周全起见,有两种情况要考虑一下 第一种情况:要交换的方法并没有在目标类中实现,而是在其父类中实现了, 这时使用class_getInstanceMethod函数获取到的originalSelector指向的就是父类的方法 第二种情况:要交换的方法已经存在于目标类中
3、 swift进阶 Swift进阶https://www.jianshu.com/p/24519d162c01
成员访问级别约定规则
1》如果一个类的访问级别是private那么该类的所有成员都是private(此时成员无法修改访问级别),如果一个类的访问级别是internal或者public那么它的所有成员都是internal(如果类的访问级别是public,成员默认internal,此时可以单独修改成员的访问级别),类成员的访问级别不能高于类的访问级别(注意:嵌套类型的访问级别也符合此条规则);
2》常量、变量、属性、下标脚本访问级别低于其所声明的类型级别,并且如果不是默认访问级别(internal)要明确声明访问级别(例如一个常量是一个private类型的类类型,那么此常量必须声明为private)
3》在不违反1、2两条规则的情况下,setter的访问级别可以低于getter的访问级别(例如一个属性访问级别是internal,那么可以添加private(set)修饰将setter权限设置为private,在当前模块中只有此源文件可以访问,对外部是只读的);
4》必要构造方法(required修饰)的访问级别必须和类访问级别相同,结构体的默认逐一构造函数的访问级别不高于其成员的访问级别(例如一个成员是private那么这个构造函数就是private,但是可以通过自定义来声明一个public的构造函数),其他方法(包括其他构造方法和普通方法)的访问级别遵循规则1
5》子类的访问级别不高于父类的访问级别,但是在遵循三种访问级别作用范围的前提下子类可以将父类低访问级别的成员重写成更高的访问级别(例如父类A和子类B在同一个源文件,A的访问级别是public,B的访问级别是internal,其中A有一个private方法,那么B可以覆盖其private方法并重写为internal)
6》协议中所有必须实现的成员的访问级别和协议本身的访问级别相同,其子协议的访问级别不高于父协议;
7》如果一个类继承于另一个类的同时实现了某个协议那么这个类的访问级别为父类和协议的最低访问级别,并且此类中方法访问级别和所实现的协议中的方法相同
8》扩展的成员访问级别遵循规则1,但是对于类、结构体、枚举的扩展可以明确声明访问级别并且可以更低(例如对于internal的类,你可以声明一个private的扩展),而协议的访问级别不可以明确声明
9》元组的访问级别是元组中各个元素的最低访问级别,注意:元组的访问级别是自动推导的,无法直接使用以上三个关键字修饰其访问级别
10》函数的访问级是函数的参数、返回值的最低级别,并且如果其访问级别和默认访问级别(internal)不符需要明确声明
11》枚举成员的访问级别等同于枚举的访问级别(无法单独设置),同时枚举的原始值、关联值的访问级别不能低于枚举的访问级别
12》泛型类型或泛型函数的访问级别是泛型类型、泛型函数、泛型类型参数三者中最低的一个
13》类型别名的访问级别不能高于原类型的访问级别
4、反射:https://www.jianshu.com/p/24519d162c01
熟悉C#、Java的朋友不难理解反射的概念,所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。在使用ObjC开发时很少强调其反射概念,因为ObjC的Runtime要比其他语言中的反射强大的多。在ObjC中可以很简单的实现字符串和类型的转换(NSClassFromString()),实现动态方法调用(performSelector: withObject:),动态赋值(KVC)等等,这些功能大家已经习以为常,但是在其他语言中要实现这些功能却要跨过较高的门槛,而且有些根本就是无法实现的。不过在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect),即使目前Swift中的反射还没有其他语言中的反射功能强大(Swift还在发展当中,相信后续版本会加入更加强大的反射功能
在Swift中反射信息通过MirrorType协议来描述,而Swift中所有的类型都能通过reflect函数取得MirrorType信息,获取到一个变量(或常量)的MirrorType之后就可以访问其类型、值、类型种类等元数据信息。
5、dynamic关键字 扩展--KVO https://wwgw.jianshu.com/p/24519d162c01
和KVC一样,在Swift中使用KVO也仅限于NSObject及其子类,因为KVO本身就是基于KVC进行动态派发的,这些都属于运行时的范畴。Swift要实现这些动态特性需要在类型或者成员前面加上@objc(继承于NSObject的子类及非私有成员会自动添加),但并不是说加了@objc就可以动态派发,因为Swift为了性能考虑会优化为静态调用。如果确实需要使用这些特性Swift提供了dynamic关键字来修饰,例如这里要想使用KVO除了继承于NSObject之外就必须给监控的属性加上dynamic关键字修饰
注意:对于系统类(或一些第三方框架)由于无法修改其源代码如果要进行KVO监听,可以先继承此类然后进行使用dynamic重写;此外,并非只有KVO需要加上dynamic关键字,对于很多动态特性都是如此,例如要在Swift中实现Swizzle方法替换,方法前仍然要加上dynamic,因为方法的替换也需要动态派发
swift kvo https://www.jianshu.com/p/f6aa48a11a88
6、指针与内存 https://www.jianshu.com/p/24519d162c01
除了循环引用问题,Swift之所以将指针类型标识为“unsafe”是因为指针没办法像其他类型一样进行自动内存管理,因此有必要了解一下指针和内存的关系。在Swift中初始化一个指针必须通过alloc和initialize两步,而回收一个指针需要调用destroy和dealloc(通常dealloc之后还会将指针设置为nil)。
运行程序可以看到p对象在函数执行结束之后被销毁,但是如果仅仅将pointer设置为nil是无法销毁Person对象的,这很类似于之前的MRC内存管理,在Swift中使用指针需要注意:谁创建(alloc,malloc,calloc)谁释放。 当然上面演示中显然对于指针的操作略显麻烦,如果需要对一个变量进行指针操作可以借助于Swift中提供的一个方法withUnsafePointer。例如想要利用指针修改Person的name就可以采用下面的方式:
var p = Person(name: "Kenshin Cui")
var p2 = withUnsafeMutablePointer(&p, {
(pointer:UnsafeMutablePointer) -> Person in
pointer.memory.name = "Kaoru"
return pointer.memory
})
println(p.name) //结果:Kaoru
7、扩展—Core Foundation
Core Foundation作为iOS开发中最重要的框架之一,在iOS开发中有着重要的地位,但是它是一组C语言接口,在使用时需要开发人员自己管理内存。在Swift中使用Core Foundation框架(包括其他Core开头的框架)需要区分这个API返回的对象是否进行了标注:
1. 如果已经标注则在使用时完全不用考虑内存管理(它可以自动管理内存)。
2. 如果没有标注则编译器不会进行内存管理托管,此时需要将这个非托管对象转化为托管对象(当然你也可以使用retain()、release()或者autorelease()手动管理内存,但是不推荐这么做)。当然,苹果开发工具组会尽可能的标注这些API以实现C代码和Swift的自动桥接,但是在此之前未标注的API会返回Unmanaged<Type>结构,可以调用takeUnretainedValue()和takeRetainedValue()方法将其转化为可以自动进行内存管理的托管对象(具体是调用前者还是后者,需要根据是否需要开发者自己进行内存管理而定,其本质是使用takeRetainedValue()方法,在对象使用完之后会调用一次release()方法。按照Core Foundation的命名标准,通常如果函数名中含“Create”、“Copy”、“Retain”关键字需要调用takeRetainedValue()方法来转化成托管对象)。
当然,上述两种方式均是针对系统框架而言,如果是开发者编写的类或者第三方类库,应该尽可能按照Cocoa规范命名并且在合适的地方使用CF_RETURNS_RETAINED和CF_RETURNS_NOT_RETAINED来进行标注以便可以进行自动内存管理。
8、swift的#selector
12.24swift常用的三方库:
AFNetworking Alamofire / Moya
MBProgressHUD
SDWebImage Kingfisher
MJExtension SwiftyJSON / HandyJSON
MJRefresh MJRefresh
IQKeyboardManager IQKeyboardManagerSwift
Masonry
Reachability 检测当前网络连接情况
swift常用框架:
Alamofire / Moya;
MBProgressHUD
Kingfisher 喵神王巍写的一款关于图片下载、缓存的框架,灵感取自于SDWebImage。
SwiftyJSON / HandyJSON
MJRefresh
IQKeyboardManagerSwift
SnapKit:自动布局框架,类似于Masonry。
Reachability 检测当前网络连接情况
SQLite.swift:用swift封装的sqlite 3操作框架。
MonkeyKing三方分享框架, 社会化分享框架,支持分享text、url、image、audio、file到WeChat、QQ、Alipay、Weibo
swift常用的库
1、json解析框架:https://www.jianshu.com/p/e9d933ce7c74
1》SwiftyJSON:本质上仍然是根据JSON结构去取值,使用起来顺手、清晰;但这种做法没能妥善解决上述的几个问题,因为它不是机遇model的,我们使用的时候,依然必须制定key去获取value,这在一定程度上不是很友好
2》ObjectMapper:实现了JSON直接转Model的功能,不过使用起来,代码量会多一点,因为我们必须遵循Mappable协议,制定json内的每一个key和model属性的对应关系
3》HandyJSON:是阿里一位大神推出的,能够做到JSON转Model一步到位,而且使用起来,非常简洁方便;HandyJSON另辟蹊径,采用Swift反射+内存赋值的方式来构造Model实例,保持原汁原味的Swift类定义
2、Moya:https://www.jianshu.com/p/219b197a230a
Moya是一个帮助我们管理Alamofire的网络管理层,可以让我们去更清晰的去管理我们的网络请求。
深入理解Moya设计 https://segmentfault.com/a/1190000012997081
swift的Moya虽然也有使用到继承,但是它的整体上是以POP思想(Protocol Oriented Programming,面向协议编程)为主导的;而Swift则引入了面向协议编程,通过协议来规定事物的实现。通过遵守不同的协议,来对一个类或者结构体或者枚举进行定制,它只需要实现协议所规定的属性或方法即可,有点类似于搭建积木,取每一块有需求的模块,进行组合拼接,相对于OOP,其耦合性更低,也为代码的维护和拓展提供更多的可能性。关于POP思想大致是这样,下面是王巍关于POP的两篇文章,值得读一番
王巍的博客 面向协议编程与 Cocoa 的邂逅 (上) https://onevcat.com/2016/11/pop-cocoa-1/
3、Kingfisher:https://www.jianshu.com/p/fa2624ac1959
网络图片缓存库 https://github.com/onevcat/Kingfisher
4、SnapKitExtend 相当于Masonry
5、EmptyDataSet-Swift 当列表页面无数据时不要展示一个白板,显得太空旷,放一个背景图,或者文字提示什么的
Carthage
使用swift编写,只支持动态框架,只支持ios8+是Cocoa依赖管理工具;与现在流行的 CocoaPods 不同,Carthage编译你的依赖,并提供框架的二进制.framework文件,但你仍然保留对项目的结构和设置的完整控制,Carthage不会自动的修改你的项目文件或编译设置。是一个去中心化的Cocoa依赖管理工具
安装Carthage和使用: https://www.jianshu.com/p/9c4de4ebbc96
iOS开发——Carthage:去中心化的Cocoa依赖管理器https://my.oschina.net/u/2524932/blog/729410
1) CoaoaPods 是一套整体解决方案,我们在 Podfile 中指定好我们需要的第三方库。然后 CocoaPods 就会进行下载,集成,然后修改或者创建我们项目的 workspace 文件,这一系列整体操作
2) 相比之下,Carthage 就要轻量很多,它也会一个叫做 Cartfile 描述文件,但 Carthage 不会对我们的项目结构进行任何修改,更不多创建 workspace。它只是根据我们描述文件中配置的第三方库,将他们下载到本地,然后用 xcodebuild 构建成 framework 文件。然后由我们自己将这些库集成到项目中。Carthage 使用的是一种非侵入性的哲学
Carthage 基本的工作流程:
1> 创建一个Cartfile,包含你希望在项目中使用的框架的列表
2> 运行Carthage,将会获取列出的框架并编译它们
3> 将编译完成的.framework二进制文件拖拽到你的Xcode项目当中
Carthage编译你的框架/库,然后提供该框架的二进制文件,但你仍然持有该项目结构和设置的绝对控制。Carthage不会自动的修改你的项目文件或编译设置
Carthage优势:
① 使用 Carthage 的话,所有的第三方库依赖,除非是更新的需要,不然平常干净编译 Project,它是不需要再次编译的,大大加快平常编译及 Archive 的时间.
② 它是去中心化的,没有中心服务器. 这意味着每次配置和更新环境,只会去更新具体的库,而不会有一个向中心服务器获取最新库的索引这么个过程,如此又省了很多时间.
③ CocoaPods 无缝集成!一个项目可同时使用两套包管理工具, 当前 CocoaPods 管理主要 Framework 的配置下, 将少量其他 Framework 交给了 Carthage 管理, 二者可以和谐地共存.
④ 结构标准的项目天然就是 Carthage 库
Carthage不足:
1⃣️ 库依然不如 CocoaPods 丰富:尽管很多库不需要声明并改造就直接可以被 Carthage 用,但依然有大量 CocoaPods 能用的库不支持,我相信时间能解决这个问题;
2⃣️ 只支持 Framework,所以是 iOS 8 Only了,随着时间推移,这个也不会是问题;
3⃣️ 工具仍不完善:在使用过程中,发现它无法在一个结构复杂的项目中正确发现库(比如有 iOS, Mac demo + framework 的结构);
4⃣️ 无法在 Xcode 里定位到源码:如果你在写代码过程中,想跳转到一个第三方库去看具体的实现,这是无法办到的,Carthage 的配置只能让你看到一个库的头文件
associatedtype
https://www.jianshu.com/p/2039dc0804c3
typealiashttps://www.jianshu.com/p/ac5185c89af1 swift associatedtype和typealias
为已有类型重命名
1、可以用来对已有的类型进行重命名,比如在表示两点之间的距离的时候,可以使用typealias将x和y轴的距离Double表示为Distance。
2、可以对闭包进行重新命名,这样在做参数传递的时候更加清晰
3、协议使用associatedtype的时候,可以用来对关联类型重定义,协助协议实现泛型功能,下面会说到
Result<Value, Error> https://onevcat.com/2018/10/swift-result-error/
它的思想非常简单,用泛型将可能的返回值包装起来,因为结果是成功或者失败二选一,所以我们可以藉此去除不必要的可选值。
Where
Where关键字后边应该跟条件语句啊?以下where后边是什么用法??
public extension Reactive where Base: MoyaProviderTyp
public enum Result<Value, Erroresult.ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible where Error : Error
只给遵守了 WRProtocol 协议的UIView添加了下面的拓展 https://www.jianshu.com/p/1b162ea1660c
extension WRProtocol where Self:UIView
{
func getString() -> String{
return "string"
}
}
错误处理 try-catch https://www.jianshu.com/p/0335ebf8f7d5
错误处理:反映运行出错的“细节”,并恢复程序的过程;可选类型仅处理值缺失,错误处理可以针对不同的出错原因对不同的应对
一个函数可以加上throws关键字,表示可以处理错误,这个函数的调用者可以捕获(catch)这个错误并进行应对
func method1() throws {
print("我是一个函数")
}
当你调用可以抛出错误的函数,需在前面加上try关键字
try method1()
处理更细分的错误情况,错误类型需遵从Error协议
enum learningObstacle : Error {
case noMethod, noReading, noTool(withMac: String)
}
func iOSLearning(hasMethod: Bool, hasWay: Bool, hasTool: Bool) throws {
guard hasMethod else {
throw learningObstacle.noMethod
//guard语句成立才往后执行,否则抛出错误
}
guard hasWay else {
throw learningObstacle.noReading
}
guard hasTool else {
throw learningObstacle.noTool(withMac: "mac电脑")
}
}
var budget = 7000
func purchase(tool: String) {
//采购
if budget >= 6000 {
budget -= 6000
print("您已采购", tool, ",花费了6000,余额是:", budget)
} else {
print("资金不足")
}
}
do {
try iOSLearning(hasMethod: true, hasWay: true, hasTool: false)
print("成功开始学习")
} catch learningObstacle.noMethod {
print("打开慕课网,学习swift语法先")
} catch learningObstacle.noReading {
print("看慕课视频")
} catch learningObstacle.noTool(let mac) {
purchase(tool: mac)
}
if let result = try? iOSLearning(hasMethod: true, hasWay: true, hasTool: true) {
//?可忽略原来写的错误处理
print("愉快的学习了!")
} else {
print("学习失败!")
}
//try! 有一项失败程序就中断掉,不推荐
try! iOSLearning(hasMethod: true, hasWay: true, hasTool: true)
特定的清理收尾工作,defer语句
func pk() {
defer {
//最后执行
print("Game Over")
}
print("Fight!")
print("You vs xiaobo ")
/*
运行结果
Fight!
You vs xiaobo
Game Over
*/
}
pk()
Java的异常处理
try catch finally
1、不管有没有出现异常,finally块中代码都会执行;2、当try和catch中有return时,finally仍然会执行;3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的;4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
https://baijiahao.baidu.com/s?id=1621169426787906677&wfr=spider&for=pc
12.27 whre语句 https://www.jianshu.com/p/50736a304c08
12.27:
泛型:
类型约束:类型约束指定了一个必须继承自指定类的类型参数,或遵循一个特定的协议或协议构成,例:
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
第一个类型参数 T,有一个要求 T 必须是 SomeClass 子类的类型约束;
第二个类型参数 U,有一个要求 U 必须符合 SomeProtocol 协议的类型约束。
open class MoyaProvider<Target: TargetType>: MoyaProviderType
MoyaProvider类的泛型Target要遵守TargetType协议,且此类遵守MoyaProviderType协议
假设从A控制器通过present的方式跳转到了B控制器,那么 A.presentedViewController 就是B控制器;B.presentingViewController 就是A控制器。
Where:
不同的版本的Swift中where用法有些不同,以最新的Swift4为准
1》extension + where后跟类型(UIImage为例)
extension Kingfisher where Base: Image
public typealias Image = UIImage
2》泛型 + where
func test<T>(a:T) where T:Person
3》where后跟条件表达式
Initializer for conditional binding must have Optional type, not 'String' 条件语句必须为可选类型
swift中where的用法总结:https://www.jianshu.com/p/50736a304c08
1》if, guard, while三个语句中where被去掉了,直接使用,相隔就行了
2》do catch
enum ExceptionError:Error{
case httpCode(Int)
}
func throwError() throws {
throw ExceptionError.httpCode(500)
}
do{
try throwError()
}catch ExceptionError.httpCode(let httpCode) where httpCode >= 500{
print("server error")
}
3》switch
var value:(Int,String) = (1,"小明")
switch value {
case let (x,_) where x < 60:
print("不及格")
default:
print("及格")
}
4》for in
for i in arrayOne where dictionary[i] != nil
5》泛型
func genericFunction<S>(str:S) where S:ExpressibleByStringLiteral
// 也可以不使用where语句,直接在尖括号中定义时做限制
func genericFunction2<S:ExpressibleByStringLiteral>(str:S)
(注:ExpressibleByStringLiteral是协议)
6》协议
extension aProtocol where Self:UIView //只给遵守myProtocol协议的UIView添加了扩展
12.28
在swift中条件语句可以有多个condition,例如:if case let xs? = t, xs is Int
swift中的模式匹配:https://juejin.im/entry/596ceaf25188252a0d35cd7e
switch的主要特性就是模式匹配:
//swift中的switch的主要特征就是模式匹配
//普通模式:
switch 1 {
case 1:
print("1")
default:
break;
}
//1、绑定变量:我们创建karl这个常量时给其设置了name,age,address,我们就可以在switch里面通过值绑定的形式将其取出来。
enum School{
case Student(name:String,age:Int,address:String?)
case Teacher(name:String,age:Int,address:String?)
}
let karl = School.Student(name: "karl", age: 27, address: "AH")
switch karl {
//我们创建karl这个常量时给其设置了name,age,address,我们就可以在switch里面通过值绑定的形式将其取出来。
// case .Student(let name,let age,let address):
//多个let太麻烦,可以这样写
case let .Student(name, age, address):
print("name:\(name) age:\(age) address:\(address!)")
default:
break
}
//2、通配符 _:使用通配符表示忽略这个值,这个值不重要,我不需要使用到它,但是这个也分为两种情况
let student = School.Student(name: "丽丽", age: 34, address: "CH")
let teacher = School.Teacher(name: "大爷", age: 55, address: "HB")
switch student {
case let .Teacher(name, _, _):
print("name:\(name)")
default:
break
}
// 这里第二个参数和第三个参数就是使用了通配符表示,不关心它是什么值,只要前面参数能匹配成功就可以,就算成立
_? m没必要这样写吧
// switch student {
// case let .Teacher(name, _, _?):
// print("name:\(name)")
// default:
// break
// }
//3、元组
let people = (age:27,name:"k卡顿")
switch people {
case let(_,name):
print("name:\(name)")
default:
break
}
switch people {
case (_,let name):
print("namename:\(name)")
default:
break
}
//4、模式匹配中使用where
switch student {
case let .Student(_, age, _) where age > 18:
print("成年了:\(age)")
default:
break
}
//5、匹配range
let num = 10
switch num {
case 0 ... 7:
print("[0,7]")
case 10 ..< 20:
print("[10 20)")
default:
break
}
//6、类型推断和类型转换
// 可以通过is判断这个值是否是某个类型。是的话,表示匹配成功;
// 通过 as可以将这个值尝试转换为某个类型,如果成功,则表示匹配成功
protocol Animal{}
struct Dog:Animal{}
struct Cat:Animal{}
var animalArray = [Animal]()
let dog = Dog()
let cat = Cat()
animalArray.append(dog)
animalArray.append(cat)
for item in animalArray {
switch item {
case is Dog:
print("dog")
case let cat as Cat:
print("cat:\(cat)")
default:
break
}
}
//7、模式匹配操作符 ~=
// 我们可以重载~=这个操作符,让我们自定义switch匹配规则
// 看下面的代码,我们重载了~=操作符,操作符有两个参数,返回一个Bool值。
// 参数一:case的值
// 参数二:switch后的值
// 返回值:匹配是否成功
struct Size{
var width: CGFloat
var height: CGFloat
}
let size = Size(width: 20, height: 20)
func ~=(caseArea:CGFloat,switchSize:Size) -> Bool{
return caseArea == switchSize.width * switchSize.height
}
switch size {
case 400:
print("计算正确")
default:
print("计算错误")
}
//8、关于Optionals
let count :Int? = 5
switch count {
case 1?:
print("1")
case 2?:
print("2")
case 3?:
print("3")
case _:
print("nil")
default:
break
}
//使用?的形式写,算是一个语法糖,可以这么写:
switch count {
case Optional.some(1):
print("1")
case Optional.some(2):
print("2")
case Optional.some(3):
print("3")
default:
break
}
//9、 if case let
let b:Int = 10;
let a:Int = 10;
if case let a = b {
print("这是b的值等于a的情况")
}
// 等价于
switch b {
case let a:
print("这是b的值等于a的情况")
default:
break
}
// 当只有一个条件的时候,用switch会显得冗余,直接用if case let会使代码读起来更便捷。
// 直接拿上面的绑定变量那一节的内容来说,如果直接这么写的话,会显得更易读,而且也会少点代码
if case let .Student(name,_,_?) = student {
print(name)
}
//if case let 加 ,
if case let .Student(name,age,_?) = student,age > 20{
print(name,age);
}
//10、 guard case let
//和 if case let的用法相同,仅仅是guard和if的用法不同
//11、for case let
// 一个数组里存放的是Any类型的可选值,我想输出这个数组里面大于10的数字。
let caseArray:[Any?] = [1,2,nil,4,5,"","ss"]
//如果不用 for case let,代码如下
for item in caseArray {
if let element = item as? Int,element > 10 {
print("element:\(element)")
}
}
//用for case let 用的是where,而不是逗号
for case let x? as Int? in caseArray where x > 10{
print(x)
}
//enum School的for case let
let liLei = School.Student(name: "李雷", age: 12, address: "A")
let hanMeiMei = School.Student(name: "韩梅梅", age: 23, address: "B")
let xiaoFang = School.Student(name: "小芳", age: 22, address: "C")
let maMing = School.Teacher(name: "马明", age: 56, address: "CH")
let suFen = School.Teacher(name: "素芬", age: 66, address: "CH")
var schoolArray = [School]()
schoolArray.append(liLei)
schoolArray.append(hanMeiMei)
schoolArray.append(xiaoFang)
schoolArray.append(maMing)
schoolArray.append(suFen)
// 获取到数组里的Teacher,并且address不能为空,并且age要大于50的。
for case let .Teacher(name,age,_) in schoolArray where age > 50 {
print(name,age)
}
数组遍历:正反向,迭代器,forin https://www.jianshu.com/p/e5a6f1c5764e
12.29:
1、swift中bool的默认值是nil
var isdd :Bool?
var iss:Bool = true
var iff:Bool = false
print("isdd:\(isdd) iss:\(iss) iff:\(iff)")//isdd:nil iss:true iff:false
2、@discardableResult https://www.jianshu.com/p/af2f062b3d89 https://blog.csdn.net/weixin_34122810/article/details/85996785
有返回值的方法,没有得到接收合适使用会出现 Result of call to ‘’ is unused 警告,加@discardableResult是为了去除警告;还有一种取消警告的方法,不加@discardableResult直接加通配符_接收方法返回值
或者在调用函数时,对某些参数不赋值
3、swift的元类
T.Type .self理解
extension MoyaProvider {
@discardableResult
open func request<T: HandyJSON>(_ target: Target,
model: T.Type,
completion: ((_ returnData: T?) -> Void)?) -> Cancellable? {
return request(target, completion: { (result) in
Result
guard let completion = completion else { return }
guard let returnData = try? result.value?.mapModel(ResponseData<T>.self) else {
completion(nil)
return
}
completion(returnData.data?.returnData)
})
}
}
T.Type
ResponseData<T>.self
理解 Swift 中的元类型:.Type 与 .self:
元类型:就是类型的类型 https://www.jianshu.com/p/36083d0404b9
1》Swift 中的元类型用 .Type 表示。比如 Int.Type 就是 Int 的元类型
类型与值有着不同的形式,就像 Int 与 5 的关系。元类型也是类似,.Type 是类型,类型的 .self 是元类型的值
let intMetatype: Int.Type = Int.self
2》AnyClass: 系统创建好的变量,代表任何类的元类:具体实现是typealias AnyClass = AnyObject.Type
获得元类型后可以访问静态变量和静态方法,例如:
func register(AnyClass?, forCellReuseIdentifier: String)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
这里的 AnyClass 其实就是一个元类型:
typealias AnyClass = AnyObject.Type,通过上面的定义我们可以知道,AnyClass 就是一个任意类型元类型的别名。
3》当我们访问静态变量的时候其实也是通过元类型的访问的,只是 Xcode 帮我们省略了 .self。下面两个写法是等价
Int.max Int.self.max
4》type(of:) 与 .self
type(of:)和.self都可以获得元类型的值,但区别在于:
let instanceMetaType: String.Type = type(of: "string")//是运行时候的元类型,也就是这个实例 的类型
let staicMetaType: String.Type = String.self //.self 取到的是静态的元类型,声明的时候是什么类型就是什么类型
5》Protocol
~~protocol MyProtocol { }
let metatype: MyProtocol.Type = MyProtocol.self~~
Protocol 自身不是一个类型,只有当一个对象实现了 protocol 后才有了类型对象。所以 Protocol.self 不等于 Protocol.Type
MyProtocol.Type 也是一个有效的元类型,那么就需要是一个可承载的类型的元类型,所以改成这样:
struct MyType: MyProtocol { }
let metatype: MyProtocol.Type = MyType.self
Protocol.self 是什么类型?苹果造了一个类型:Protocol
let protMetatype: MyProtocol.Protocol = MyProtocol.self
元类型可以变得非常灵活和强大,而且在我们编写某些框架性的代码时会非常方便
HandyJSON的Metadata.swift文件中
if !(superclass is HandyJSON.Type) && !(superclass is HandyJSONEnum.Type) {
return nil
}
将HandyJSON是个协议,但却没用.Protocol;我换成Protocol后依旧能正常运行
4、swift中的swift 中 Self 与self https://www.jianshu.com/p/a6bcdebd83f5
在swift 开发过程中,尤其是第三方库中,我们多次看到首字母大写的Self,很多时候不明白其中意思,Self 与self 又有何区别:
1>.self可以用在类型后面取得类型本身,也可以用在实例后面取得这个实例本身
2>Self 不仅指代的是 实现该协议的类型本身,也包括了这个类型的子类
protocol MProtocolTest01 {
// 协议定一个方法,接受实现该协议的自身类型并返回一个同样的类型
func testMethod(c: Self) -> Self
//不能在协议中定义 范型 进行限制
//Self 不仅指代的是 实现该协议的类型本身,也包括了这个类型的子类
}
例:
protocol Copyable {
func copy() -> Self
}
class MMyClass: Copyable {
var num = 1
func copy() -> Self {
let result = type(of: self).init()
result.num = num
return result
}
//必须实现
//如果不实现:Constructing an object of class type 'Self' with a metatype value must use a 'required' initializer。错误
required init() {
}
}
12.30
swift的引用类型和值类型 https://mp.weixin.qq.com/s/Z5txegks7-he2hhSj_DRbg
内存(RAM)中有两个区域,栈区(stack)和堆区(heap)。在 Swift 中,值类型,存放在栈区;引用类型,存放在堆区
值类型:
struct、enum、tuple
Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理;
如果声明一个值类型的常量,那么就意味着该常量是不可变的(无论内部数据为 var/let)
打印地址: withUnsafePointer(to:)函数来打印值类型变量的内存地址
withUnsafePointer(to: &coordA) { print("\($0)") }
withUnsafePointer(to: &coordB) { print("\($0)") }
// 0x000000011df6ec10
// 0x000000011df6ec20
在 Swift 中,双等号(== & !=)可以用来比较变量存储的内容是否一致,如果要让我们的 struct 类型支持该符号,则必须遵守 Equatable 协议
引用类型:
class、闭包、函数
引用类型,即所有实例共享一份数据拷贝。
引用类型的赋值是浅拷贝(Shallow Copy),引用语义(Reference Semantics)即新对象和源对象的变量名不同,但其引用(指向的内存空间)是一样的,因此当使用新对象操作其内部数据时,源对象的内部数据也会受到影响
如果声明一个引用类型的常量,那么就意味着该常量的引用不能改变(即不能被同类型变量赋值),但指向的内存中所存储的变量是可以改变的
引用类型打印地址值:
print(Unmanaged.passUnretained(dogA).toOpaque())
print(Unmanaged.passUnretained(dogB).toOpaque())
// 0x0000600000031380
// 0x0000600000031380
swift的===和==:
三等号(=== & !==)可以用来比较引用类型的引用(即指向的内存地址)是否一致。也可以在遵守 Equatable 协议后,使用双等号(== & !=)用来比较变量的内容是否一致
swift的===和==:https://www.jianshu.com/p/b1df95cb286c
==:默认比较基本类型的值,Int String;不能比较引用类型或值类型,除非引用类型或值类型实现了Equatable协议
比较变量值,不比较指针是否指向同一内存
===:检查两个对象是否完全一致,只能比较引用类型,不能比较基本类型和值类型
不仅比较变量值,还会比较指针是否指向同一内存
==比较两个对象即使返回true这两个对象也不一定是完全相同的,可能只是对象的属性值相同,而===返回true则证明比较的对象是完全一致的
oc的==和isEqual:
oc的==判断的是地址值;
oc的isEqual判断的两个对象的值是否相等;
1>值类型和引用类型函数传参的不同:
函数的参数默认为常量,即在函数体内只能访问参数,而不能修改参数值,具体来说:
2>值类型作为参数传入时,函数体内部不能修改其值
引用类型作为参数传入时,函数体内部不能修改其指向的内存地址,但是可以修改其内部的变量值
值类型和引用类型对比:
1》引用类型必须明确指定init,不指定编译器报错
2》值类型赋值给新对象时,新对象将赋值;引用类型赋值给新对象时,新旧指向同一个对象
3》引用类型的默认值是可以修改的,而值类型的默认值是只读的。值类型要修改必须用mutating来修饰,class中则不同,可以直接给self赋值。
关于Swift中Struct,Class和Enum的那些事儿https://mp.weixin.qq.com/s/Z5txegks7-he2hhSj_DRbg
{//引用类型必须明确指定init
//由于class之间可以存在继承关系,因此它的初始化过程要比struct复杂,为了保证一个class中的所有属性都被初始化,Swift中引入一系列特定规则
// 一、指定构造器
// class Point2D0{//这样写报错 Class 'Point2D0' has no initializers
// var x: Double
// var y: Double
// }
//1>默认构造器,系统自带的init
class PointDefaultClass{
var x: Double = 0.0
var y: Double = 0.0
}
let design = PointDefaultClass();
//2> memberwise init方法 自定义init后,默认的init消失
class PointMemberwiseClass{
var x : Double//非可选类型属性,要么初始化的时候给值,要么在构造方法里给值
var y : Double
init(x:Double,y:Double) {
self.x = x
self.y = y
}
}
//如果给了memberwise构造器,即重写了init方法,则指定构造器let design = PointDesignedClass();失效;会导致编译错误。因为,我们接手了init的定义后,编译就不会插手init工作。所以,在定义init方法时添加默认参数, 我们称这种初始化为 designated init
let memberwise = PointMemberwiseClass(x: 2, y: 3)
//3> designed init:即在定义init方法时,添加默认参数(不论属性声明时是否赋值),此时系统init的方法又出现了
class PointDesignedClass{
// var x: Double = 0.0
// var y: Double = 0.0
var x: Double
var y: Double
init(x:Double=1,y:Double=0) {
self.x = x
self.y = y
}
}
let designed1 = PointDesignedClass()
print(designed1.x)
let designed2 = PointDesignedClass(x: 2, y: 3)
print(designed2.x)
// 二、便利构造器 convenience 需要用convenience修饰的init方法
// 必须调用指定构造器完成对象初始化,直接调self.x self.y会导致编译错误
class ConvienceClass{
var x : Double
var y : Double
init(x : Double = 0, y: Double = 0) {
self.x = x
self.y = y
}
convenience init(at:(Double,Double)){
self.init(x: at.0,y: at.1)
}
}
//三、可失败构造器 ??
// class failClass{
// convenience init?(at: (String,String)){
// guard let x = Double(at.0 ),let y = Double(at.1 ) else {
// return nil
// }
// self.init(at:(x,y))
// }
// }
// 总结:指定构造器必须调用其直接父类的指定构造器;
// 便利构造器必须调用同类中定义的其它构造器;
// 便利构造器必须最终导致一个指定构造器被调用
// 指定构造器必须总是向上代理;便利构造器必须总是横向代理
// 如果派生类没有定义任何designated initializer,那么它将自动继承所有基类的designated initializer
// 如果一个派生类定义了所有基类的designated init,那么它将自动继承基类所有的convenience init
/**
init的继承
1、如果派生类没有定义任何designated initializer,那么它将自动继承所有基类的designated initializer
2、如果一个派生类定义了所有基类的designated init,那么它将自动继承基类所有的convenience init
*/
/**
init的重载
在派生类自定义designated init, 表示明确控制派生类的初始化构造过程, Swift 就不会干涉构造过程。那么,之前创建Point3D就会出现错误
***如果想让Point3D从Point2D继承所有的convenience init,只有在派生类中实现所有的designated init方法
*/
// class Point3D: Point2D {
// var z: Double
// init(x: Double = 0, y: Double = 0, z: Double = 0) {
// self.z = z
// super.init(x: x, y: y)
// }
// override init(x: Double, y: Double) {
// // 注意先后顺序
// self.z = 0
// super.init(x: x, y: y)
// }
// }
// 此时,就可以正常工作了。只要派生类拥有基类所有的designated init方法,他就会自动获得所有基类的convenience init方法。另外,重载基类convenience init方法,是不需要override关键字修饰
}
1.8
1、颜色选择器:#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
图片选择器:#imageLiteral(resourceName: "tab_mine_S")
1.10
setter 和 getter
Getter和setter默认和原属性访问等级相同,但是swift允许给setter设置比原属性等级低的访问等级,这样可以有效的读写保护。
语法是在var前写
fileprivate(set), private(set) , internal(set)(set 可以换成 get)
例:
private (set) var view:UIView
https://www.jianshu.com/p/e6021e4f7482
1.11 IBDesignable 和IBInspectable
swift:https://medium.com/anantha-krishnan-k-g/ibdesignable-and-ibinspectable-in-swift-3-702d7dd00ca
@IBDesignable @IBInspectable
oc: https://www.jianshu.com/p/a90e44ba1f2b
IB_DESIGNABLE @property(nonatomic,assign) IBInspectable CGFloat cornerRadius;
1.12
一、extension:
1、使用范围:可以给类、结构体、枚举、协议添加新功能,类似于oc的category
2、作用:
1》一般可以添加计算属性,但是添加存储属性需要通过runtime;
1.1》增加计算属性:
计算属性只能计算
extension Double{
var km : Double { return self * 1000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm (换算成meter)
1.2》增加存储属性:runtime
objc_getAssociatedObject;
objc_setAssociatedObject
2》增加方法:
增加实例方法 类型方法 可变实例方法:(扩展增加的实例方法可以修改实例本身。结构体和枚举类型中的方法如果想要修改实例本身或者属性的话需要用mutating来修饰方法,所以扩展这样的方法也需要加mutating)
3》提供新的初始化器:
增加构造方法:
只能增加遍历构造方法,不能增加指定构造方法
4》定义下标:扩展可以给存在的类型增加新的下标
extension Int{
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return(self/ decimalBase) % 10
}
}
746381295[0] // returns 5
746381295[1] // returns 9
746381295[2] // returns 2
746381295[8] // returns 7
5》给类、结构体、枚举 定义和使用新的内置类型:
6》让一个存在的类型服从一个协议:
二、存储属性和计算属性比较:https://www.jianshu.com/p/986ae3d8bee4
1、计算属性可以用于 类+结构体+枚举;存储属性可以用于类+结构体
2、存储属性可以是变量存储属性(var);也可以是常量存储属性(let)
计算属性只能是var
3、计算属性不能直接存储,而是提供一个get和set方法,间接设置或获取其它属性
三、UIVisualEffectView 模糊效果
高斯模糊:https://www.jianshu.com/p/90cdbc56c06d
1.12
1、edgesForExtendedLayout https://www.jianshu.com/p/ca3c5a94c32b
通过设置此属性,你可以指定view的边(上、下、左、右)延伸到整个屏幕
2、 https://blog.csdn.net/shifang07/article/details/76204378
1》swift枚举的原始值rawvalue
enum WeekDayWithRaw : String {
case Monday = "1. Monday"
case Tuesday = "2. Tuesday"
case Wednesday = "3. Wednesday"
case Thursday = "4. Thursday"
case Friday = "5. Friday"
case Saturday = "6. Saturday"
case Sunday = "7. Sunday"
}
let day = WeekDayWithRaw(rawValue: "3. Wednesday")
2》swift的关联值:
enum Shape {
case Rectangle(CGRect)
case Circle(CGPoint,Int)
}
1.13
1》swift关键字
https://mp.weixin.qq.com/s/AeOStA2MkuBW-c_Bhe0xhA
2》compactMap
过滤数组,获得新的数组;或对数组元素处理获取新的数组(可以是元素类型不同的数组)
https://blog.csdn.net/yao1500/article/details/80407358
3》遵守NSCoding协议的类的子类都要实现 required init方法
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
https://segmentfault.com/q/1010000005792852
是NSCoding协议定义的方法,是必要的构造器,当没有任何指定构造器时,会默认继承父类的必要构造器;当在子类中定义或重写指定构造器就必须实现父类的指定构造器;如果在子类中只定义了便利构造器则必要构造器会自动继承。
UIViewController 的那个无参的构造器是从 UIKit 里面迁移过来的,也就是说这个无参的构造器实际上就是 Objective-C 的 UIViewController 的无参构造方法,因为UIKit是OC写的,是Objective-C bridge过来的。
convenience构造器可以调用init(string: String)但不能调用init?(coder aDecoder: NSCoder)吗?若是这样,那是应为init?(coder aDecoder: NSCoder)是可失败构造器,要么你把你的那个构造器也弄成可失败,要么直接用‘!’解开optional。
NSCoding协议的init?(coder aDecoder: NSCoder)为么是必要构造器
xib的初始化要调用initWithCoder原因: https://www.jianshu.com/p/c6bfe153775a
.nib文件,是程序员通过Interface Builder(界面生成器)构建界面之后生成的包括界面,按钮点击事件等内容的归档(archiving)文件。它可以被程序员直接使用,但是使用前需要进行解除归档(Unarchiving)操作
4》swift的set和get方法:https://www.jianshu.com/p/87fc570e44df
Get方法里:
var c:Int{
get{
//不能写 c; self.c; _c; 都会死循环
}
set{
//不能写 c; self.c; _c; 都会死循环
}
}
1.14、
学习flutter网站 https://flutterchina.club/flutter-for-ios/
1.15、
1》UnsafeMutableRawPointer UnsafeRawPointer
private let parallaxHeaderKVOContext = UnsafeMutableRawPointer.allocate(
byteCount: 4,
alignment: 1
)
swift的指针:
Pointer结尾的类型一般都是swift类型的指针;https://www.jianshu.com/p/103591adc3d1
iOS中有的库还是使用C语言构建的,在与C混编的时候,不可避免的要将对象转换为C的指针类型以便传值。比如GCD,CF框架Runloop操作;
swift与c指针转换对应如下:
1.1》返回值、变量、参数指针:
const int * ===》 UnsafePointer <Int32>
int * ===》 UnsafeMutablePointer <Int32>
1.2》类对象指针:
Type * const * ===》 UnsafePointer<Type>
Type * __strong * === 》 UnsafeMutablePointer<Type>
Type ** ===》 AutoreleasingUnsafeMutablePointer<Type>
1.3》无符号类型:
const void * ===》 UnsafeRawPointer
void * ===》 UnsafeMutableRawPointer
如果像不完整结构体的这样的c指针的值的类型无法用Swift来表示,则用OpaquePointer来表示
1.2》常量指针:
UnsafePointer<Type> c中有const修饰
1.2.1》类型为UnsafePointer<Type>, UnsafeMutablePointer<Type>, or AutoreleasingUnsafeMutablePointer<Type>或者转换为UnsafePointer<Type>类型的值
1.2.2》String类型的值,如果类型是Int8或UInt8,那字符串会自动转换为UTF8的buffer,然后这个buffer的指针将会传入函数
1.2.3》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这样的情况下,将左边起始地址传入函数
1.2.4》[Type]数组类型值,将数组起始地址传入函数。
1.3》可变指针:定义为UnsafeMutablePointer<Type>、UnsafeMutableRawPointer的指针为可变指针
1.3.1》类型为UnsafeMutablePointer<Type>类型的值
1.3.2》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这样的情况下,将地址传入函数
1.3.3》表达式[Type]类型的变量、属性、同类型的下标表达,这样的情况下,将左边起始地址传入函数
1.3》自动释放指针:Autoreleasing Pointers
定义为AutoreleasingUnsafeMutablePointer<Type>,可以接收下面几种类型的值:
1.3.1》AutoreleasingUnsafeMutablePointer<Type>的值
1.3.2》表达式Type类型的变量、属性、同类型的下标表达(如:a[0]),这里会按位拷贝到临时缓冲区,缓冲区的值会被加载,retain,分配值 注意:不能传数组
1.4》函数指针:在C中有回调函数,当swift要调用C中这类函数时,可以使用函数指针
swift中可以用@convention 修饰一个闭包:
@convention(swift) : 表明这个是一个swift的闭包;
@convention(block) :表明这个是一个兼容oc的block的闭包,可以传入OC的方法
@convention(c) : 表明这个是兼容c的函数指针的闭包,可以传入C的方法
(C中的方法int (*)(void) 在swift中就是@convention(c) () -> Int32)
1.5》Buffer指针:buffer指针用于比较底层的内存操作,你可以使用buffer指针做高效的处理和应用程序与服务间的通信。有下面几种类型的buffer指针:
UnsafeBufferPointer;
UnsafeMutableBufferPointer;
UnsafeRawBufferPointer;
UnsafeMutableRawBufferPointer;
UnsafeBufferPointer、 UnsafeMutableBufferPointer,能让你查看或更改一个连续的内存块;UnsafeRawBufferPointer、UnsafeMutableRawBufferPointer能让你查看或更改一个连续内存块的集合,集合里面每个值对应一个字节的内存
1.6》指针用法:
当使用指针实例的时候,可以使用pointee属性获取指针指向内容的值,指针不会自动管理内存或对准担保。你必须自己管理生命周期以避免泄露和未定义的行为
内存可能有几种状态:
未分配的:没有预留的内存分配给指针;已分配的:指针指向一个有效的已分配的内存地址,但是值没有被初始化;已初始化:指针指向已分配和已初始化的内存地址 指针将根据我们具体的操作在这 3 个状态之间进行转换。
用法示例:
未分配的指针用allocate方法分配一定的内存空间;
分配完内存空间的指针用各种init方法来绑定一个值或一系列值。初始化时,必须保证指针是未初始化的。(初始化过的指针可以再次调用初始化方法不会报错,所以使用时需要特别注意。);
然后修改值
回到初始化值之前,没有释放指针指向的内存,指针依旧指向之前的值。
释放指针指向的内存,据官方文档说,在释放指针内存之前,必须要保证指针是未初始化的,不然会产生问题。
1.7》
转换:指针间转换
可变指针与不可变指针间转换
1.7.1》一个函数需要传入不可变指针时,可变指针可以直接传入
1.7.2》一个函数需要可变指针时,可以使用init(mutating other: UnsafePointer<Pointee>)方法转换
var i: Int8 = 12
func printPointer(p: UnsafePointer<Int8>) {
let muS2ptr = UnsafeMutablePointer<Int8>.init(mutating: p)! // UnsafePointer<Int8> -> UnsafeMutablePointer<Int8>
print(muS2ptr.pointee)
var constUnTypePointer = UnsafeRawPointer(p) // UnsafePointer<Int8> - > UnsafeRawPointer
var unTypePointer = UnsafeMutableRawPointer(mutating: constUnTypePointer) // UnsafeRawPointer -> UnsafeMutableRawPointer
var unTypePointer2 = UnsafeMutableRawPointer(muS2ptr) // UnsafeMutablePointer<Int8> -> UnsafeMutableRawPointer
}
转换:不同类型指针间转换
1.7.3》 Uint8的指针 转换为UInt64
let pointer1 = UnsafeRawPointer(uint8Pointer).assumingMemoryBound(to: UInt64.self)
let pointer2Value = UnsafeRawPointer(uint8Pointer).load(as: UInt64.self)
let pointer3 = UnsafeMutablePointer(mutating: uint8Pointer).withMemoryRebound(to: UInt64.self, capacity: 1) { return $0 }
转换:指针转换成swift
1.7.4》load等方法转为对应的swift类型
func print<T>(address p: UnsafeRawPointer, as type: T.Type) {
let value = p.load(as: type)
print(value)
}
转换:对象转换
1.7.5》在调用一些底层库的时候,经常要把Swift对象传入C中,然后将从C中的回调函数中转换成Swift对象
C函数的回调函数中,传出来一个UnsafeMutableRawPointer对象的指针,我展示了3种方式,可以将这个指针转换为ViewController对象:
let controllerPoint = withUnsafeMutablePointer(to: &blockSelf) { return $0}
var context = Context(info: controllerPoint, retain: nil)
abcPrint(&context) {
let controller1 = $0?.assumingMemoryBound(to: ViewController.self).pointee
let controller2 = $0?.bindMemory(to: ViewController.self, capacity: 1).pointee
let controller3 = $0?.load(as: ViewController.self)
}
1.8、CFRoopLoop使用
Unmanaged方式:
苹果的一些底层框架返回的对象有的是自动管理内存的(annotated APIs),有的是不自动管理内存
对于Core Fundation中有@annotated注释的函数来说,返回的是托管对象,无需自己管理内存,可以直接获取到CF对象,并且可以无缝转化(toll free bridging)成Fundation对象,比如NSString和CFString。目前,内存管理注释正在一步步的完善,所以等到未来某一个版本我们就可以完完全全的像使用Fundation一样使用Core Fundation
2》NSKeyValueObserving swift的kvo
guard let scrollView = self.superview as? UIScrollView else {
return
}
scrollView.addObserver(
self.parent,
forKeyPath: NSStringFromSelector(
#selector(getter: scrollView.contentOffset)
),
options: NSKeyValueObservingOptions.new,
context: parallaxHeaderKVOContext
)
kvo contentOffset
kvo:
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
progressView.isHidden = webView.estimatedProgress >= 1
progressView.setProgress(Float(webView.estimatedProgress), animated: true)
}
}
1.16
1》约束详解: https://www.jianshu.com/p/d67395deb694
translatesAutoresizingMaskIntoConstraints
translatesAutoresizingMaskIntoConstraints 的本意是将 frame 布局 自动转化为 约束布局,转化的结果是为这个视图自动添加所有需要的约束,如果我们这时给视图添加自己创建的约束就一定会约束冲突。
为了避免上面说的约束冲突,我们在代码创建 约束布局 的控件时 直接指定这个视图不能用frame 布局(即translatesAutoresizingMaskIntoConstraints=NO),可以放心的去使用约束了。
2》
http://app.u17.com/v3/appV3_3/ios/phone/comment/list
1.19
1、字符串拼接方法:\()\()
textView.text = "【\(model.comic?.cate_id ?? "")】\(model.comic?.description ?? "")"
2、swift的cell中设置model(oc中cell的setModel方法)在model的didSet中方法设置:
var model: DetailStaticModel? {
didSet{
guard let model = model else { return }
textView.text = "【\(model.comic?.cate_id ?? "")】\(model.comic?.description ?? "")"
}
}
1.30:
@inlinable
这些对于应用程序不是必须的,开发者可以将一些公共功能注释为@inlinable,这给编译器提供了优化跨模块边界的泛型代码的选项。
2.1
1、swift循环引用: https://www.jianshu.com/p/f9bc8afd5b53
1》强引用,弱引用
2》循环引用和内存泄漏:
2.1》变量间互相引用 引起的泄露
引用类型间的互相引用会出现循环引用;因为引用或传递引用类型的变量时不会在内存中重新分配地址;像struct之间互相引用就不会发生循环引用;
值类型间的互相引用不会出现循环引用;因为引用或传递值类型的变量时会在内存中重新分配地址;像class之间互相引用就会发生循环引用;
避免方法:使用weak
2.2》Protocol(委托代理模式中) 引起的泄露
委托代理模式中class、struct、enum都可以用Protocol,但是只有class时才会引起循环引用,所以,只有在class中使用Protocol才需要weak修饰delegate属性,因此,声明协议时,指定只有class类型的变量可以代理它(
即:protocol ListViewControllerDelegate : class {
func configure(with list : [Any])
}
)
避免方法:使用weak 和 class
2.3》Closure 引起的泄露:(对象对closure有一个强引用,同时在closure的代码块中又对该对象本身有一个强引用。这样就引起了循环引用的发生)
避免方法:使用[unowned self] 和 [weak self]
使用[unowned self] 的时候需要注意的一点是:调用closure的时候如果对象已经被释放的话,会出现crash;
[weak self] 和[unowned self] 的区别是 [weak self]处理的时候是一个可选类型。
2、Date()
2.2
设置tableview的tableView:heightForHeaderInSection 或 tableView:heightForFooterInSection的高度为0:
以前设置为0.01会导致像素偏移,最佳方法是设置为float中的最小非零数字
oc是FLT_TRUE_MIN ;swift CGFloat.leastNonzeroMagnitude 表示CGFloat支持的最小非零十进制数值
(注:oc是FLT_MIN swift是CGFloat.leastNormal;Magnitude 表示CGFloat支持的最数值,包括零)
https://www.jianshu.com/p/103b79891e8b