Swift3.0 基础语法2

主要分为以下几个部分:

  • 1.函数
    • 1.1 函数的定义
    • 1.2 闭包
    • 1.3 多线程GCD的变化
    • 1.4 闭包循环引用
    • 1.5 尾随闭包
    • 1.6 懒加载
  • 2.构造函数
    • 2.1 重载构造函数
    • 2.2 构造函数KVC
    • 2.3 便利构造函数
  • 3.setter和getter
    • 3.1 计算型属性
    • 3.2 反射机制
    • 3.3 利用反射机制设置根控制器
  • 4.Swift应用
    • 4.1 加法计算器
    • 4.2 加法计算器(便利构造函数)
    • 4.3 runtime加载属性列表
    • 4.4 session使用
    • 4.5 项目零散总结

1.函数

1.1函数的定义

函数格式 : func 函数名(形参列表) -> 返回值类型

 func sum(x:Int, y: Int) -> Int {
    
    return x + y
}

外部参数: 1.外部参数就是在形参前面加一个名字,不会影响函数内部细节,并且会让函数看起来更加直观。如果外部参数使用‘_’,在外部调用函数时,会忽略形参的名字.

 func sum2(_ x:Int,_ y: Int) -> Int {
   
    for _ in 0..<10 {
        
        print("hello world")
    }
    
    
    return x + y
}

默认值: 通过给参数设置默认值,在调用的时候就可以组合参数,如果不能指定的,就可以使用默认值

    func sum3(x: Int = 1, y: Int = 2) -> Int {
    
    return x + y
}

print(sum3())  //3
print(sum3(x: 10, y: 20))  //30
print(sum3(x: 10))     //12
print(sum3(y: 10))      //11

无返回值写法:

1.直接省略

 func demo1() {
    
    print("11")
}

2.用()
func demo2() -> () {
    print("11")
}

3.用Void
func demo3() -> Void {
    
    print("11")
}

1.2闭包

什么是闭包: 类似于OC中的block,主要作用是:提前准备好代码,在需要的时候执行。还可以当做参数传递。

1.没有参数,没有返回值的闭包

  () -> () 没有参数,没有返回值的函数
  如果没有参数,没有返回值,可以省略,连in一起省略
  let b1 = {
      
      print("hello")
  }
  
  //执行闭包
  b1()

2.带参数的闭包

 (Int) -> ()
 闭包中,参数,返回值,实现代码都是写在{}中
 需要使用一个关键字 “in” 分隔定义和实现
 { (形参列表 -> 返回值类型 in 实现代码)}
 let b2 = {
     
     (x: Int) -> ()
     
     in
     
     print(x)
 }
 
 b2(100)

3.带参数,带返回值的闭包

 类型:(Int) -> Int
 let b3 = { (x: Int) -> Int in
     
     return x
 }
 
 print(b3(10))

1.3 多线程GCD的变化

开启一个异步线程:

 DispatchQueue.global().async { 
        
        print("耗时操作 \(Thread.current)")
    
    }

回到主线程:

 DispatchQueue.main.async(execute: { 
     
     print("主线程更新UI \(Thread.current)")
     

 })        

模拟异步网络请求:并把一个闭包作为参数

func loadData(completion:@escaping (_ result : [String] ) -> ()) -> () {
    DispatchQueue.global().async {
        
        print("耗时操作 \(Thread.current)")
        
        //休眠
        Thread.sleep(forTimeInterval: 1.0)
        
        //获取结果
        let json = ["头条","八卦","出大事了"]
        
        //主队列回调
        DispatchQueue.main.async(execute: { 
            
            print("主线程更新UI \(Thread.current)")
            
            //回调
            completion(json)
        })
    }
}

@escaping:关键词,逃逸闭包 一些异步函数会使用逃逸闭包。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。
作用:可以理解为延长的闭包的生命周期,当函数返回时,闭包不会被销毁,而是可以等待被调用。

1.4 循环引用

