Funky、

Insist, Hard, Success

Welcome to Funky‘s zone. About me,so simple,Keep on coding,Keep on dancing. If you like something,stick to do!


Swift-基础语法&面向对象


Swift基础语法

一、特色:

  • 取消了预编译指令包括宏
  • 取消了OC的指针及其他不安全的访问的使用
  • 全面改为句点表示法
  • Swift3.0 去除了NS前缀
  • 将绝大部分Class转化为struct
  • @UIApplication 程序的入口
  • 只有.swift 没有 .h/.m 在swift中默认全局共享
  • 所有的代码都包装在{ }中,默认方法都有缩进
  • 访问当前对象的属性,可以不用 ‘self’
  • 没有 分号‘ ;’
  • // MARK: - 作标记
  • // TODO: 作标记
  • // FIXME: 作临时标记

二、基础语法:

1. 变量和常量

  • 定义变量 var 定义之后可修改,定义常量 let 定义之后不可修改
  • 自动推导,变量或常量的类型会根据右侧的代码执行结果,推导对应的类型(option + click 查看类型)
  • 在Swift中对类型要求异常严格,任何不同类型的数据之间,不允许直接运算,不会做默认的隐式转换,所有的类型确定,都要由程序猿负责
  • Swift中,不存在基本数据类型,都是结构体

    func test() {
    
        let x = 10
        let y = 10.5
        //print(x + y)  这种写法是错误的
    
        print(x + Int(y)) //将y转换成整数
        print( Double(x) + y) //将x装换成 Double
    
        //整数:Int(OC 中 NSInteger 类似)
        //小数:Double 精度高(OC中的 CGFloat)
    }
    func test1() {
    
        //如果需要,指定类型 (实际开发中 极少指定类型)
        let x:Double = 10
        let y = 10.5
        print(x + y)
    }
    

2. 简单的分支和三目的写法

func test() {

    // 1. 条件不需要 ()
    // 2. 语句必须有 {}
    let x = 10

    if x > 5 {
        print("大于5")
    } else {
        //永远不会执行 ,编译器编译的时候会做语法检查
        print("小于5")
    }

    //三目运算
    x > 10 ? print("大于10") : print("等于于10")
    x > 10 ? print("大于10") : () //相当于没有else
}

3. Optional 可选项

  • 强行解包与可选值

    func test() {
    
        // Optional 是一个枚举 none:没有值 some:某一类值
        let x : Optional = 10
    
        // '?' 用来定义 y 是一个可选的Int类型 可能没有值,也可能有一个整数
        let y: Int? = 20
    
        // 不同类型之间的值不能直接运算,如果没有值 为nil,不是任何数据类型,不能参与计算
        // '!' 强行解包 -> 从可选值中强行获取对应的非空值,如果是 nil,就会崩溃
        print(x! + y!)
    
    }
    
  • 变量和常量可选值的默认值

    • var 的可选值默认为 nil
    • let 的可选值没有默认值
  • 对可选值的安全处理

    test(x: 10, y: nil) //调用下面的方法
    
    func test(x: Int?, y: Int?) {
    
        // 1. 强行解包有风险
        // print(x! + y!)
    
        // 2. 使用if判断
        if x != nil && y != nil {
            print(x! + y!)
        }else{
            print("x 或 y 为nil")
        }
    
        // 3. if let 连用,判断值是否为nil ,进入分支后,a,b一定有值,不需要解包
        // if var 连用 {}内可以对值进行修改
        if let a = x, let b = y {
            print(a + b)  // a,b作用域只在 {}中
        }else{
            print("x 或 y 为nil")
        }
    
        // 4. guard let 和 if let刚好相反
        guard let m = x,let n = y else {
            print("x 或 y 为nil")
            return
        }
        print(m + n)
    
        // 5. ?? 是一个简单的三目 (如果前面有值,使用值,如果没有值,使用??后面的值)
        print((x ?? 0) + (y ?? 0))
    }
    

4. switch的语法

  • switch 可以针对任意类型的值进行分支,不在局限在整数
  • switch 一般不需要 break
  • switch 如果要多值,使用 ,
  • 所有的分支至少需要一条指令,如果什么都没有,才使用break

    func test(num : String) {
        switch num {
            case "10", "9":
            print("优")
    
            case "8": break
    
            default:
            print("other")
        }
    }
    

