美文网首页
Swift| 基础语法(四)

Swift| 基础语法(四)

作者: 進无尽 | 来源:发表于2018-05-16 20:20 被阅读0次

    前言

    总结下 swift下的基础语法,里面涉及到:常量&变量、Swift中的数据类型、逻辑分支、循环、字符串相关、数组和字典、方法的书写调用等内容,考虑到阅读体验分多篇来展示,希望对大家学习swift有所帮助,同时也是对自己的一个总结。

    Swift| 基础语法(一)
    Swift| 基础语法(二)
    Swift| 基础语法(三)
    Swift| 基础语法(四)
    Swift| 基础语法(五)

    本文涉及:

    • 闭包
    • 代理
    • 通知
    • 构造函数
    • 懒加载
    • 重写属性的Set、get发方法

    一、闭包

    Swift 中闭包的概念就像是Objective-C中的block。OC中的block类似于匿名函数,闭包是用来定义函数, 同时闭包可以嵌套和作为参数传递。 在 Swift 中,函数也只不过是一种特殊的闭包。
    Swift 中的闭包有很多优化的地方:

    1 根据上下文推断参数和返回值的类型
    2 从单行表达式闭包中隐式返回 可以省略return
    3 可以使用简化的参数如 $0 $1 意为从0或者1开始
    4 提供了尾随闭包的语法
    
    //语法   parameters参数 return 隐藏了
    //{(parameters) -> return type in
    //    parameters
    //}
    

    各种格式下的闭包

    //最简单的闭包//省略in的
    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闭包,就是逃逸的。
    
    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)
            })
        }
    }
    

    定义闭包属性的问题

    在Swift中,如果在某个类中定义一个属性,那么这个属性必须要初始化,否者会报错,
    如果暂时不想初始化,那么可以在后面写上一个 ? 号
    
    但是在定义闭包的属性时,一定要注意,以下这种写法是最常见的一种错误写法:
      /** 当前写法代表闭包的返回值可以是nil,而不是初始化的闭包 */
      var finished: () -> ()?
      /** 正确写法:需要在整个闭包的后面加问号 */
      var finished: (() -> ())?
    

    闭包的循环引用问题

    在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,导致循环引用
            self.view.backgroundColor = UIColor.redColor()
        }
    // 判断是否被销毁了
    deinit {
           print("控制器被销毁了")
    }
    
    当我们执行以上代码,并且从TwoController返回到OneController时,
    TwoController的deinit方法没有被调用,表明TwoController没有被销毁,闭包存在了循环引用的问题;
    
    这是因为:控制器通过闭包属性引用闭包,而闭包中又强引用着self(控制器),所以导致了循环引用的问题;
    
    Swift中关于循环引用的解决方案
    weak var weakSelf = self
    那么原先代码中只需要把self改成weakSelf即可
    // 由于weakSelf为可选类型,这里必须保证有值,所以加上!号
    weakSelf!.view.backgroundColor = UIColor.redColor()
    

    二、代理

    流程基本和OC中的一致

    import UIKit
    @objc protocol mydelegate {
        func FF1(a:String)     //必须实现的
       @objc optional func FF2(aa:String)  //可以选择是否实现的
    }
    
    /*在声明的protocol中为什么要用  @objc 呢? 
    因为 Swift 的 protocol 是可以除了 class 以外的其他类型遵守的,而对于像 struct 或是 enum 这样的类型,
    本身就不通过引用计数来管理内存,所以也不可能用 weak 这样的 ARC 的概念来进行修饰。
    想要在 Swift 中使用 weak delegate,我们就需要将 protocol 限制在 class 内。
    一种做法是将 protocol 声明为 Objective-C 的,这可以通过在 protocol 前面加上 @objc 关键字来达到*/
    
    class MyTest: NSObject {
        weak var delegate:mydelegate?
        /*一定要 weak var  delegate: AADelegate?  去声明你的代理属性,避免循环引用问题*/
        func test() -> () {
            self.delegate? .FF1(a: "代理方法1")
             /* 为什么要 delegateOK.clickAction?() 中要有个问号呢
                原因是在OC的代码中, 用respondsToSelector()方法来判断是否实现了方法。
                而在swift 中就直接可以用 ? 来判断是否实现了方法。有时系统可能会给你自动提示,然后你就fix 一下*/
        } 
    }
    
    /*遵循协议*/
    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中的一致

    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)
    }
    

    四、构造函数

    普通构造函数

    //构造方法
    init(name: String, age: Int) {
        //当属性名和参数名相同时  需要加上 self来区分
        self.name = name
        self.age = age
        super.init()
    }
    
    let myste = MyTest.init(name: "bady", age: 19)
    

    构造函数-KVC构造

     @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)
    }
    //过滤掉 不存的在属性对应的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 变量: 类型 = { 创建变量代码 }()

    懒加载的写法本质上是定义并执行一个闭包

    //MARK: -View懒加载
    lazy var View: UIView = {
        let view = UIView(frame:CGRect(x: 0, y: 0, width: 100, height: 100))
        view.backgroundColor = UIColor.redColor()
        return view
    }()
    
    //MARK: -TableView懒加载
    lazy var zyTableView: UITableView = {
        let tempTableView = UITableView (frame: self.view.bounds, style: UITableViewStyle.plain)
        tempTableView.delegate = self
        tempTableView.dataSource = self
        return tempTableView
    }()
    
      //MARK: -数组懒加载
    lazy var dataList: [String] = {
            print("我懒加载了")
            return ["lnj", "lmj", "why"]
      }()
    

    六、重写属性的Set、get发方法

    class MyTest: NSObject {
    
      //定义一个变量
      private var _tittle: String?
      var tittle: String?{
          set{
              print("重写了set方法!")
              _tittle=newValue
          }
          get{
              return _tittle
          }
       }
    }
    
    myste.tittle = "asd"

    相关文章

      网友评论

          本文标题:Swift| 基础语法(四)

          本文链接:https://www.haomeiwen.com/subject/sqfqdftx.html