为什么会产生循环应用的问题: 如果只是出现单方向的引用是不会产生循环引用的。如果self里面有一个属性对闭包进行了引用,而在闭包中也使用了self,就会造成循环引用问题。

解除循环引用:
1.先使用一个属性记录下闭包:

 var completionCallBack : (()->())?

 func loadData(complete: @escaping () -> ()) {
    
    //使用属性记录闭包 (循环引用)
    completionCallBack = complete;
    
    DispatchQueue.global().async { 
        
        
        DispatchQueue.main.async(execute: { 
            
            complete()
            
        })
    }
}

方法1:使用OC方法

   weak var weakSelf = self
    loadData {
        
        print(weakSelf?.view)
    }

细节:
   1. weak只能是用var修饰,不能用let,因为weak修饰的对象 可能会在运行时被修改->指向的对象一旦被释放,就会自动设置为nil
   2.解包有两种方式的解包,
    ?可选解包 -> 如果self已经被释放,不会像对象发送getter的消息,更加安全
    ! 强行解包 -> 如果self已经被释放,强行解包会导致崩溃

方法2:Swift 的推荐方法

 loadData { [weak self] in
        
        print(self?.view)
    }

细节:
   1.[weak self] 表示 {} 中的所有self 都是弱引用,需要解包

方法3:Swift 的另外一个用法(一般不要用)

loadData { [unowned self] in
       
        print(self.view)
    }

细节:
   1. [unowned self] 表示{} 中的所有self 都是assign的,不会强引用,但是,如果对象释放,指针地址不会变化
   2.如果对象被释放,继续调用,就会出现野指针

1.5 尾随闭包

什么是尾随闭包: 如果函数的最后一个参数是闭包,函数参数可以提前结束,最后一个参数直接使用{}包装闭包的代码

//原本:
 loadData(completion: { (result) -> () in
        
        
    })
 
 //尾随闭包
 loadData { (result) in
        
        print(result)
 }

1.6懒加载

懒加载通常写法:

 lazy var label: DemoLabel =  DemoLabel()

懒加载完整写法;

 lazy var label = { () -> DemoLabel in
    
    let l = DemoLabel()
    
    //设置label的属性
    
    return l
    
}()  
写代码的时候,使用通常写法,不使用完整写法,因为闭包里面容易出现循环引用的问题

懒加载在Swift和OC中的差别:

OC中,如果懒加载的属性被赋值为nil,再次调用的时候还是会进行懒加载。而Swift中则不行,设置为nil后,懒加载也不会被再次执行。懒加载的代码只会在第一次调用的时候,执行闭包,然后将闭包的结果保存在label的属性中。

2.构造函数

命名空间: 在Swift中,默认同一个项目中(同一个命名空间下),所有类都是共享的,可以直接访问,不需要 import,所有对象的属性 var,也可以直接访问到。使用cocapods 可以保证类,方法在不同的命名空间下。

构造函数的目的:
1.构造函数目的:给自己的属性分配空间并且设置初始值
2.如果重载了构造函数,并且没有实现父类init方法,系统不再提供init()构造函数(默认是会有的)
因为默认的构造函数,不能给本类的属性分配空间

Swift构造函数的初始化过程:
1.给自己的属性分配空间并且设置初始值
2.调用父类的“构造函数”,给父类的属性分配空间初始值,NSObject 没有属性,只有一个成员变量‘isa’

class Person: NSObject {

var name: String

    override init() {
        
        name = "Hayer"
        super.init()
    }
}

() -> alloc/init 
作用:给成员变量分配空间,初始化成员变量

let p = Person()
       
print(p)
    
print("\(p.name)")

Swift的初始化过程与OC相反

Swift先初始化话自己,再初始化父类。OC先初始化父类,在初始化自己

2.1 重载构造函数

重载与重写: override 与 overload

override: 覆盖原有的方法

 override init() {
    
    name = "Hayer"
    super.init()
}
    
overload: 函数名相同,参数和个数不同,重载可以给自己的属性从外部设置初始值