5. For循环

  • 以前的写法(在swift 3.0 被取消)

    // i++ / ++i 在swift 3.0 被取消 改为 i += 1
    for var i = 0; i < 10; i++ {
    }
    
  • swift3.0写法

    //0..<5 => [0,5)   0...5 => [0,5]
    for i in 0..<5 {
        print(i)
    }
    
  • 反序遍历

    for i in (0..<5).reversed() {
        print(i) //输出 4 3 2 1 0
    }
    

6. 字符串

  • 字符串的遍历

    let str = "Swift"
    for c in str.characters {
        print(c)
    }
    
  • 字符串的长度

    let str = "Swift基础"
    
    //2.1 返回指定编码的字节数量 UTF8的编码(0-4个),每个汉字3个字节
    print(str.lengthOfBytes(using: .utf8))
    
    //2.2 字符串长度
    print(str.characters.count)
    
    //2.3 使用 NSString 中转
    //as 类型转换
    let ocStr = str as NSString
    print(ocStr.length)
    
  • 字符串的拼接

    let name = "Funky"
    let age = 18
    let title: String? = "Swift基础" //注意 可选项Optional
    
    let str = "\(name) \(age) \(title ?? "")"
    print(str) //=> Funky 18 Swift基础
    
  • 字符串的格式化

    let h = 8
    let m = 9
    let s = 6
    
    let date = "\(h):\(m):\(s)"
    print(date) //=> 8:9:6
    
    let date1 = String(format: "%02d:%02d:%02d", h,m,s)
    print(date1) //=> 08:09:06
    
  • 字符串的子串

    //1. Swift取子串的方法一直在优化,一般使用 NSString 作为中转
    let str = "Swift基础"
    let ocStr = str as NSString
    let s1 = ocStr.substring(with: NSMakeRange(2, 3))
    print(s1) //=> ift
    
    //2.String 的 3.0 方法
    print(str.startIndex) //=> _position: 0
    print(str.endIndex) //=> _position: 7
    
    let s2 = str.substring(from: "ab".endIndex)
    print(s2) //=> ift基础
    
    // 取子字符串的范围
    guard let range = str.range(of: "ift") else {
    print("不包含此字符串")
        return
    }
    print(range)
    print(str.substring(with: range)) //=>ift
    

7. 数组

  • 数组的定义

    //可变数组 (var),不可变数组 (let)
    // 自动推导的结果 【String】 -> 表示数组中存放的都是String
    let array = ["1","2","3"]
    print(array)
    
    //[Int] -> 表示存放的都是 Int,在Swift中基本数据类型不需要包装
    let array1 = [1,2,3]
    print(array1)
    
    //[CGPoint] -> 存放的是CG结构体
    let p = CGPoint(x:10, y:300)
    let array2 = [p]
    print(array2)
    
    //混合数组:[Any] ,开发中几乎不用,CG结构体需要包装
    let array3 = ["Funky",18,NSValue(cgPoint:p)] as [Any]
    print(array3)
    
  • 数组的遍历

    //2.1 按下标遍历
    let array = ["张三","李四","王五"]
    for i in 0..<array.count {
        print(i)
    }
    
    //2.2 for in遍历元素
    for s in array {
        print(s)
    }
    
    //2.3 enum block遍历
    //(offset: Int, element: String) 元组
    for e in array.enumerated() {
        print(e)
        print("\(e.offset) \(e.element)")
    }
    
    //2.4 n 是索引下标; s 是下标对应的元素 (n/s 可以随便写)
    for (n, s) in array.enumerated() {
    
        print("\(n) \(s)")
    }
    
    //2.5 反序遍历 (先枚举在反序)
    for (n, s) in array.enumerated().reversed() {
    
        print("\(n) \(s)")
    }
    
  • 数组的增、删、改

    var array = ["张三","李四","王五"]
    //追加
    array.append("Funky")
    //修改
    array[0] = "老李"
    //删除
    array.remove(at: 3)
    //删除所有,并且保留空间
    array.removeAll(keepingCapacity: true)
    
  • 数组的合并

    var array = ["张三","李四","王五"]
    
    let array1 = ["Funky"]
    //将array1合并到array中(数组类型必须一致)
    array += array1
    print(array)
    

