31.Any、NSObject、AnyObject的区别
- Any : public typealias Any = protocol<>
- 是"零”个协议,任何类型都遵循了这个协议,涵盖范围最大
AnyObject: @objc public protocol AnyObject {}
是一个协议内容为空的协议,涵盖范围次之, 所有的 class 都隐式地实现了这个接口
-
NSObject: public class NSObject :
- NSObjectProtocol遵循了NSObjectProtocol协议的类,涵盖范围最小: (NSObject类或其子类的实例对象)
32.可选链的使用
概念: 它的可选性体现于请求或调用的目标当前可能为空(nil)
( 如果可选的目标有值,那么调用就会成功;如果选择的目标为空(nil),则这种调用将返回空(nil))多次调用被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
可选链的使用:
①在可选类型后面放一个问号,可以定义一个可选链。
②可选值后面放一个叹号来强制拆得其封包内的值( 一般的强制解析很容易引发运行时错误。)
33.协议
1.协议基本使用:
1)协议的格式: 协议的定义方式与类,结构体,枚举的定义都非常相似
protocol SomeProtocol {
// 协议方法
}
2)遵守协议的格式:
class SomeClass:SomeSuperClass ,FirstProtocol, AnotherProtocol{
// 类的内容
//实现协议中的方法
}
3)定义协议和遵守协议: 类, 结构体, 枚举都可以遵循协议
4)协议之间的继承:
protocol CrazySportProtocol {
func jumping()
}
protocol SportProtocol : CrazySportProtocol {
func playBasketball()
func playFootball()
}
2.协议中代理使用:
概念:即协议继承用于代理设计模式
protocol BuyTicketProtocol {
func buyTicket()
}
class Person {
// 1.定义协议属性
var delegate : BuyTicketProtocol
// 2.自定义构造函数
init (delegate: BuyTicketProtocol) {
self.delegate = delegate
}
// 3.行为
func goToBeijing() {
delegate.buyTicket()
}
}
class HuangNiu: BuyTicketProtocol {
func buyTicket() {
print("买了一张火车票")
}
}
let p = Person(delegate: HuangNiu())
p.goToBeijing()
注意:
代理属性, 一般都是使用weak修饰,而weak修饰的又必须是类类型的对象 所以, 一般要求, 协议继承自NSObjectProtocol / class
3.协议中的可选:
1)注意:
①在swift里面, 如果遵循了一个协议, 必须要实现, 协议里面所有的方法
② 协议可选, 是属于OC 的特性
2)使用:
@objc 修饰协议,@objc optional 修饰方法
// 1.定义协议
@objc
protocol SportProtocol {
func playBasketball()
@objc optional func playFootball()
}
// 2.遵守协议
class Person : SportProtocol {
var name : String?
var age : Int = 0
// 实现协议中的方法
@objc func playBasketball() {
print("人在打篮球")
}
}
34.泛型
- 概念:简单理解就是一个"泛化"的类型, 并不特指某一个具体的类型
- 使用:
①作为函数的参数或者返回值
②泛型与类型的结合:- 与类的结合
- 与协议的关联
- 与结构体的结合
③泛型与where子句的结合使用
func test<T>(a:T) where T:Person{}
35.闭包
1.基本使用:
闭包的介绍: 闭包和OC中的block非常相似
注意:
①OC中的block是匿名的函数,Swift中的闭包是一个特殊的函数
② block和闭包都经常用于回调-
闭包使用:
block写法:
类型: 返回值(^block的名称)(block的参数)
值: ^(参数列表) { //执行的代码 };闭包的写法: 类型: (形参列表)->(返回值) 值: { (形参) -> 返回值类型 in //执行代码 } 闭包的简写: 如果闭包没有参数, in和in之前的内容可以省略 httpTool.loadRequest( { print("回到主线程", NSThread.currentThread());} )
2.参数闭包/尾随闭包:
尾随闭包写法: ①如果闭包是函数的最后一个参数,则可以将闭包写在()后面 ②如果函数只有一个参数,并且这个参数是闭包,那么()可以不写 //方式一 httpTool.loadRequest() { print("回到主线程", NSThread.currentThread()); } //方式二 // 开发中建议该写法 httpTool.loadRequest { print("回到主线程", NSThread.currentThread()); }
36.闭包循环引用
1.原因:如果在HttpTool中有对闭包进行强引用,则会形成循环引用
class HttpTool: NSObject {
// 定义属性,来强引用传入的闭包
var callBack : (()->())?
func loadRequest(callBack : ()->()) { dispatch_async(dispatch_get_global_queue(0, 0)) {
() -> Void in
print("加载数据", [NSThread.currentThread()])
dispatch_async(dispatch_get_main_queue(), {
() -> Void in
callBack()
})
}
self.callBack = callBack //直接调用self,引发循环引用
}
}
补充:
在Swift中检测一个对象是否销毁,可以实现对象的`deinit`函数 // 析构函数(相当于OC中dealloc方法)
deinit{
print("ViewController----deinit")
}
2.解决方案:
方案一: 使用weak,对self使用弱引用。
(但是因为self可能有值也可能没有值, 因此weakSelf是一个可选类型,在真正使用时可以对其强制解包
该处强制解包没有问题,因为控制器一定存在,否则无法调用所在函数)
weak var weakSelf = self //(OC: __weak typeof(self) weakself = self )
httpTool.loadData {
print("加载数据完成,更新界面:", NSThread.currentThread())
weakSelf!.view.backgroundColor = UIColor.redColor()
}
方案二: 和方案一本质一样,只是书写方式更加简单 可以写在闭包中,此时闭包中用到的self都是弱引用
httpTool.loadData {
[weak self]
() -> () in
print("加载数据完成,更新界面:", NSThread.currentThread())
self!.view.backgroundColor = UIColor.redColor()
}
方案三:使用关键字`unowned`
从行为上来说 unowned 更像OC中的 unsafe_unretained
unowned 表示:即使它原来引用的对象被释放了,仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向 nil
httpTool.loadData {
[unowned self]
() -> () in
print("加载数据完成,更新界面:", NSThread.currentThread())
self.view.backgroundColor = UIColor.redColor()
}
37.懒加载的使用
- 介绍
swift中也有懒加载的方式,和OC不同的是swift有专门的关键字来实现懒加载,lazy关键字可以用于定义某一个属性懒加载(苹果的设计思想:希望所有的对象在使用时才真正加载到内存中) - 格式
lazy var 变量: 类型 = 函数名()
//构造函数或者自定义函数
lazy var 变量: 类型 = { 创建变量代码 }()
// 懒加载的本质是,在第一次使用的时候执行闭包,将闭包的返回值赋值给属性(lazy的作用是只会赋值一次)
lazy var array : [String] = {
() -> [String] in
return ["sz", "lmj", "lnj"]
}()
38.常见注释
①单行注释 //
②多行注释 /* */ 注意:和与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中
③文档注释:
/** */
或者
///
④分组注释
注意:swift中不可以再使用 `#pragma mark -` 改用 `// MARK:-`方式
⑤TODO 需要做
⑥FIXME 解决Bug
39.访问权限
OC中的访问权限:
@private : 作用范围只能在自身类
@protected: 作用范围在自身类和继承自己的子类,什么都不写,默认是此属性。
@public: 作用范围最大,在任何地方
@package: 本包内使用,跨包不可以
注意:
①只是用来修饰成员变量; 无法修饰方法
②@interface中的声明的成员变量默认是public,@implatation中声明的成员变量默认是privateSwift 中的访问控制权限:基于模块和源文件,类这三个概念
internal : 在本模块中都可以进行访问 (默认,子类也可以继承)
private : 当前类私有 (一个源文件中可以有多个类!!)
fileprivate: 在当前源文件中可以访问
public : 在其他模块中可以访问, 但不能被override,如果修饰类, 则无法继承
open: 在其他模块中可以访问, 并且可以被override,如果修饰类, 可以继承
注意:
①Swift访问权限, 作用于类, 属性, 方法等
②Swift 中的访问级别遵循一个基本原则:不可以在某个实体中定义访问级别更高的实体
40.异常
异常介绍
只要我们在编程,就一定要面对错误处理的问题。( 比如:只有使用Optional才能处理空值;)
Swift在设计的时候就尽可能让我们明确感知错误,明确处理错误如何描述一个错误?
在Swift里,任何一个遵从Error protocol的类型,都可以用于描述错误。
Error是一个空的protocol,它唯一的功能,就是告诉Swift编译器,某个类型用来表示一个错误。
通常,我们使用一个enum来定义各种错误的可能性。-
处理异常的方式
方式一: try方式,需要手动处理异常
do{
let result = try readFileContent("abc")
}catch{
print(error)
}方式二: try?方式,不处理异常,如果出现了异常,则返回一个nil.没有异常,则返回对应的值 let result = try? readFileContent("abc”) // 最终返回结果为一个可选类型 方式三: try!方法,告诉系统该方法没有异常. try! readFileContent("abc”) // 注意:如果出现了异常,则程序会崩溃
41.Swift调用OC
①根据系统提示,创建桥接文件
②手动配置桥接文件
42.OC调用Swift
①根据系统提示,创建桥接文件
②手动配置桥接文件: -Swift.h结尾,开头任意,配置好之后,用到Swift的地方导入你设置的头文件就好。
注意:
- swfit文件你想共享,跨模块得是Public或者open
- 如果是类, 必须继承自NSObject
- 如果是协议,必须用@objc修饰协议。
43.Playground高级使用
快速查看: 数值,image,color,URL,View
-
Sources 目录
- 原因:在 Playground写的代码,会被编译器实时编译,并运行将结果显示出来,造成效率低下
解决方案:
放到Sources 目录下的源文件会被编译成模块(module)并自动导入到 Playground 中并且只会编译一次。
使用注意: 需要使用public关键字修饰资源文件中, 需要暴露给外界的内容资源
原因:单一的Playground并不是一个完整的APP, 所以并没有使用沙盒机制,如果想要在Playground中, 使用某些资源(比如图片资源), 该怎样做呢?
解决方案: 将数据存放在资源中。
①独立资源:Resources目录,放置到此目录下的资源是每个 Playground 是独立的,可以通过 mainBundle 进行访问获取。
②共享资源:共享资源的目录是放在用户目录的Documents目录下的XCPlaygroundSharedDataDirectoryURL来获取共享资源目录的 URL
注意:需要先导入 XCPlayground 模块异步执行
原因: Playground 中的代码会从上到下执行,并在执行完毕之后立即停止.所以, 在Playground , 测试一些异步处理(比如网络请求) 一般情况, 就无法实现
-
解决方案:
步骤:
1.让Playground永远执行
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
或者
PlaygroundPage.current.needsIndefiniteExecution = true2.停止执行 XCPlaygroundPage.currentPage.finishExecution() 或者 PlaygroundPage.current.finishExecution() print("走到这里了") XCPlaygroundPage.currentPage.needsIndefiniteExecution = true let queue = DispatchQueue(label: "test") queue.asyncAfter(deadline: DispatchTime.now() + 2) { print("几秒后执行") XCPlaygroundPage.currentPage.finishExecution() }
多页面
-
解释:即可以把不同的代码, 放在不同的界面
界面间跳转:
//: Next//: [Previous](@previous) //: ["Go to AnyPage"](PageName) 注意:①Playground支持 markdown语法, 所以默认, 是以markdown语法的格式显示 ②如果需要跳转, 需要,设置渲染文档[图片上传中。。。(1)]
TimeLine : 捕捉动画
XCPlaygroundPage.currentPage.liveView
44.集成CocoaPods
注意:swift中podfile文件, 一定要添加use_frameworks!因为swift只支持framework格式的库。