init(name: String) {
    
    //使用参数的name 设置给属性
    self.name = name
    
    //调用父类的构造函数
    super.init()
}

2.2 构造函数KVC

Swift使用KVC的注意点:
1.定义模型属性的时候,如果是对象,通常都是可选的
2.如果是基本数据类型,不能设置成可选的,而且要设置初始值,否则KVC会崩溃
3.如果使用KVC 设置数值,属性不能是private
4.使用KVC方法之前,应该调用super.init 保证对象实例化完成

class Person: NSObject {

    var name: String?
    
    var age: Int = 0
    
    //- 如果是private 属性,使用KVC 设置值得时候,同样无法设置
    //- 如果设置成private 属性/方法,禁止外部访问
    private var title: String?
   
    //重载构造函数,使用字典为本类设置初始值
    init(dict: [String : Any])
    {
        //保证对象已经完全初始化完成
        super.init()

        //要求对象已经实例化完成
        setValuesForKeys(dict)
    }
    
    //重写父类的方法
    override func setValue(_ value: Any?, forUndefinedKey key: String) {
        
        //没有调用super,将父类的代码实现完全覆盖,不会崩溃
    }
}

使用:
let p = Person(dict: ["name":"Hayder", "age": 109, "title":"BOSS"])
    
 print("\(p.name) \(p.age)")

2.3 便利构造函数

便利构造函数的目的:
判断条件,只有满足条件,才实例化对象,可以防止造成不必要的内存开销
简化对象的创建
本身不负责属性的创建和初始化工作

class Person: NSObject {

var name: String?
var age: Int = 0

convenience init?(name: String, age: Int) {
    
    if age > 100 {
        
        return nil
    }

    self.init()

    self.name = name
}

// 没有func -> 不让调用
// 没有 () -> 不让重载,不许带参数
//在对象被销毁钱自动调用
//类似于OC的dealloc
    deinit {
        
        //1.跟踪对象的销毁
        //2.必须释放 
        /**
            - 通知,不释放不会崩溃,但是崩溃
            - KVO,不释放会崩溃
            - NSTimer/ CADisplayLink
         */
    }
}

注意点:
1.便利构造函数允许返回nil
  - 正常的构造函数一定会创建对象
  - 判断给定的参数是否符合条件,如果不符合条件,直接返回nil,不会创建对象,减少内存开销

2.遍历构造函数中使用“self.init()” 构造当前对象
  - 没有 convenience 关键字的构造函数是负责创建对象的,反之是用来检查条件的,本身不负责对象的创建

3.如果要在遍历构造函数中使用当前对象的属性,一定要在self.init之后

3.setter和getter

private var _name : String?

//Swift中不会重写 getter 和 settter  方法
var name: String? {
    
    get{
        
        //返回成员变量
        return _name
    }
    
    set{
        
        _name = newValue
    }
}

Swift中一般不会重写setter和getter,如果需要对传过来的对象进行赋值

 var person: Person?{
    
    // 就是替代OC中重写Setter方法
    //区别,再也不需要考虑 _成员变量 = 值!
    //OC中如果是copy属性,应该 _成员变量 = 值.copy
    didSet{
        
        //此时name属性已经有值,可以直接使用设置UI内容!
        text = person?.name
    }
}

3.1 计算型属性

readOnly属性
只读属性又称为:计算型属性。本身不保存内容,都是通过计算获得结果,类似于一个没有参数,只有固定返回值的函数

重写getter方法就是readOnly属性
var title: String {
    
    //只重写了getter方法,没有重写setter方法,就是readOnly属性
    get{
        
        return "Mr." + (name ?? "")
    }
}

也可以简写

var title: String{
    
     return "Mr." + (name ?? "")    
}

懒加载的title
  懒加载的title,本质是一个闭包
  懒加载会在第一次访问的时候执行,闭包执行结束后,会把结果保存在title3中
  后续调用,直接返回内容,不会执行
  懒加载的属性会分配空间存储值

 lazy var title3: String = {
    
   return "Mr." + (self.name ?? "")
}()