8.字典

  • 定义

    //[String : Any]
    let dict = ["name":"Funky","age":18] as [String : Any]
    print(dict)
    
    //定义一个字典的数组
    let array:[[String: Any]] = [
        ["name":"Funky","age":18],
        ["name":"Funky","age":18]
    ]
    print(array)
    
  • 增删改

    var dict = ["name":"Funky","age":18] as [String : Any]
    
    //新增
    dict["height"] = 180
    print(dict)
    
    //修改
    dict["age"] = 19
    print(dict)
    
    //删除
    dict.removeValue(forKey: "age")
    print(dict)
    
  • 遍历

    let dict1 = ["name":"Funky","age":18,"height":180] as [String : Any]
    
    for e in dict1 {
        print(e)
        print("\(e.key) \(e.value)")
    }
    
    for (key,value) in dict1 {
        print("\(key) \(value)")
    }
    
  • 合并

    var dict2 = ["name":"Funky","age":18,] as [String : Any]
    
    let dict3 = ["height":180]
    
    for e in dict3 {
        dict2[e.key] = dict3[e.key]
    }
    
    print(dict2)
    

三、Swift函数:

1. 函数

  • 函数定义

    print(sum(x: 10, y: 50))
    
    //格式: 函数名(形参列表)-> 返回值类型
    func sum(x:Int,y:Int) -> Int {
        return x + y
    }
    
  • 外部参数 (在形参前加一个名字,不影响函数内部的细节)

    print(sum1(num1: 10, num2: 10))
    print(sum2(10, 20))
    
    func sum1(num1 x:Int,num2 y:Int) -> Int {
        return x + y
    }
    
    //外部调用函数时,会忽略形参的名字 (Swift中 `_` 可以忽略任意不感兴趣的内容)
    func sum2(_ x:Int,_ y:Int) -> Int {
        return x + y
    }
    
  • 默认值

    print(sum3())
    print(sum3(x: 10, y: 50))
    print(sum3(x: 10))
    print(sum3(y: 50))
    
    //通过给参数设置默认值,在调用的时候,可以任意组合参数,如果不指定的,就使用默认值
    func sum3(x:Int = 1,y:Int = 1) -> Int {
        return x + y
    }
    
  • 无返回值 (-> 表示前面执行 后面输出目标结果)

    func test1() {
        print("XXX")
    }
    func test2() -> () {
        print("XXX")
    }
    func test3() -> Void {
        print("XXX")
    }
    

2. 闭包

  • 最简单的闭包

    /*
        //闭包的基本格式(in 是用于区分形参返回值和执行代码)
        {
            (形参列表) -> (返回值)
            in
            需要执行的代码
        }
    */ 
    
    //bi: () -> () (没有参数,没有返回值)
    let b1 = {
        print("xxx")
    }
    b1() //执行闭包
    
  • 带参数的闭包

    //需要一个关键字 `in` 分隔定义和实现
    //闭包中,参数,返回值,实现代码都是写在 {} 中
    //b2: (Int) -> ()
    let b2 = { (x: Int) -> () in
        print(x)
    }
    b2(100)
    
  • 带参数带返回值的闭包

    //需要一个关键字 `in` 分隔定义和实现
    //闭包中,参数,返回值,实现代码都是写在 {} 中
    //b2: (Int) -> ()
    let b3 = { (x: Int) -> Int in
        return x + 250
    }
    print(b3(100))
    
  • 闭包的几种格式

    //1.将闭包通过实参传递给函数
    //2.如果闭包是函数的最后一个参数,那么闭包可以写在函数()的后面
    //3.如果函数只接受一个参数,并且这个参数是闭包,那么()可以省略
    
    loadData { () -> () in
        print("被回调了")
    }
    
    loadData (finished: { () -> () in
        print("被回调了")
    })
    
    loadData(){() -> () in
    
    }
    
    loadData2(num: 10, finished: {() -> () in
    
    })
    
    loadData2(num: 10){() -> () in
    
    }
    
    //如果闭包没有参数也没有返回值,那么in之前的都可以不写,包括in
    loadData {
    }
    
    //参数接受一个没有参数,没有返回值,名称是finished 一个闭包
    func loadData(finished:() -> ()) {
    
        print("耗时操作")
        finished()
    }
    
    func loadData2(num:Int,finished:() -> ()) {
    
        print("耗时操作")
        finished()
    }
    

