协议
协议支持多继承
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
// 实现协议中的方法
}
- 类 结构体 枚举都可以遵守协议
// 1.定义协议
protocol SportProtocol {
func playBasketball()
func playFootball()
}
// 2.遵守协议
// 注意:默认情况下在swift中所有的协议方法都是必须实现的,如果不实现,则编译器会报错
class Person : SportProtocol {
var name : String?
var age : Int = 0
// 实现协议中的方法
func playBasketball() {
print("人在打篮球")
}
func playFootball() {
print("人在踢足球")
}
}
协议之间的继承
protocol CrazySportProtocol {
func jumping()
}
protocol SportProtocol : CrazySportProtocol {
func playBasketball()
func playFootball()
}
协议继承用于代理设计模式
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
在Swift中,如果遵守了一个协议,必须要实现,协议里面所有的方法
协议可选是OC的特性
- @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("人在打篮球")
}
}
泛型
简单的理解泛型就是一个"泛化"的类型,并不特指某一个具体的类型
- 泛型的使用
-
作为函数的参数或返回值
- 泛型与类型的结合
-
与结构体的结合
-
与类的结合
-
与协议的关联
-
- 泛型与where子句结合使用
-
闭包
- 闭包与OC中的Block非常相似
- OC中的block是匿名的函数
- Swift中的闭包是一个特殊的函数
- block和闭包都是经常用于回调
block的用法回顾
* 定义网络请求的类
@interface HttpTool : NSObject
- (void)loadRequest:(void (^)())callBackBlock;
@end
@implementation HttpTool
- (void)loadRequest:(void (^)())callBackBlock
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"加载网络数据:%@", [NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
callBackBlock();
});
});
}
@end
* 进行网络请求,请求到数据后利用block进行回调
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.httpTool loadRequest:^{
NSLog(@"主线程中,将数据回调.%@", [NSThread currentThread]);
}];
}
闭包格式
类型 类型:(形参列表)->(返回值)
{
值 (形参) -> 返回值类型 in
执行代码
}
使用闭包代替block
* 定义网络请求的类
class HttpTool: NSObject {
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()
})
}
}
}
* 进行网络请求,请求到数据后利用闭包进行回调
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// 网络请求
httpTool.loadRequest ({ () -> () in
print("回到主线程", NSThread.currentThread());
})
}
闭包的简写
如果闭包没有参数 in和in之间的内容可以省略
httpTool.loadRequest({
print("回到主线程", NSThread.currentThread());
})
尾随闭包
- 写法
- 如果闭包是函数的最后一个参数,则可以将闭包写在()后面
- 如果函数只有一个参数,并且这个参数是闭包,那么()可以不写
httpTool.loadRequest() {
print("回到主线程", NSThread.currentThread());
}
// 开发中建议该写法
httpTool.loadRequest {
print("回到主线程", NSThread.currentThread());
}
如果在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
}
}
在Swift中检测一个对象是否销毁,可以实现对象的deinit函数
循环引用解决方案
-
方案一
- 使用weak,对self使用所引用
- 但是self可能有值也可能没有值,因为weakSelf是一个可选类型,正真正使用时可以对其强制解包(该处强制捷豹没有问题,因为控制器一定存在,否则无法调用所在函数)
// 解决方案一: weak var 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() }
逃逸闭包 非逃逸闭包
// @escaping 逃逸闭包 // 可以被其他对象引用 -- 逃逸闭包 // 默认情况下, 闭包, 是不能被其他对象引用 -- 非逃逸闭包
懒加载
- 懒加载介绍
- 和OC不同的是swift有专门的关键字来实现懒加载
- lazy关键字可以用于定义某一个属性懒加载
- 格式
- lazy var 变量:类型 = 函数名() 构造函数
自定义函数 - lazy var 变量:类型 = {创建变量代码}()
- lazy var 变量:类型 = 函数名() 构造函数
- 懒加载的使用
// 懒加载的本质是,在第一次使用的时候执行闭包,将闭包的返回值赋值给属性
// lazy的作用是只会赋值一次
lazy var array : [String] = {
() -> [String] in
return ["sz", "lmj", "lnj"]
}()
注释
-
单行注释
- 单行注释以 // 作为起始标记
- // 注释内容
-
多行注释
- 以/*开头
- 以*/结尾
- 和C语言多行注释不同的是,Swift的多行注释可以嵌套多行注释
/* 内容 /* 内容 */ 内容 */
-
文档注释
-
格式 1
/** 方法的含义描述 - parameter path: 路径 - throws: 抛出异常 - returns: 返回值 */
格式 2
/// 方法的功能描述 /// - parameter a 参数a的含义描述 /// - parameter b 参数b的含义描述 /// - throws: 异常描述 /// - returns: 返回值描述
快捷键 command + option + /
格式 3
/// 方法的功能描述 /// * 描述1 /// * 描述2 /** 描述 描述 */ /// - parameter a: 参数a的含义描述 /// - parameter b: 参数b的含义描述 /// - throws: 异常描述 /// - returns: 返回值描述
-
-
分组注释
- Swift中不可以再使用 '#pragma mark - '
- 如果打算对代码进行分组可以使用 '//MARK:-'方式
- MARK : //MARK: -
- TODO : //TODO: - 需要做
- FIXME : //FIXME:解决bug
开启分组注释
因为默认的话 TODO 跟 FIXME 是没有开启的,所以需要我们手动开启
步骤 一
步骤二
开启注释代码 直接复制粘贴就好了
TAGS="TODO:|FIXME:"
echo "searching ${SRCROOT} for ${TAGS}"
find "${SRCROOT}" \\( -name "*.swift" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($TAGS).*\\$" | perl -p -e "s/($TAGS)/ warning: \\$1/"
优点:
访问权限
OC中的访问权限
@private:作用范围只能在自身类
@protected:作用范围在自身类和继承自己的子类,什么都不写,默认是此属性。
@public:作用范围最大,在任何地方
@package:本包内使用,跨包不可以
- 注意
- 只是用来修饰成员变量,无法修饰方法
- @interface中的声明的成员变量默认是public,@implatation中声明的成员变量默认是private
Swift中的访问控制模型基于模块和源文件,类这三个概念
internal:在本模块中都可以进行访问 默认的 子类可以继承
private:当前类私有
fileprivate:在当前源文件中可以访问
public:在其他模块中可以访问,但不能被override,如果修饰类,则无法继承
open:在其他模块中可以访问,并且可以被override,如果修饰类,可以继承
- 注意
- Swift访问权限,作用与类,属性,方法等
- Swift中的访问级别遵守一个基本原则:不可以在某个实体中定义访问级别更高的实体
方法抛出异常
-
异常的介绍
- 只要我们在编程,就一定要面对错误处理的问题.
- Swift在设计的时候就尽可能让我们明确感知错误.明确处理错误 *比如:只有使用Optional才能处理空值
- 如何描述一个错误?
- 在Swift里,任何一个遵从Error protocol的类型,都可以用于描述错误
- Error是一个空的protocol,它唯一的功能,就是告诉Swift编译器,某个类用来表示一个错误.
- 通常,我们使用一个enum来定义各种错误的可能性
-
异常的示例
-
当我们调用方法获取结果为nil时,你并不能确定到底参数什么错误得到了nil
func readFileContent(filePath : String) -> String? {
// 1.filePath为""
if filePath == "" {
return nil
}// 2.filepath有值,但是没有对应的文件 if filePath != "/User/Desktop/123.plist" { return nil } // 3.取出其中的内容 return "123" } readFileContent("abc") ```
使用异常对上述方法进行改进
// 1.定义异常 enum FileReadError : Error { case FileISNull case FileNotFound } // 2.改进方法,让方法抛出异常 func readFileContent(filePath : String) throws -> String { // 1.filePath为"" if filePath == "" { throw FileReadError.FileISNull } // 2.filepath有值,但是没有对应的文件 if filePath != "/User/Desktop/123.plist" { throw FileReadError.FileISNull } // 3.取出其中的内容 return "123" }
-
-
处理异常的方式
- 1 try方式,需要手动处理异常
do { let result = try readFileContent("abc") } catch { print(error) }
- 2 try?方式,不处理异常,如果出现了异常,则返回一个nil,没有异常则返回对应的值
// 最终返回结果为一个可选类型 let result = try? readFileContent("abc")
- try!方法,告诉系统改方法没有异常
// 注意:如果出现了异常,则程序会崩溃 try! readFileContent("abc")
Swift调用OC
方式一 创建OC文件时提示,点击Create Bridging Header
方式二
导入桥接文件的全路径
在桥接文件中,直接导入OC的头文件,就可以使用了
OC调用Swift
- 注意
- 如果想让Swift类/方法/属性, 在OC中使用; 需要使用public关键字对类/方法/属性等进行修饰
- 如果是类, 必须继承自NSObject
-
如果是协议
-