六, ==Optinal== 可选类型(难点):
swift特色语法之一;是一种对类型安全的体现,
Int?,float!
注意的是后面的符号必须紧贴前面的类型,中间不能有任何的空白;- 只有Optional类型可以为空值(oc中的nil),确保非optional的对象不能为空,也无法赋值为空;
- 任何的类型都可以作为Optional类型:
let tupleTest :(Int,String)? = nil
; - 访问属性或实例方法时候,不必每次去判断Optional对象是否为空,而使用Optional链表达式;
基本使用
- Optional:
var testOp1 :Optional<Int8> = 1008611
- String? 这里?前面不能有空格
- Optional(name) 如果一个可选类型中没有值,强制解包会报错
- name! 可选类型强制解包(不解包就是Optional(name))
// 声明Optional变量testOptF类型为 Int?,初始化为空 var testOptF: Float? = nil var name: String? = nil // 这里使用optional链操作符, // 在这里会先判断testOptF是否为空,不为空则执行a = 10操作, // 否则不执行任何操作。 testOptF? = 3.1415// // 这条语句同上面一样 testOptF? += 10 // 这里输出:testOptF = nil print("testOptF = \(String(describing: testOptF))") testOptF! += 10 print("testOptF! = \(testOptF!)") if testOptF != nil { }
- Optional:
强制拆解:
从某处代码开始,已经可以确保该对象不为空了,这时候可以使用强制拆解;即:!表示其操作数表达式一定不为空
Optional 绑定
if let obj = Optional(0) { } 或: if var obj = Optional(0) { }
这里表示,声明了一个对象odj且使用=后面的Opeional对象表达式对其进行初始化操作,如果obj不是空的则执行if中的语句;
需要注意的是绑定解包中更多的用的是guard
语句,因为guard语句中的解包在guard的作用域内是有效的
fileprivate func testGuardBinding(){
let testA = Int?(2)
guard var tempA = testA else{
//这里打印会报错
// print("tempA inner = \(tempA)")
exit(1)
}
tempA += 10
print("tempA outdoor = \(tempA)")
}
Optional 介绍
- Optional本质是遵循ExpressibleByNilLiteral协议的泛型枚举类型;苹果API中定义如下:
public enum Optional<Wrapped> : ExpressibleByNilLiteral { case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) /// Creates an instance that stores the given value. public init(_ some: Wrapped) public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? public init(nilLiteral: ()) public var unsafelyUnwrapped: Wrapped { get } }
- 可以看出有两个枚举值:none 标示空值,sone当赋值后就存储在这里;
常用方式及使用
例子
///c测试可选类型 func testOptionals() -> () { let testOp1 :Optional<Int> = 1008611 print("定义方式一:未解包前类型和值是:\(String(describing: testOp1))") // let testInt8 :Int8 = Int8(testOp1!)错误 // print("int转成int8是:\(testInt8)") let testOpt2 = Optional<String>("第二种定义方式") print("定义方式二:未解包前类型和值是:\(String(describing: testOpt2))") var testOpS : String? testOpS = "4334675444635" print("定义方式三:未解包前类型和值是:\(String(describing: testOpS))") print("强制解包类型和值是:\(testOpS!)") if let deOptionS = testOpS { print("绑定解包类型和值是:\(deOptionS)") let reserint64 = Int64.init(deOptionS) print("转换成int64是:\(reserint64 ?? 12)") if let tempint64 = reserint64 { let reserNSNum = NSNumber.init(value: tempint64) print("转换成NSNumber是:\(reserNSNum)") } } let refClosure: ((_ cunS : String?)-> String?)? = {(_ cus:String?)-> String? in print("\(cus!)-->这是一个闭包引用") return "测试用例" } let testClosWithOpt = refClosure!("测试参数") print("测试闭包与可选类型======\(testClosWithOpt ?? "error")") let ss: NSString = "9876543210" let ll: Int64 = ss.longLongValue print("转成 int64是:"+"\(ll)") }
输出结果
定义方式一:未解包前类型和值是:Optional(1008611) 定义方式二:未解包前类型和值是:Optional("第二种定义方式") 定义方式三:未解包前类型和值是:Optional("4334675444635") 强制解包类型和值是:4334675444635 绑定解包类型和值是:4334675444635 转换成int64是:4334675444635 转换成NSNumber是:4334675444635 测试参数-->这是一个闭包引用 测试闭包与可选类型======测试用例 转成 int64是:9876543210
七, 函数
- 简述:
- swift中的函数,总是由##func##关键词引出,后跟函数名,形参列表(在括号内有逗号分隔,可缺省只加双括号)及返回值(由==->==引出,可缺省),函数体;
- 返回类型可以是swift支持的任意类型;当返回值不适用的时候应使用占位符例:
_ = testFunc()
,否则会有警告; - 函数参数有实参标签和形参标签。
- 实参标签当某一个不需要的时候可以用“_”占位符标识缺省;
- 定义
func 函数名(参数类型) -> 返回值 {
代码块
return 返回值
}
实参标签
- 实参标签主要是提供给调用者解释所需传递实参的意义,形参名主要用于函数体内部的引用;
- 当有多个形参时候,实参标签可以相同,但是形参必须不同;
- 当返回类型是带有标签的元组,函数调用接收的返回值得元组也必须是带有标签的元组;
func getName(user_id userID: Int) -> String{ return "haha" } //user_id实参标签 userID:形参名字 func funNAME(_ name: Int) { print("parameter is: \(name)") } funNAME(10)
函数的默认形参值
带有默认形参值得函数,在函数调用时候其所对应的参数可以缺省;函数调用时候提示如下:
let _ = self.testDefaultAgr(test1: 3) func testDefaultAgr(test1 sub:Int,test2Def arg:Int = 5) -> Int{ return sub+arg }
- 默认形参可以在任何位置;但是如果放在没有默认形参的形参中间的时候,最好做标记以区分;
输入输出形参
- 默认的形参都是常量,函数体内修改会发生编译错误;
- 输入输出形参,在函数内修改的同时一会影响其对应的实参值(类似于c中的指针传参);但是swift中是以”输入-处理-输出”的数据流处理方式;
- 调用前:拷贝实参值到形参中;
- 执行函数体时:对输入输出形参的值进行修改;
- 函数返回后:将输入输出形参的值拷贝回去;
inout 修饰输入输出形参,调用函数时要在对应的实参前加&,表示该参数可以被形参所修改;
var tempI = 8 self.testFuncAgrInout(test1: 4, test2Def: &tempI) print("修改后的输入输出实参是:\(tempI)") //函数 func testFuncAgrInout(test1 sub:Int,test2Def arg:inout Int){ print("传入的输入输出形参是:\(arg)") arg += sub //传入的输入输出形参是:8 //修改后的输入输出实参是:12 }
- 函数重载
同一作用域或者同一名字空间中出现一组函数名相同,但是具有不同返回类型或者具有不同的形参个数或者形参类型,这就叫重载;
func testreset(test arg:Int) -> Int{ return 6+arg } func testreset(test1 sub:Int,test2Def arg:Int = 5) -> Double{ return Double(sub+arg) } func testreset(test1 sub:Int,test2Def arg:Int,test3Def arg2:Int) -> Int{ return sub+arg+arg2 }
函数类型及函数对象
- 可以用一个函数类型声明指向它的一个引用对象,称为函数引用对象;
// 函数引用 // 指向函数类型的引用变量 let ref = testreset(test:) let _ = ref(1)
函数类型作为参数
//定义一个函数类型的函数 func testreset(test arg:Int) -> Int{ return 6+arg } //函数类型作为参数 func testRefFun(refFun fun:inout (Int)->Int) -> Void { let t = fun(8) print("函数引用类型\(t)") } // 函数调用 var ref = testreset(test:) self.testRefFun(refFun: &ref) // 结果:函数引用类型14
8.嵌套函数
- 一个函数可以定义在函数的内部;
嵌套在内部的函数可以作为函数的返回值;
func testFunInFunc() -> Void { print("函数1") func testInner(){ print("inner func") } testInner() }
- 函数引用体系
一个引用对象除了可以引用函数外还可以引用结构体,闭包,枚举,以及类类对象的实例方法等。
- 函数引用体系
========================================修改于2017-09-19=======================================
八, 类
存储值
如果类型是结构体或者类。通常定义为可选类型
如果是基本属性类型,则直接在定义的时候初始化
class Persion : NSObject { // 存储属性 var name : String? var age : Int = 0 var money : Double = 0.0 //计算属性 var avarageSorce :Double { get { return money; } } // 类属性 static var courseCount : Int = 0 } //实例化 let stu = Persion() stu.name = "hhh" print(stu.avarageSorce) Persion.courseCount = 45//类属性,必须用类调用
属性监听
var name : String?{ //在该方法中,系统有一个标识符 willSet { print(name ?? "haha") print(newValue ?? "444") } didSet { print(name!) print(oldValue ?? "haha") } }
- 类的初始化方法
-
类似于oc的init方法
override init() { //可以不调用,如果不调用系统会默认调用 // super.init() } //自定义初始化构造函数 init(name : String , age : Int) { self.name = name; self.age = age; }
类的析构函数
- 不能显示的去掉用该方法,只能同过运行时的引用计数自动去调用,
- 如果一个不显示的提供一个析构方法,则会自动添加一个默认的析构方法,
deinit { print("Father is destroyed") }
九,闭包
简述–可以在你的代码中传递并使用的一种代码块
闭包是其函数与其执行环境(#所在函数中的局部对象与自身所在环境的对象做一个值或者引用的关联映射–name binding#)作用的一种记录;闭包可以捕获他所在作用域内(函数)的局部对象,或者说可以直接访问所在函数的局部变量;
通用范式
//因为具有**函数尾随闭包**的特性,所以其不能以单独使用的形式出现
_ = {(参数列表...) -> 返回类型 in
/**
闭包执行代码
*/
}
- 调用方式
//注意:在swift中换行符与“;”都属于一条语句的结束符
_ = {(para1:Int) -> Void in
print("这是一个闭包自调用测试")
}(5)
使用
class testFun: NSObject { //闭包类型:(参数列表)->(参数) func requ(callback : @escaping ()->()) -> () { DispatchQueue.global().async { () -> Void in let currentThread = Thread.current print("当前线程为\(currentThread)") DispatchQueue.main.async { () -> Void in let currentThread = Thread.current print("回调 当前线程为\(currentThread)") callback() } } } } //实例化后直接调用 testFunC?.requ(callback: { print("开始请求数据了") })
循环引用问题
weak var weakSelf = self;
- 作用在函数中
func testFunc() -> (Int,Int) -> Void {
return {(para1:Int,para2:Int) -> Void in
print("测试闭包作为函数返回值")
}
}
//调用
_ = testFunc()(1,2)
- 简略表达式
swift中闭包可以自己(编译器)推导出参数类型和返回类型;
ref = {(a,b) in return a + b}
//在确定其形参类型和返回类型,可以直接使用下列的方式
var ref:(Int,Int) -> Int = {$1 + $2}
print("\(ref(4,5))")
fileprivate func testBlock(){
//这里显示注明了闭包类型,形参及返回值都可以省略
let ref:(Int,Int) -> Void = {a,b in
print("a=\(a) b=\(b)")
}
ref(3,4)
//最简写法
let ref1:(Int,Int) -> Int = {$0 + $1
}
print(" b=\(ref1(9,10))")
}
=============修改于 2018-03-15===============
十,协议
协议:提供了对某类具有相同特性的一组公共属性或操作
1. 使用protocol来定义协议,协议只能存放计算式属性和方法声明,协议相当于接口
2. 使用
* 定义协议
//定义协议
protocol testProtoDelate {
func getSomething(someThing someS:String?) -> String?
}
//设置代理
var testDelgate :testProtoDelate?
//触发协议
let eatS = testDelgate?.getSomething(someThing: "帮我打个电话吧")
print("好了吗? "+eatS!)
* 代理方代理
```
//遵守协议
let FunC = testFun()
FunC.testDelgate = self
// extension中实现代理,testProtoDelate
func getSomething(someThing someS: String?) -> String? {
print("协议 告诉我 \(someS!)")
return "电话已经打完了!"
}
```
----------------------------------------修改于2017-09-07-----------------------------------------
十一, 命名空间
为了避免命名冲突Swift中新增了一个叫做命名空间的概念,
- 不同项目中的命名空间默认情况下的名称就是当前项目的名称;
- Swift可以通过命名空间来解决重名的问题,在Swift开发时我们应尽量使用cocoapods来集成三方框架, 这样可以有效的避免类名重复;
- 通过一个字符串来创建一个类和OC中也不太一样, OC中可以直接通过类名创建一个类, 而Swift中如果想通过类名来创建一个类必须加上命名空间;
// 1.动态获取命名空间 // 由于字典/数组中只能存储对象, 类型是AnyObject? guard let name = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { NJLog("获取命名空间失败") return } let cls: AnyClass? = NSClassFromString(name + "." + childControllerName) // Swift中如果想通过一个Class来创建一个对象, 必须告诉系统这个Class的确切类型 guard let typeCls = cls as? UITableViewController.Type else { NJLog("cls不能当做UITableViewController") return } // 通过Class创建对象 let childController = typeCls.init() NJLog(childController)
注:之所以是AnyObject?,是因为字典或者数组等存储的是AnyObject类型, 取值时候如果key写错或者没有对应的值, 那么就可能导致异常;
十二, 访问等级
swift提供五中访问等级:
如果没有被任何访问等级的显示修饰限定,那么默认为internal
关键字 | 范围 | 说明 | 子类继承 | 存储属性 | 计算属性及方法 |
---|---|---|---|---|---|
open | 其他任何没有被final修饰的属性和方法 | 只能用于类类型及类类型成员, | 能 | 子类可以重写该属性的的属性和属性观察者 | 子类都可以重写 |
public | 能被其他模块使用,所有文件作用域中的基本元素及属性和方法 | 不能继承其当前所定义的类类型 | 能 | 不能修改类类型中的属性和方法 | |
internal | 与public相同,但是只能在当前模块内使用 | 只能当前模块中的各个源文件访问 | |||
fileprivate | 与public相同 | 只能在当前的源文件的作用域内可见(3.0引入) | |||
private | 修饰某个类型中的属性和方法 | 对当前类型或该类型的对象可见 |
==注:不管是类,枚举,结构体,其初始化方法最高只能使用public,累的析构器方法不能用任何等级方位修饰==
一个实体的访问等级必须<=其所关联的实体中最小访问等级:
- 一个public的对象,不能用比public访问等级低的类型来定义;可能会导致低等级出现他们不应该可见的地方
- open等级的类不能继承<其访问等级的类,这些低能级访问类会暴露其不可见的地方
- 函数的访问等级不能高于其返回类型及形参
- 一个类被限定为fileprivate,其成员默认具有fileprivate属性,类限定为open,public,internal,其成员默认为internal访问等级;
- 自定义类型中可以限定访问等级比类型高或者低的访问等级,但是其他文件访问无法突破该类的型的限定范围;
元组的访问等级是其元素中等级最低的,枚举不能定义每一个元素的限定等级,
- 子类的访问等级不能高于父类,子类重修可以提升限定等级但是不能降低;
—————————————-修改于2017-09-11—————————————–