3.循环引用

  • 造成循环引用

    //定义属性
    var completionCallBack: (() -> ())?
    
    //只是闭包对self 进行了copy,闭包执行完成后,会自动销毁,同时释放对self的引用
    loadData {
        print(self.view)
    }
    
    //循环引用:单方向引用是不会产生循环引用的
    //闭包对self 进行了copy,闭包执行完成后,会自动销毁,同时释放对self的引用
    //同时需要 self 对闭包引用,就会造成循环引用
    
    func loadData(completed:@escaping ()->()) -> (){
    
        //使用属性记录闭包 -> self 对闭包引用了
        completionCallBack = completed
        completed()
    }
    
    /*
        deinit 相当于OC的 dealloc ,对象被释放前自动调用   
        - 没有 func 不允许直接调用;
        - 没有( ) 不允许带参数,不允许被重载/重写
        - 使用场景 :跟踪对象被销毁,NSTimer/CADisplayLink,通知,KVO...
    */
    deinit {
        print("释放")
    }
    
  • 解决循环引用

    //方法一:OC的方式
    //weak 只能修饰 var,不能修饰let
    //weak 可能会在运行时被修改 -> 指向的对象一旦被释放,会被自动设置为nil
    weak var weakSelf = self
    loadData {            
        // ? 可选解包 - 如果 self 已经被释放,不会向对象发送 getter 的消息,更加安全
        // ! 强行解包 - 如果 self 已经被释放,强行解包会导致崩溃
        print(weakSelf?.view) //weakSelf?.view 只是单纯的发送消息,没有计算
    }
    
    //方法二:Swift的方式
    //[weak self] 表示 {}中的所有 self 都是循环引用,需要注意解包
    loadData { [weak self] in
        print(self?.view)
    }
    
    //方法二:Swift的另外一种方式
    //[unowned self] 表示闭包中的所有 self 都是assign弱引用的,不会强引用,但是如果对象释放,指针地址不会改变
    //如果对象被释放,继续调用,就会出现野指针的问题
    loadData { [unowned self] in
        print(self.view)
    }
    

—————————————-

Swift面向对象

一、构造函数

1. 构造函数基础

构造函数是一种特殊的函数

  • 主要用来在创建对象时初始化对象
  • 为对象的成员变量设置初始值

在OC中的构造函数是 initWithxxx,在 Swift 中由于支持函数重载,所有的构造函数都是init

  • 重载 overload :(函数名相同,参数类型和个数不同)
  • 重写 overrride : (父类存在相同的方法,子类重新编写父类方法的实现)

构造函数的作用

  • 分配空间 alloc
  • 设置初始值 init

2. 必选属性的构造过程

//错误1:Class 'Person' has no initializers
//Person 类没有初始化器,构造函数可以有多个,默认的是 init
class Person: NSObject {
    var name:String
}


class Person: NSObject {
    var name:String
    //错误2:Overriding declaration requires an 'override' keyword
    //重写(父类有这个方法,子类重新实现),需要 override 关键字
    init() {
    }
}


class Person: NSObject {
    var name:String
    //错误3:Property 'self.name' not initialized at implicitly generated super.init call
    //implicitly(隐式生成的 super.init()) 调用父类的构造函数之前,属性 self.name 没有被初始化
    override init() {

    }
}


class Person: NSObject {
    var name:String

    override init() {

        //错误4:Property 'self.name' not initialized at super.init call
        //在调用父类 super.init() 的时候,没有给 self.name 初始化(分配空间,设置初始值)
        super.init()
    }
}


/*
    必选属性的构造:
    1.给自己的属性分配空间并且设置初始值
    2.调用父类的构造函数,是给父类的属性分配空间设置初始值
    NSObject 没有属性,只有一个成员变量 `isa`(记录是哪个类)
*/
class Person: NSObject {

    var name:String

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

3. 重写和重载

class Person: NSObject {

    var name:String

    //1.重写 : 父类有这个方法
    override init() {

        // name 确实有初始值,但所有的初始值都是 funky -> 引入重载设置初始值
        name = "funky"
        super.init()
    }

    //2.重载 : 函数名相同,但是参数和个数不同
    // 重载可以给自己的属性从外部设置初始值
    // OC 没有重载
    //注意 : 如果重载了构造函数,并且没有实现父类 init 方法,系统不再提供 init() 构造函数!
    init(name: String) {
        self.name = name
        super.init()
    }
}

4. KVC 函数调用

  • 定义模型属性的时候,如果是对象,通常都是可选的

    • 在需要的时候创建
    • 避免写构造函数,可以简化代码
  • 如果是基本数据类型,不能设置成可选的,而且要设置初始值,否则 KVC 会崩溃

    • 原因: Int是一个基本数据类型的结构体,OC中没有,OC 中只有基本数据类型
  • 如果需要使用 KVC 设置数值,属性不能是 private 的

  • 使用KVC 方法之前,应该调用 super.init() 保证对象已经完全初始化完成

class Person: NSObject {

