一、闭包 Swift 中闭包的概念就像是Objective-C中的block。OC中的block类似于匿名函数,闭包是用来定义函数, 同时闭包可以嵌套和作为参数传递。 在 Swift 中,函数也只不过是一种特殊的闭包。 Swift 中的闭包有很多优化的地方:
1 2 3 4 5 6 7 8 9 1 根据上下文推断参数和返回值的类型2 从单行表达式闭包中隐式返回 可以省略return 3 可以使用简化的参数如 $0 $1 意为从0 或者1 开始4 提供了尾随闭包的语法
各种格式下的闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 let b = { print ("这也是闭包" ) } b() let countNum = {(num1:Int ,num2:Int )->Int in return num1+ num2; } let count1 = self .countNum(2 ,3 )print ("count1: \(count1) " )let countNum = {(num1:Int ,num2:Int )-> () in let aa = num1+ num2; print ("count2: \(aa) " ) } countNum(222 ,333 ) let countNum11 = {()-> Int in self .global = 800 return self .global! ; } let newValue = countNum11()print ("count3: \(newValue) " )闭包的调用都带有 ()
@noescape 和 @escaping
简单的介绍就是如果这个闭包是在这个函数结束前内被调用,就是非逃逸的即noescape。 如果这个闭包是在函数执行完后才被调用,调用的地方超过了这函数的范围,所以叫逃逸闭包。
举个例子 就是我们常用的masonry或者snapkit的添加约束的方法就是非逃逸的。因为这闭包马上就执行了。 网络请求请求结束后的回调的闭包则是逃逸的,因为发起请求后过了一段时间后这个闭包才执行。 比如这个Alamofire里的处理返回json的completionHandler闭包,就是逃逸的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 loadData { (result) in print ("获取json信息\(result) " ) } func loadData (completion : @escaping (_ result: [String ])->()) -> () { DispatchQueue .global().async { print ("耗时操作\(Thread.current) " ) Thread .sleep(forTimeInterval: 1.0 ); let json= ["12" ,"23" ,"34" ] DispatchQueue .main.async(execute: { print ("主线程\(Thread.current) " ) completion(json) }) } }
定义闭包属性的问题
1 2 3 4 5 6 7 8 在Swift中,如果在某个类中定义一个属性,那么这个属性必须要初始化,否者会报错, 如果暂时不想初始化,那么可以在后面写上一个 ? 号 但是在定义闭包的属性时,一定要注意,以下这种写法是最常见的一种错误写法: var finished: () -> ()? var finished: (() -> ())?
闭包的循环引用问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 在Swift开发中,有一个原则就是能不写self就不写self,但是在闭包中必须写上self; 这是因为闭包是用来保存一段代码,而且系统也不知道这段代码具体的调用时间, 所以为了保证闭包中的对象不被释放,需要 self 进行一次强引用;这其实和Block中的原理差不多 。 所以以后看到self 基本上都和闭包有关系。 (这也是闭包中循环引用来源的原因) 下面我举一个简单的关于闭包循环引用的例子: 你定义了两个控制器:OneController和TwoController,OneController只是负责push出TwoController , 我们在TwoController中进行一些关于闭包的操作,然后在pop返回的时候查看该控制器是否被销毁了, 来验证闭包是否发生了循环引用问题; 在TwoController中我们只需要简单写一些代码即可: var finished: (() -> ())? func loadData (finished : () -> ()) { print ("调用了" ) self .finished = finished finished(); } loadData { () -> () in print ("回调了" ) self .view.backgroundColor = UIColor .redColor() } deinit { print ("控制器被销毁了" ) } 当我们执行以上代码,并且从TwoController返回到OneController时, TwoController的deinit方法没有被调用,表明TwoController没有被销毁,闭包存在了循环引用的问题; 这是因为:控制器通过闭包属性引用闭包,而闭包中又强引用着self (控制器),所以导致了循环引用的问题; Swift中关于循环引用的解决方案 weak var weakSelf = self 那么原先代码中只需要把self 改成weakSelf即可 weakSelf! .view.backgroundColor = UIColor .redColor()
二、代理 流程基本和OC中的一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import UIKit@objc protocol mydelegate { func FF1 (a :String ) @objc optional func FF2 (aa :String ) } class MyTest : NSObject { weak var delegate:mydelegate? func test () -> () { self .delegate? .FF1 (a: "代理方法1" ) } } class ViewController : UIViewController ,mydelegate {override func viewDidLoad () { super .viewDidLoad() let myTest = MyTest () myTest.delegate = self myTest.test() } func FF1 (a :String ){ print ("代理:\(a) " ) } } 代理:代理方法1
三、通知 流程基本和OC中的一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class MyTest : NSObject { func test () -> () { NotificationCenter .default.post(name: NSNotification .Name .init ("test" ), object: self , userInfo: ["post" :"哈哈" ]) } } override func viewDidLoad () { super .viewDidLoad() NotificationCenter .default.addObserver(self , selector: #selector (test), name: NSNotification .Name .init ("test" ), object: nil ) let myste = MyTest () myste .test() } @objc func test (nofi : Notification ) -> Void { let str = nofi.userInfo! ["post" ] let str1 = str as! String print ("通知传来了: \(str1) " ) } deinit { NotificationCenter .default.removeObserver(self ) }
四、构造函数 普通构造函数 1 2 3 4 5 6 7 8 9 init (name : String , age : Int ) { self .name = name self .age = age super .init () } let myste = MyTest .init (name: "bady" , age: 19 )
构造函数-KVC构造 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @objc var name: String ? @objc var age: Int = 0 init (dict : [String : AnyObject ]){ super .init () setValuesForKeys(dict) } override func setValue (_ value : Any ? , forKey key : String ) { super .setValue(value, forKey: key) } override func setValue (_ value : Any ? , forUndefinedKey key : String ) { print ("no: \(value) ,\(key) " ) } let dic = ["name" :"zhangsan" ,"age" :2 ,"title" :"nini" ] as [String : Any ] let myste = MyTest .init (dict: dic as [String : AnyObject ]) print ("name: \(myste.name) \n age:\(myste.age) " )
在swift 4.0中使用系统方法setValuesForKeys()进行赋值,明明有这个属性,但它还是走 override func setValue(_ value: Any?, forUndefinedKey key: String) 这个未定义的方法 原因是:swift 4.0 版本要在属性前面加@objc
五、懒加载 格式: lazy var 变量: 类型 = { 创建变量代码 }() 懒加载的写法本质上是定义并执行一个闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 lazy var View : UIView = { let view = UIView (frame:CGRect (x: 0 , y: 0 , width: 100 , height: 100 )) view.backgroundColor = UIColor .redColor() return view }() lazy var zyTableView: UITableView = { let tempTableView = UITableView (frame: self .view.bounds, style: UITableViewStyle .plain) tempTableView.delegate = self tempTableView.dataSource = self return tempTableView }() lazy var dataList: [String ] = { print ("我懒加载了" ) return ["lnj" , "lmj" , "why" ] }()
六、重写属性的Set、get发方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class MyTest : NSObject { private var _tittle: String ? var tittle: String ?{ set { print ("重写了set方法!" ) _tittle= newValue } get { return _tittle } } } myste.tittle = "asd"