3.2 反射机制

反射机制概念:

1.对于任意一个类,都能够知道这个类的所有属性和方法
2.对于任意一个对象,都能够调用它的任意个方法和属性

比如:
~ 利用NSClassFromString 使用字符串获取类(重要)
~ 利用isMemberOfClass 是否是某一个类
~ 利用isKindOfClass 是否是某一个类的子类
~ 利用performSelector 或者 objc_msgSend 间接调用方法

3.3 利用反射机制设置根控制器

在didFinishLaunchingWithOptions方法中
1.设置window的属性,并设置窗口可见

self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.backgroundColor = UIColor.white
self.window?.makeKeyAndVisible()

2.设置 根控制器,需要添加命名空间 -> 默认是项目名称(最好不要有数字和特殊符号)

    //利用计算型属性-从阅读上,计算型属性更加直观
    let className = Bundle.main.nameSpace+"."+"ViewController"
    //AnyClass? -> 视图控制器的类型
    let cls = NSClassFromString(className) as? UIViewController.Type
    
    //使用类创建视图控制器
    let vc = cls?.init()
    
    self.window?.rootViewController = vc

利用计算型属性抽取命名空间:
创建一个:Bundle+Extension.swift 文件

extension Bundle{

    //计算型属性,和函数类似,没有参数,有返回值
    var nameSpace: String{
        
        return infoDictionary?["CFBundleName"] as? String ?? ""

    }

}

4.Swift应用

4.1 加法计算器

class ViewController: UIViewController {

var result: UILabel?
var numText1: UITextField?
var numText2: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()
    
    setUpUI()
}

/// 计算结果
func calc() {
    
    //将文本框内容转换为数值
    //Int? 如果文本框内容不是数字 Int之后是nil
    //先测试:let num1 = Int(numText1?.text ?? "")如果不是数字就会输出为空
    guard let num1 = Int(numText1?.text ?? ""),let num2 = Int(numText2?.text ?? "")
        else {
        
            print("必须输入数字才能进行计算")
            
            return
    }
    
    result?.text = "\(num1 + num2)"
}

func setUpUI() -> () {
    
    //1.两个 textField
    let tf1 = UITextField(frame:CGRect(x: 20, y: 20, width: 100, height: 30))
    tf1.borderStyle = .roundedRect
    tf1.text = "0"
    
    view.addSubview(tf1)
    
    let tf2 = UITextField(frame:CGRect(x: 140, y: 20, width: 100, height: 30))
    tf2.borderStyle = .roundedRect
    tf2.text = "0"
    
    view.addSubview(tf2)
    
    //记录属性
    numText1 = tf1
    numText2 = tf2
    
    //2. 3个 label
    let label1 = UILabel(frame: CGRect(x: 120, y: 20, width: 20, height: 30))
    label1.text = "+"
    label1.textAlignment = .center
    view.addSubview(label1)
    
    let label2 = UILabel(frame: CGRect(x: 240, y: 20, width: 20, height: 30))
    label2.text = "="
    label2.textAlignment = .center
    view.addSubview(label2)
    
    let label3 = UILabel(frame: CGRect(x: 260, y: 20, width: 80, height: 30))
    label3.textAlignment = .center
    label3.text = "0"
    view.addSubview(label3)
    
    self.result = label3
    
    //4.1个button
    let button = UIButton()
    
    button.setTitle("计算", for: UIControlState(rawValue : 0))
    button.setTitleColor(UIColor.black, for: .normal)
    
    button.sizeToFit()
    button.center = view.center
    
    button.addTarget(self, action: #selector(calc) , for: .touchUpInside)
    view.addSubview(button)
}

}

4.2 加法计算器(便利构造函数)

使用分类

extension UITextField{
    
    convenience init(frame: CGRect, placeholder: String, fontSize: CGFloat = 14)
        {
            //实例化对象
            self.init(frame: frame)
            
            //访问属性
            self.borderStyle = .roundedRect
            self.placeholder = placeholder
            self.font = UIFont.systemFont(ofSize: fontSize)
        }
}