    var name:String?

    //var age:Int? 不能这样写
    var age:Int = 0

    //如果是 private(私有的) 属性,使用 KVC 设置值的时候,同样无法设置!
    //如果设置成 private 属性/方法,禁止外部访问的,使用运行时也获取不到属性(可以获取到ivar),同样会让KVC崩溃
    //private var title: String?
    var title: String?

    init(dict:[String:AnyObject]) {

        //保证对象已经完全初始化完成
        super.init()

        //Use of 'self' in method call 'setValuesForKeys' before super.init initializes self
        //使用self的方法 setValuesForKeys 之前,应该调用 super.init
        //KVC 是OC的方法(在运行时给对象发送消息),要求对象已经实例化完成
        setValuesForKeys(dict)
    }

    //重写父类的方法
    override func setValue(_ value: Any?, forUndefinedKey key: String) {
        //没有调用 super,将父类的代码实现完全覆盖!不会崩溃
    }
}

5. 运行时获取对象属性列表

class Person: NSObject {

    var name:String?
    var age:Int = 0
    var title: String?

    //使用运行时,获取当前类所有属性的数组
    // class 相当于 OC 的加号方法
    class func propertyList() -> [String] {


        var count: UInt32 = 0

        //1. 获取 `类` 的属性列表
        let list = class_copyPropertyList(self, &count)

        //2. 遍历数组
        for i in 0..<Int(count) {

            /*  

                //3. 根据下标获取属性
                //objc_property_t?
                let pty = list?[i]

                //4. 获取属性的名称
                //UnsafePointer<Int8>? Int8 C语言的字符串
                let cName = property_getName(pty!)

                //5. 转换成 String 的字符串
                let name = String(utf8String: cName!)

            */

            //安全守护
            guard let pty = list?[i],
                let cName = property_getName(pty),
                let name = String(utf8String: cName)

            else {
                    // 继续便利下一个
                    continue
                }
            print(name) //name是一定有值的
        }

        free(list)
        return []
    }
}

6. 便利构造函数

let button = UIButton(title: <#T##String#>, color: <#T##UIColor#>)


//extension 类似于 OC 中的 category
extension UIButton {

    convenience init(title: String, color:UIColor = UIColor.darkGray) {
        //便利构造函数 与 OC中的抽取分类类似,
        //注意:便利构造函数 本身不负责对象的创建,使用self.init() 初始化对象
        self.init()
        self.setTitle(title, for:.normal)
        self.setTitleColor(color, for: .normal)
        self.sizeToFit()
    }
}

7. 懒加载

//懒加载 - lazy
// 1. 能够延迟创建
// 2. 解除解包的烦恼
// 注意:懒加载的代码只会在第一次调用的时候,执行闭包,然后将闭包的结果保存在 label 的属性中 
// Swift中一定注意不要主动清理视图或者控件,因为懒加载不会再次创建

lazy var label:UILabel? = UILabel()

override func viewDidLoad() {
    super.viewDidLoad()

    print(label)

    label?.text = "hello"
    label?.sizeToFit()    
    print(label)

    label = nil //释放label
    print(label)
}

8. getter&setter方法

class Person: NSObject {

    private var _name: String?
    //Swift 中一般不会重写 getter/setter方法
    var name:String? {
        get {
            return _name
        }
        set {
            _name = newValue
        }
    }
}

9. 只读属性 readonly

//readonly -> 重写 getter 方法
var title: String{

    //只重写 getter 方法,就是只读属性
    get {
        return "Mr" + (name ?? "")
    }
}

//只读属性的简写(直接 return)
//又称为 计算型属性(本身不保存内容,都是通过计算获得结果)
var title2: String {
    return "Mr XXX" + (name ?? "")
}

10. 利用模型的didSet 设置 UI 界面

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

11. 可选项

最近的文章

OC-触摸事件&响应者链条

iOS中的事件 在用户使用app过程中,会产生各种各样的事件,iOS中的事件可以分为3大类型:触摸事件,加速计事件,远程控制事件 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。我们称之为响应者对象 UIApplication、UIView …

于  knowledge 继续阅读
更早的文章

OC-Block的使用

Block的基础用法1 block作用保存一段代码块 2 block声明// 返回值(^block变量名)(参数) void(^block)() // block快捷方式 inline 返回值类型(^变量名)(参数类型) = ^(参数) { &lt;#statements#&gt; }; …

于  knowledge 继续阅读
comments powered by Disqus