extension UIButton{

    convenience init(title: String,target:Any?, action: Selector) {
        
        self.init()
        
        self.setTitle(title, for: UIControlState(rawValue : 0))
        self.setTitleColor(UIColor.black, for: .normal)
        self.sizeToFit()
        self.addTarget(target, action: action , for: .touchUpInside)
             
    }
}

class ViewController: UIViewController {

var result: UILabel?
var numText1: UITextField?
var numText2: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()
    
    setUpUI()
}

/// 计算结果
func calc() {
    
    guard let num1 = Int(numText1?.text ?? ""),let num2 = Int(numText2?.text ?? "")
        else {
        
            print("必须输入数字才能进行计算")
            
            return
    }
    
    result?.text = "\(num1 + num2)"
}

func setUpUI() -> () {
    
    //1.两个 textField
    let tf1 = UITextField(frame: CGRect(x: 20, y: 20, width: 100, height: 30), placeholder: "0", fontSize: 14)
    
    view.addSubview(tf1)
    
    let tf2 = UITextField(frame:CGRect(x: 140, y: 20, width: 100, height: 30), placeholder: "0", fontSize: 14)
    view.addSubview(tf2)
    
    //记录属性
    numText1 = tf1
    numText2 = tf2
    
    //2. 3个 label
    let label1 = UILabel(frame: CGRect(x: 120, y: 20, width: 20, height: 30))
    label1.text = "+"
    label1.textAlignment = .center
    view.addSubview(label1)
    
    let label2 = UILabel(frame: CGRect(x: 240, y: 20, width: 20, height: 30))
    label2.text = "="
    label2.textAlignment = .center
    view.addSubview(label2)
    
    let label3 = UILabel(frame: CGRect(x: 260, y: 20, width: 80, height: 30))
    label3.textAlignment = .center
    label3.text = "0"
    view.addSubview(label3)
    
    self.result = label3
    
    //4.1个button
    let button = UIButton(title: "计算",target: self,action: #selector(calc))
    button.center = view.center
    
    view.addSubview(button)
   
}

}

4.3 runtime加载属性列表

class Person: NSObject {

var name: String?
var age: Int = 0

class func propertylist() -> ([String]){
    
    var count: UInt32 = 0
    //1.获取“类”的属性列表
    //outCount: UnsafeMutablePointer! 可变的
    let list = class_copyPropertyList(self, &count)
    
    print("属性的数量 \(count)")
    
    var result: [String] = []

    //遍历数组
    for i in 0..

注意点:
1.基本数据类型,在OC中没有可选,如果定义成可选,运行时同样获取不到,使用KVC就会崩溃
2.private 的属性,使用运行时,同样获取不到属性(可以获取到ivar),同样会使KVC崩溃

4.4 session使用

 if let url = URL(string: "http://www.baidu.com")
   {
    //发起网络请求
    //和OC 的区别,闭包的所有参数需要自己写,OC是直接写入
    // - 如果不关心,可以直接‘_’忽略
    URLSession.shared.dataTask(with: url) { (data, _, _) in
        
        guard let data = data else{
            
            print("网络请求失败")
            return
        }
        
        let html = String(data: data, encoding: .utf8)
        
        print(html)
        
        }.resume()

    }else
   {
        print("url 为nil")
    }

}

4.5项目零散总结

在项目中做类型转换一般使用: as

1.Swift中String之外,绝大多数使用 as 需要? /!
2.as? / as! 直接根据前面的返回值来决定
3.注意 if let 、guard let 判空语句,一律使用as?

destination类型: var destination: UIViewController { get }
let vc = segue.destination as! DetailController

sender类型:let sender: Any?
if let indexpath = sender as? IndexPath

执行闭包回调

1.!强行解包 -> 闭包一定不要用!
2.?可选解包 ->如果闭包为nil,就什么也不做
completionCallBack?()

你可能感兴趣的:(Swift3.0 基础语法2)