• Swift开发语法


    Swift开发入门


    简介

    • Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序
    • 2014 年,在 Apple WWDC 发布

    历史

    • 2010 年 7 月,苹果开发者工具部门总监 Chris Lattner(克里斯·拉特纳) 开始着手 Swift 编程语言的设计工作
    • 用一年时间,完成基本架构
    • Swift 大约历经 4 年的开发期,2014 年 6 月发表

      克里斯·拉特纳
      )

    特色

    • 苹果宣称 Swift 的特点是:快速、现代、安全、互动,而且明显优于 Objective-C 语言
    • 可以使用现有的 CocoaCocoa Touch 框架
    • Swift 取消了 Objective C 的指针及其他不安全访问的使用
    • 舍弃 Objective C 早期应用 Smalltalk 的语法,全面改为句点表示法
    • 提供了类似 Java 的名字空间(namespace)、泛型(generic)、运算对象重载(operator overloading)
    • Swift 被简单的形容为 “没有 C 的 Objective-C”(Objective-C without the C)

    现状

    • 2015 年 2 月,苹果同时推出 Xcode 6.2 Beta 5 和 6.3 Beta,在完善 Swift 1.1的同时,推出了 Swift 1.2 测试版
    • 2015 年 6 月,苹果在 WWDC 发布了 Swift 2.0 测试版,并且宣称在年底开源
    • 从发布至今,苹果的每一个举措都彰显其大力推广 Swift 的决心
    • Swift 当前正式版本:1.2,测试版本是 2.0
    • 目前有些公司的新项目已经直接采用 Swift 开发
    • 目前很多公司已经在做 Swift 的人才储备
    • 应聘时,会 Swift 开发 无疑会增加自身筹码
    • 到 2015 年底,iOS 9.0 正式发布的同时,Swift 势必大行其道!

    资源网站

    • 1.开发入门
      • 简单体验
    var i = 10
    print(i)
    i = 15
    print(i)
    
    let j = 20
    // 常量一经定义不能自改数值
    //        j = 25
    print(j)
    • 创建对象
    • 从OC转换为Swift的规律: alloc initXXX --> (xxxx:)
      OC: [[UIView alloc] init] -- [[UIView alloc] initWithFrame:]
      Swift: UIView() -- UIView(frame: )
    • 分号
      • 在OC中每一条语句后面必须跟上一个;, Swift中如果一行只有一条语句, 那么;可以省略
    • 调用方法
      OC: [UIColor redColor];
      Swift: UIColor.redColor()
    • 枚举

      OC: UIButtonTypeContactAdd
      Swift: UIButtonType.ContactAdd
    • 示例

      // 1.创建UIView
      let customView = UIView(frame: CGRect(x: 0, y: 0,  100, height: 100))
      customView.backgroundColor = UIColor.redColor()
      // 2.创建一个按钮
      let btn = UIButton(type: .ContactAdd)
      btn.center = CGPoint(x: 50, y: 50)
      // 3.将按钮添加到UIView上
      customView.addSubview(btn)

    • 2.常量和变量
    // 定义变量
    var i = 10
    print(i)
    i = 15
    print(i)
    
    let j = 20
    // 常量一经定义不能自改数值
    //        j = 25
    print(j)
    • 小结

      • var 定义变量,设置之后可以修改
      • let 定义常量,设置之后不可以修改
      • 语句末尾不用使用 ;
      • 在 Swift 中使用 print() 替代 OC 中的 NSLog
      • print 的性能更好
    • 定义 OC 对象

    // 实例化视图
    let v = UIView(frame: CGRectMake(0, 0, 100, 100))
    // 设置背景颜色
    v.backgroundColor = UIColor.redColor()
    
    // 添加到根视图
    view.addSubview(v)
    • 小结
      • Swift 中要实例化一个对象可以使用 类名() 的格式,与 OC 中的 alloc/init 等价
      • OC 中的 initWithXXXSwift 中通常可以使用 类名(XXX: ) 找到对应的函数
      • OC 中的 [UIColor redColor] 类方法,在 Swift 中通常可以使用 类名.XXX 找到对应的函数
      • 使用 let 修饰 v 并且赋值,表示 该常量的内存地址不允许修改,但是可以修改其内部的属性
      • 当前对象的属性,不需要使用 self.

    常量&变量的使用原则:尽量先用 let,只有需要变的时候,再用 var,能够更加安全

    • 变量类型
    let x = 10
    let y = 10.5
    let z: Double = 20
    
    print(Double(x) + y) // 20.5
    print(x + Int(y)) // 20
    print(y + z) // 30.5
    • 小结
      • 初次接触 Swift 中会因为简单的 var let 误以为 Swift 中的类型非常松散
      • 其实所有变量的准确类型都是在赋值的同时自动推导的
      • Swift 是对类型要求非常严格的一门语言,一个值永远不会被自动转换成其他类型
      • 如果要转换,必须显示转换,Swift 中
        • 小数默认是 Double 类型
        • 整数默认是 Int 类型
      • 如果要显式的指定变量的类型,可以在定义是使用 var 变量名: 类型 = 值
        • 变量: var
        • 常量: let
        • 格式: 修饰符 变量/常量名称: 数据类型 = 值
    • 数据类型
      • 只要将OC中的数据类型第一个字母转换为大写, 就是Swift中的数据类型
    • 注意点:
      • 在Swift开发中, 一般情况下先用let, 只要需要修改数据时才用var, 使用let的好处, 可以避免数据被修改, 可以保证数据安全性
    var number: Int = 30
    number = 50
    
    let number2: Int = 88
    //number2 = 55
    • 类型推导:
      • Swift中如果在定义变量/常量时进行初始化, 那么数据类型可以不用写,系统会自动根据右边的复制推导出变量/常量的类型
      • Swift开发中能不写数据类型就不写数据类型, 尽量使用编译器的自动推导
      • 只有当我们需要明确的指定数据的长度, 获取需要先定义再初始化时才明确的指定数据类型
      • 使用自动类型推导好处: 大大降低代码中的冗余代码
    let number3 = 10.10
    
    var number4: Int
    number4 = 99
    • 类型转换:
      • OC中有显示转换和隐式转换 double value = 10.1 + 9
      • Swift中只有显示转换没有隐式转换, 也就是说只有相同类型的数据才能进行赋值和计算
    // 只有相同数据类型才能赋值
    let number5: Int = Int(55.5) // 50
    
    // 只有相同数据类型才能进行运算
    let number6 = 10
    let number7 = 88.8
    let sum = Double(number6) + number7 // 98.8
    
    // CGFloat --> double
    
    let size = CGSize( 10, height: 10)
    let number8 = 10.1
    let sum2 = size.width + CGFloat(number8) // 20.1

    • 3.元祖
    let number1 = 10
    let number2 = 10.1
    • 元祖

      • 复合数据类型
      • 只要将多个相同或者不同的数据用()括起来就是元祖
      • 优点: 在以前没有元祖之前C和OC语言是通过传入指针或者返回结构体的方式来返回多个值的,而有了元祖之后就可以实现让一个函数返回多个值
    let number3: (Int, Double, Int, Double) = (10, 10.1, 9, 44.40) //(.0 10, .1 10.1, .2 9, .3 44.4)
    number3.0 // 10
    number3.1 // 10.1
    number3.2 // 9
    number3.3 // 44.40
    
    // 给元祖的元素起名称
    let person = (name: "gcy", age: 30, score: 100.0) // (.0 "gcy", .1 30, .2 100)
    person.name // "gcy"
    person.age // 30
    person.score // 100
    
    
    // 提取元祖的数据
    let (name, age, score) = ("gcy", 30, 100.0)
    name
    age
    score

    • 4.分支
    var i = 10
    
    if i > 0 {
        print("OK")
    }
    • 小结

      • Swift 中没有 C 语言中的非零即真概念
      • 在逻辑判断时必须显示地指明具体的判断条件
      • if 语句条件的 () 可以省略
      • 但是 {} 不能省略
    • if

      • 大部分用于和OC中一致
      • Swif中条件语句可以不用写()
      • OC中如果if后面只有一条语句, 那么{}可以省略, 但是Swift不行
      • OC中条件语句可以是任何数值, OC中非0即真, YES/NO
      • Swift中条件语句的取值必须是Bool类型,
      • 也就是说Swift中提供了真正的Bool类型, true/false
    let number = 10
    //if number = 10 // Swift有效的避免了这种问题
    if number == 10
    {
        print(number)
    }
    
    let age = 16
    if age >= 18
    {
        print("开网卡")
    }else
    {
        print("回家找妈妈")
    }
    • 三目运算符
    var a = 10
    var b = 50
    
    var result = a > b ? a : b
    print(result) // 50
    • 小结
      • Swift 中的 三目 运算保持了和 OC 一致的风格
      • 大部分用法和OC一样
      • 条件表达式只能是Bool值
    print(age >= 18 ? "开网卡" : "回家找妈妈")
    • switch
      • 大部分用法和OC一样
        Swift中条件语句可以不用写()
      • OC中default可以省略, 而Swift中大部分情况不能省略
      • OC中default的位置可以随便写, 而Swift不可以
      • OC中每个case后面必须加上break, 否则会出现穿透, 而Swift不会穿透, 也就是说不用写break
      • OC中要在case中间定义变量必须加上{}, 否则作用域混乱, 而Swift不用
      • 可以判断区间和元祖
    let score = 100
    switch score
    {
    case 59:
        print("不及格")
        var num = 100
    case 100:
        print("满分")
    default:
        print("Other")
    }
    • 区间
      • 闭区间: 0...10 , 取值范围0~10, 包含头包含尾
      • 半闭区间: 0..<10 取值范围0~9, 包含头不包含尾
    // 判断区间
    switch score
    {
    case 0..<60: // 0~59
        print("不及格")
    case 60..<80: // 60~79
        print("良好")
    case 80..<100: // 80~99
        print("优秀")
    default:
        print("满分")
    }
    
    let point = (100, 50)
    // 判断元祖
    switch point
    {
    case (0, 0):
        print("原点")
    case (50, 50):
        print("中点")
    case (100, 100):
        print("右下角")
    default:
        print("Other")
    }
    
    // 取出元祖中的值
    switch point
    {
    case (var x, var y) where x > y:
        print(x)
        print(y)
    default:
        print("Other")
    }

    • 5.可选类型

    • 什么是可选类型: 一个变量可以有值也可以没有值, 我们就称之为可选类型

      • 在Swift中如果使用一个可选类型的变量/常量, 必须解包操作
        • 解包: 只需要在变量/常量后面加上 !
        • !含义: 代表告诉系统该变量/常量中一定有值, 如果强制解包一个没有值的常量/变量,那么会报错
      • 优点: 提高代码阅读性
      • 格式: 修饰符 变量名称:Optional<数据类型> 修饰符 变量名称: 数据类型?
      • 语法糖: 因为在Swift中可选类型用得非常非常多, 所以为了简化代码, Swift提供了一个语法糖, 可以用? 代替 Optional<数据类型>
    • 提示: 对于初学者来说 ? ! 是最为头疼的语法, 刚开始的时候建议利用Xocde的语法提示来解决? !的问题
    //var number: Optional<Int> = nil
    //var number2: Int = nil
    
    let number: Optional<Int> = 10
    print(number!) // 10
    let number2 = 10
    let sum = number! + number2 // 20
    
    
    let number3: Int? = 55
    print(number3) // "Optional(55)
    "
    
    /*
    可选类型注意点:
    * 在开发中一般情况下尽量不要强制解包一个可选类型, 否则会引发错误
    */
    
    //let url = NSURL(string: "http://www.baidu.com")
    //print(url)
    //let request = NSURLRequest(URL: url!) // "Optional(http://www.baidu.com/)
    "
    
    let url = NSURL(string: "http://www.baidu.com/")
    print(url)
    //let request = NSURLRequest(URL: url!)
    if url != nil
    {
        let request = NSURLRequest(URL: url!)
    }
    
    // 可选绑定: 如果url不为nil, 系统内部就会自动将解包之后的值赋值给temp, 并且只有temp有值时才会执行{}中的代码
    // Swift开发中推荐这种写法
    if let temp = url
    {
        let request = NSURLRequest(URL: temp)
    }
    • 演练 1
    let url = NSURL(string: "http://www.baidu.com/")
    
    if url != nil {
        NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, _, _) -> Void in
            print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        }).resume()
    }
    • 小结

      • Swift 中,不是所有的对象实例化方法都会返回值,在实际开发中需要注意实例化函数的返回类型,例如:

        convenience init?(string URLString: String)
      • 如果有 ? 表示改方法有可能无法实例化到正确的对象

      • 这种函数返回的对象,被称为 可选项,即有可能有值,也有可能没有值
      • 实际开发时,需要针对这种对象加以判断,并且在分支内部使用 !,指明改对象确实是存在的
      • 相比在 OC 的开发,尤其在日常练习时,会给定一个能够运行的值,而在实际运行时,一旦条件不满足,会直接闪退,这样用户体验会非常不好

    Swift 的设计者考虑到因为对类型的强制要求,会让代码很难看,因此提供了一个变通的解决方案

    • 演练 2
    if let url = NSURL(string: "http://baidu.com") {
        NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, _) -> Void in
            print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        }).resume()
    }
    • 小结

      • 使用 if let 常量 = 可选构造函数 的方式能够确保分支内部常量一定是有值的
      • 并且在分支内部不再需要使用 !
      • 这是 Swift 代码中的一个非常重要的使用技巧
    • 提示

      • 尽管 Swift 提供了类型校验的手段,但是要写出 优雅 的 Swift 代码,还是需要多加练习的,否则一不小心就会出现分支嵌套层次很深的代码
      • 有关 ?! 的选择,可以借助 Xcode 的辅助工具,但是强烈建议每次遇到提示时,要多加思考,反复揣摩
    • 演练3

    var name: String? // nil
    print(name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) // "nil
    "
    
    //name = "gcy"
    print(name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) // "nil
    "
    
    
    print((name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) ?? 0)) //"0
    "
    • 小结
      • ?? 是一个非常有用的操作符,能够快速对 nil 进行判断
      • 如果对象是 nil,则使用 ?? 后面的值代替前面的 nil 值参与计算
      • 在使用 ?? 时,整个部分需要使用 () 包装
      • 这一技巧在 UITableView 的数据源方法中尤为重要

    • 6.循环
    • OC风格的 for
    // 传统写法
    for var i = 0; i < 10; i++ {
        print(i) // (10 times)
    }
    • Swift风格的 for
    // 遍历 0 ~ <10
    for i in 0..<10 {
        print(i) // (10 times)
    }
    
    print("---") // "---
    "
    
    // 遍历 0 ~ 10
    for i in 0...10 {
        print(i) // (11 times)
    }
    • 小结

      • Swift 中使用 in 关键字标示循环的范围
      • 0..<10 表示从0到9
      • 0...10 表示从0到10
      • 注意之间不能出现空格
    • 特殊写法

    for _ in 0...10 {
        print("hello") // (11 times)
    }
    • 小结

      • 如果不关心循环本身的索引,可以使用 _ 忽略
      • 传统for
        • 基本用法和OC一致
        • for后面的()可以省略
        • for后面的{}不可用省略
        • Swift开发中不建议使用传统for循环
    for var i = 0; i < 10; i++
    {
        print(i) // (10 times)
    }
    
    //死循环
    for ;;
    {
        print("---") // (18657 times)
    }
    // Swift开发中推荐的for循环格式
    for i in 0..<10
    {
        print(i) // (10 times)
    }
    • while
      • 基本用法和OC一致
    var number = 0
    while number < 10
    {
        print(number) // (10 times)
        number++
    }
    • do while
      • 基本用法和OC一致
      • Swift2.0开始dowhile循环中没有do, 只有repeat, 因为do被作用异常处理
    var index = 0
    repeat{
        print(index) // (10 times)
        index++
    }while index < 10

    • 7.数组
      • 数组
        • 格式 var arr: Array<Int> / var arr: [Int]
        • 可变和不可变 var/let
    • 简单体验
    let arr = ["zhangsan", "lisi"]
    print(arr) // "["zhangsan", "lisi"]
    "
    
    // 遍历每一个元素
    for a in arr {
        print(a) // (2 times)
    }
    
    // 像 OC 一样打印
    print(arr as NSArray) // "(
        zhangsan,
        lisi
    )
    "
    • 数组中保存的对象类型
    // 数组中保存的都是字符串
    let arr = ["zhangsan", "lisi"]
    
    // 数组中保存的是 NSObject
    let arr1 = ["zhangsan", 1]
    • 小结

      • 数组使用 [] 定义,这一点与 OC 相同
      • 如果初始化时,所有内容类型一致,则数组中保存的是该类型的内容
      • 如果初始化时,所有内容类型不一致,则数组中保存的是 NSObject
    • 常见数组操作

    // 定义只能保存字符串类型数组
    var array: [String]
    
    // 初始化数组
    array = ["zhangsan"]
    
    // 添加元素
    array.append("lisi")
    
    print(array) // "["zhangsan", "lisi"]
    "
    
    // 删除元素
    array.removeAtIndex(1) // "lisi"
    print(array) // "["zhangsan"]
    "
    
    // 删除所有元素
    array.removeAll(keepCapacity: true) // []
    print(array.capacity) // "2
    "
    
    // 注意数组容量的变化
    for i in 0..<10 {
        array.append("(i)") // (10 times)
        print("(array) --- (array.capacity)") // (10 times)
    }
    
    // 实例化新的数组
    var array2 = [String]() // []
    array2.append("1") //["1"]
    array2.append("2") // ["1", "2"]
    
    // 拼接数组
    array += array2 // ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "1", "2"]
    
    print(array) // "["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "1", "2"]
    "
    • 小结
      • 如果定义数组时指定了保存对象的类型,择不能向数组中添加其他类型的内容
      • 可以使用 [String]()
      • let 定义的数组是不可变的
      • var 定义的数组是可变的
    // 定义数组
    //var arr: Array<Int>
    //var arr: [Int]
    //arr = [1, 2]
    var arr = [1, 2]
    
    // 1.遍历数组(取值)
    arr[0]
    for item in arr
    {
        print(item) // (2 times)
    }
    // 2.添加
    arr.append(3)
    arr
    // 3.修改
    arr[1] = 9
    arr
    
    // 4.删除
    arr.removeAtIndex(0) // 1
    arr // [9, 3]
    
    // 5.合并
    var arr1 = [3, 5, 7]
    arr += arr1 // [9, 3, 3, 5, 7]
    arr // [9, 3, 3, 5, 7]
    
    // 6.Swift特殊
    for item in arr[0..<2] // 0~1
    {
        print(item) // (2 times)
    }
    
    //arr.removeRange(Range(start: 0, end: 2))
    //arr
    
    // 通过观察可以发现Range其实就是一个半闭区间
    arr.removeRange(0..<2) // [3, 5, 7]
    arr // [3, 5, 7]
    
    arr += arr1[0..<2] // [3, 5, 7, 3, 5]

    • 8.字典

      • 字典
        • 格式 var dict: Dictionary<String, String>
        • 注意: 将OC的{}换成了[]
        • 可变和不可变 var/let
    //var dict: Dictionary<String, String>
    //var dict: [String: String]
    var dict = ["name": "gcy", "age": "30"]
    dict
    
    // 企业开发中字典使用得最多的类型就是 [String: NSObject]类型
    var dict2 = ["name": "gcy", "age": 30, "score": 99.9]
    dict2
    
    // 取值
    dict2["name"]
    
    // 修改
    dict2["name"] = "wy"
    dict2
    
    // 增加
    // 如果key存在就直接修改, 如果key不存在就会增加
    dict2["rank"] = 1
    dict2
    
    // 删除
    dict2.removeValueForKey("name")
    dict2
    
    // 遍历
    // OC写法
    for key in dict2.keys
    {
        print(dict2[key]) // (3 times)
    }
    
    // Swift写法
    // 系统会自动将字典中的key赋值给元祖中的第一个遍历, 会自动将字典中的value赋值给元祖中的第二个遍历
    for (xx, oo) in dict2
    {
        print(xx) // (3 times)
        print(oo) // (3 times)
    }
    
    // 合并
    var dict3 = ["name": "gcy", "age": 30]
    var dict4 = ["score": 99.9]
    
    // 注意点无论是数组还是字典, 只有相同类型才能赋值
    for (key, value) in dict4
    {
        dict3[key] = value // 99.9
    }
    dict3 // ["score": 99.9, "age": 30, "name": "gcy"]

    • 9.字符串

      在 Swift 中绝大多数的情况下,推荐使用 String 类型

    • 使用 String 的原因

    • String 是一个结构体,性能更高

      • String 目前具有了绝大多数 NSString 的功能
      • String 支持直接遍历
    • NSString 是一个 OC 对象,性能略差
    • Swift 提供了 StringNSString 之间的无缝转换

    • 遍历字符串

    let str = "我要飞的更High"
    
    for s in str {
        print(s)
    }
    • 字符串拼接
    let str1 = "zhangsan"
    let str2 = "lisi"
    let i = 10
    
    print(str1 + str2) // "zhangsanlisi
    "
    print("(str1) (str2) (i)") // "zhangsan lisi 10
    "
    • 小结
      • 直接在 "" 中使用 (变量名) 的方式可以快速拼接字符串
      • 小伙伴们再也不要考虑 stringWithFormat
    • 格式化字符串
    for _ in 0...10 {
        let str = String(format: "zhangsan - %04d", arguments: [arc4random_uniform(100)])
        print(str) // (11 times) zhangsan - 0002
    }
    • 小结

      • 在实际开发中,如果需要指定字符串格式,可以使用 String(format:...) 的方式
      • 注意:后面的参数需要放在一个数组中
    • String & Range 的结合

    以下是超级费劲的代码

    let str: String = "beixinke"
    
    var subStr = str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex))
    print(subStr) // "beixinke
    "
    • 建议写法
    let str1: NSString = "beixinke" // "beixinke"
    print(str1.substringWithRange(NSMakeRange(0, 3))) // "bei
    "
    • 字符串
      • OC的字符串是NSString, Swift的字符串String
      • OC的字符串是一个对象, Swift字符串是一个结构体, 效率更高
      • OC中的字符串是一个结尾, Swift字符串不是以结尾
    NSString *str = @"abcdef";
    NSLog("%@", str); // abc
    • Swift中的字符串支持遍历
    let str = "abcdef" // "abc�def"
    print(str) // "abc�def
    "
    
    // 遍历字符串
    for c in str.characters
    {
        print(c) // (7 times)
    }
    
    // 字符串拼接
    var str2 = "gcy"
    str2 += str
    str2 // "gcyabc�def"
    
    // 字符串格式化
    // 可以使用()在字符串中插入任何数据
    let name = "gcy"
    let age = 30
    let res = "name = (name), age = (age)"
    res // "name = gcy, age = 30"
    
    // 2015-10-09 03:04
    let str3 = String(format: "%d-%02d-%02d %02d:%02d", arguments: [2015, 10, 9, 3, 4]) // "2015-10-09 03:04"
    
    // 截取字符串
    // 提示: 在Swift开发中, 我们经常需要将Swift的字符串转换为OC的字符串来操作, 并且Swift自身也意识到了这一点, 所以在OC字符串和Swift的字符串之间转换相当简单
    let str4 = "beixinke"
    //let str5: NSString = str4
    //str5.substringToIndex(4)
    //str5.substringWithRange(NSMakeRange(4, 2))
    
    // as 就是把什么当做什么
    (str4 as NSString).substringWithRange(NSMakeRange(4, 2)) // "in"

    • 10.函数
    • 简单演练
    func sum(a: Int, b: Int) -> Int {
        return a + b
    }
    • 小结

      • 函数定义格式:func 函数名(参数: 参数类型...) -> 返回值 { // 代码实现 }
      • 如果没有返回值, -> 返回值 可以省略
      • -> 是一个很有意思的符号
      • 默认情况下,在调用函数时,第一个参数名是省略的
      • Void == ()
    • 参数名的特殊处理

    • 强制要求参数名

    func sum1(x a: Int, y b: Int) -> Int {
        return a + b
    }
    • 省略参数名
    func sum2(a: Int, _ b: Int) -> Int {
        return a + b
    }
    // 1.没有参数没有返回值
    func say() -> Void
    {
        print("hi") // "hi
    "
    }
    say()
    // 如果没有返回值可以简写
    func say1() -> ()
    {
        print("hi") // "hi
    "
    }
    say1()
    
    func say2()
    {
        print("hi") // "hi
    "
    }
    say2()
    
    
    // 2.有参数没有返回值
    // Swift2.0开始, 会自动将形参列表的第二个参数名称作为标签
    // Swift2.0之前是没有这个特性的, 在Swift2.0之前如果需要显示标签需要在形参名称前面加上#
    func sum(num1: Int, num2: Int)
    {
        print(num1 + num2) // "30
    "
    }
    sum(10, num2: 20)
    
    
    // 3.没有参数有返回值
    func getNumber() -> Int
    {
        return 998
    }
    print(getNumber()) // "998
    "
    
    // 4.有参数有返回值
    func sum2(num1: Int, num2: Int) -> Int
    {
        return num1 + num2
    }
    print(sum2(50, num2: 50)) // "100
    "
    
    
    // 内部和外部参数
    /*
    * 默认情况下所有形参都是内部参数, 也就是说只能在函数内部使用
    * 从Swift2.0开始会自动将形参列表的第二个参数名称作为标签, 也就是说从第二个参数开始, 参数的名称既是内部参数又是外部参数
    * 如何指定外部参数?
    */
    func sum3(num1: Int, y num2: Int)
    {
        print("num1 = (num1), num2 = (num2)") // "num1 = 10, num2 = 20
    "
        print(num1 + num2) // "30
    "
    }
    //sum3(10, num2: 20)
    sum3(10, y: 20)
    
    // 默认参数
    // 如果指定了默认值, 那么在调用方法的时候就可以不用传递数据, 如果不传递数据系统就会使用默认值, 如果传递了就会使用传递的值
    // 在其它语言里面, 默认值一般情况只能是最后一个参数, 但是Swift可以写在任何位置
    func joinString(str1: String, str2: String = "在", str3: String) -> String
    {
        return str1 + str2 + str3 // (2 times)
    }
    joinString("gcy", str2: "也在", str3: "bxk") // "gcy也在bxk"
    joinString("wy", str3: "bxk") // "wy在bxk"
    
    
    // 常量参数和变量参数以及inout参数
    // 默认情况下所有形参都是常量参数, 不能在函数中修改形参的值
    // 如果想在函数中修改形参的值, 那么必须把形参变为变量参数
    // 和OC一样, 在函数中修改形参的值不会影响到外面实参的值
    // 如果想在函数中修改形参之后影响实参, 那么必须把形参变为inout参数
    
    //func swap(a: Int, b: Int)
    //{
    //    let temp = a
    //    a = b  // 不能修改常量参数
    //    b = temp
    //}
    
    //func swap(var a: Int, var b: Int)
    //{
    //    print("a = (a), b = (b)")
    //    let temp = a
    //    a = b
    //    b = temp
    //    print("a = (a), b = (b)")
    //}
    
    func swap(inout a: Int, inout b: Int)
    {
        print("a = (a), b = (b)") // "a = 10, b = 20
    "
        let temp = a // 10
        a = b // 20
        b = temp //10
        print("a = (a), b = (b)") // "a = 20, b = 10
    "
    }
    
    var x = 10
    var y = 20
    print("x = (x), y = (y)") // "x = 10, y = 20
    "
    swap(&x, b: &y)
    print("x = (x), y = (y)") // "x = 20, y = 10
    "
    
    // 可变参数
    // 只要参数是可变参数, 就可以传递一个或多个值
    // 在其它语言中一般情况下可变参数只能是最后一个形参, 而Swift中可以写在任意位置, 但是为了提高代码的阅读性, 还是建议写在最后
    func sum4(nums: Int..., temp: Int) -> Int
    {
        var sum = 0 // 0
        for i in nums
        {
            sum += i // (3 times)
        }
        return sum + temp // 16
    }
    sum4(1, 2, 3, temp: 10) // 16
    
    // 函数嵌套
    // 将一个函数写到另外一个函数的函数体中, 外面称之为函数嵌套
    // 1.被嵌套的函数只能在父函数内部访问
    // 2.被嵌套的函数可以访问外部的变量
    // 应用场景: 两个函数之间依赖较强, 或者一个函数就只给另外一个函数使用
    //          例如: 对数组排序是一个函数, 交换变量又是一个函数, 他们就可以使用函数嵌套
    let value = 55 // 55
    func test()
    {
        let number = 10 // 10
        func demo()
        {
            print("----(number), (value)") // "----10, 55
    "
        }
        demo()
    }
    test()

    • 11.构造函数
      • Swift中要求在创建一个类时必须给这个类中所有的属性进行初始化
      • 如果不能在创建对象时给这个类中所有的属性进行初始化, 那么这些属性必须是可选的
      • 如果已经在构造方法中对所有的属性进行了初始化, 那么这些属性就可以不是可选类型
      • 在给某一个类指定属性的数据类型时, 如果该属性是对象类型, 那么可以指定为可选类型
      • 如果该属性不是对象类型而是基本数据类型, 那么建议直接赋值为0
        • 调用 super.init()的目的主要是为了给对象分配存储空间
      • Swift中如果想在构造方法中使用KVC转换模型, 必须先调用 super.init()
    class Person: NSObject {
    
        var name: String?
        // 如果属性是基本数据类型, 并且是可选类型, 系统不会自动分配存储空间
        var age: Int = 0
    
    //    var name: String
    //    var age: Int
    
    
        // Person()
        override init() {
            // 注意: 在构造方法中必须先初始化本类再初始化父类
            name = "gcy"
            age = 24
            // 当我们重写一个类的构造方法时, 系统内部会悄悄的帮我们调用super.init()
            super.init()
    
        }
    
    
        // 自定义构造方法
        init(name: String, age: Int)
        {
            self.name = name
            self.age = age
    //        以下这一句代码, 能不写就不写
    //        super.init()
        }
    
        init(dict: [String: AnyObject])
        {
            // 注意:Swift中如果想在构造方法中使用KVC转换模型, 必须先调用 super.init()
            super.init()
            setValuesForKeysWithDictionary(dict)
        }
    
        // Swift中打印对象会调用下面这个属性
        override var description: String {
    //        return "name = (name), age = (age)"
            let property = ["name", "age"]
            let dict = dictionaryWithValuesForKeys(property)
            return "(dict)"
        }
    }
    • 注意: Swift开发中一般情况下不用导入头文件, 因为只要所有的文件都在一个命名空间中那么就可以直接使用
    • 默认情况下一个项目的命名空间就是项目名称, 而在同一个项目下的所有文件都在同一个命名空间中
    • 如果自定义一个类, 并且没有重写构造方法, 那么系统会提供默认的构造方法
    • 如果自定义一个类, 并且自定义了构造方法, 那么系统不会提供默认的构造方法

    • 12.getter & setter
      • 自定义 Person 类
    class Person: NSObject {
    
        var name: String?
        var age: Int?
    }
    • getter & setter
    var _name: String?
    
    var name: String? {
        get {
            return _name
        }
        set {
            _name = newValue
        }
    }
    • Swift 中以上形式的 getter & setter 很少用

    • didSet

    • 在 OC 中,我们通常希望在给某一个变量赋值之后,去做一些额外的操作

    • 最经典的应用就是在自定义 Cell 的时候,通过模型的设置方法完成 Cell 的填充
    var length: Int? {
        didSet {
            timeStr = String(format: "%02d:%02d:%02d", arguments: [length! / 3600, (length! % 3600) / 60, length! % 60])
        }
    }
    var timeStr: String?
        var name: String?
            {
            // 在Swift开发中用以下两个方法代替OC中的重写setter方法
            willSet{
               print("赋值之前调用 (newValue)")
            }
            didSet{
                print("赋值之后调用 (oldValue)")
            }
        }
    • 计算型属性
    var title: String {
        get {
            return "Mr " + (name ?? "")
        }
    }
    • 只实现 getter 方法的属性被称为计算型属性,等同于 OC 中的 ReadOnly 属性
    • 计算型属性本身不占用内存空间
    • 不可以给计算型属性设置数值
    • 计算型属性可以使用以下代码简写
    var title: String {
        return "Mr " + (name ?? "")
    }
    • 构造函数
    init(dict: [NSObject: AnyObject]) {
        name = dict["name"] as? String
        age = dict["age"] as? Int
    }
    • 析构函数
    deinit {
        print("88")
    }

    • 13.闭包
      • OC: block类似于匿名函数, 用于封装代码块, 在特定的时候执行
      • 执行一些耗时操作
      • 类型: 返回值类型(^block名称)(形参列表)
               值:
                ^(形参列表){
                    需要执行的代码
                }
    • Swift: 闭包是用于定义函数(Swift中函数就是闭包, 闭包就是一个特殊的函数)
      • 执行一些耗时操作
      • 类型: (形参列表)->返回值类型
        值:
        {
            (形参列表)->返回值类型
            in
            需要执行的代码
    • 闭包的几种格式
      1. 完整写法
        loadData ({ () -> () in
            print("更新UI")
        })
    • 2.如果闭包没有形参, 那么in和in之前的代码都可以省略
        loadData ({
            print("更新UI")
        })
    • 3.如果闭包是函数的最后一个参数, 那么闭包可以写在函数()的后面
        loadData (){
            print("更新UI")
        }
    • 4.如果函数只有一个闭包参数, 那么函数的()可以省略
        loadData {
            print("更新UI")
        }
        func loadData(finished: ()->())
        {
            dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
                print(NSThread.currentThread())
                print("加载数据")
    
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    print(NSThread.currentThread())
                    finished()
                })
            }
        }
    • 能用最简单的就用最简单的写法

    • 闭包的参数和返回值

      示例:
      1.在控制器的View上添加一个UIScrollview, 然后在UIScrollview上添加15个按钮, 让按钮平铺
      2.要求创建UIScrollview以及按钮通过一个方法来创建
      2.1并且按钮的个数必须通过闭包来指定
      2.2并且如何创建按钮也必须通过闭包来指定(UIScrollview上面添加什么控件通过闭包传递)
            /*
            // 1.创建UIScrollview
            let sc = UIScrollView(frame: CGRect(x: 0, y: 200,  UIScreen.mainScreen().bounds.width, height: 50))
            sc.backgroundColor = UIColor.redColor()
    
            // 2.通过循环创建15个按钮
            let count = 15
            let CGFloat = 80
            let height = sc.bounds.height
            for i in 0..<count
            {
                let btn = UIButton()
                btn.setTitle("标题(i)", forState: UIControlState.Normal)
                btn.frame = CGRect(x: CGFloat(i) * width, y: 0,  width, height: height)
                btn.backgroundColor = UIColor.greenColor()
                // 3.将按钮添加到UIScrollview上
                sc.addSubview(btn)
            }
            sc.contentSize = CGSize( CGFloat(count) *  width, height: height)
    
            // 4.将UIScrollview添加到控制器view上
            view.addSubview(sc)
            */
    
            let sc = createScrollview({ () -> Int in
                return 20
                }) { (index) -> UIView in
    //                let btn = UIButton()
    //                btn.setTitle("标题(index)", forState: UIControlState.Normal)
    //                btn.backgroundColor = UIColor.greenColor()
    
                    let label = UILabel()
                    label.text = "标题(index)!!!"
                    label.backgroundColor = (index % 2 == 0) ? UIColor.greenColor() : UIColor.purpleColor()
                    return label
            }
            view.addSubview(sc)
        }
    
        // 技巧: 在编写闭包代码时, 不管三七二十一先写上 ()->(), 然后再修改
        func createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView
        {
            // 1.创建UIScrollview
            let sc = UIScrollView(frame: CGRect(x: 0, y: 200,  UIScreen.mainScreen().bounds.width, height: 50))
            sc.backgroundColor = UIColor.redColor()
    
            // 2.通过循环创建15个按钮
            let count = getNumber()
            let CGFloat = 80
            let height = sc.bounds.height
            for i in 0..<count
            {
                /*
                let btn = UIButton()
                btn.setTitle("标题(i)", forState: UIControlState.Normal)
                btn.frame = CGRect(x: CGFloat(i) * width, y: 0,  width, height: height)
                btn.backgroundColor = UIColor.greenColor()
                */
                let subView = createView(index: i)
                subView.frame = CGRect(x: CGFloat(i) * width, y: 0,  width, height: height)
    
                // 3.将按钮添加到UIScrollview上
                sc.addSubview(subView)
            }
            sc.contentSize = CGSize( CGFloat(count) *  width, height: height)
    
           return sc
        }
    
    }

    • 闭包的循环引用
        // 注意: 在设置闭包属性是可选类型时一定更要用一个()括住闭包的所有的类型, 否则只是指定了闭包的返回值是可选的
        // 错误写法: var callback: ()->()?
        var callback: (()->())?
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    
            // OC中如何解决:  __weak typeof(self) weakSelf = self;
            // Swift中如何解决: weak var weakSelf = self
            // 对应关系:  __weak == weak   __unsafe_unretained == unowned
    
    //        weak var weakSelf = self
            loadData { [unowned self] () -> () in
                print("被回调了")
    
                // 在Swift开发中, 能不写self就不写slef
                // 一般情况下只有需要区分参数, 或者在闭包中使用
    //            self.view.backgroundColor = UIColor.greenColor()
    
    //            weakSelf!.view.backgroundColor = UIColor.greenColor()
    
                self.view.backgroundColor = UIColor.greenColor()
            }
        }
    
        func loadData(finished: ()->())
        {
            callback = finished
            // 1.加载数据
            print("加载数据")
    
            // 2.执行回调
            finished()
        }
    
        // deinit 相当于OC中的dealloc方法
        // 只要一个对象释放就会调用deinit方法
        deinit
        {
            print("88")
        }
    }

    • 14.懒加载
    • 只有外界访问到listData的时候才会去执行闭包, 然后将闭包的返回值赋值给listData
    • 注意: 一定要记住闭包后面需要写上(), 代表执行闭包
    lazy var listData: [String]? = {
            ()->[String]
            in
            print("---")
            return ["gcy", "cyh", "wy", "lsl"]
        }()
    
        // 开发中这样写
        lazy var listData2: [String]? = {
            print("---")
            return ["gcy", "cyh", "wy", "lsl"]
            }()
    
        lazy var listData3: [String]? = self.test()
        func test() -> [String]
        {
            print("+++")
            return ["gcy", "cyh", "wy", "lsl"]
        }
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            print(listData3)
            print(listData3)
            print(listData3)
            print(listData3)
        }

    • 15.单例模式
    • OC中
    + (instancetype)shareNetworkTools
    {
        static NetworkTools *_instance;
        // onceToken默认等于0, 只要执行一次之后就不等于0了, 原理是通过判断onceToken是否等于0决定是否需要执行block
        static dispatch_once_t onceToken;
        NSLog(@"%ld", onceToken);
        dispatch_once(&onceToken, ^{
            _instance = [[NetworkTools alloc] init];
        });
        return _instance;
    }
    • 如果在Swift中编写单例, 推荐使用如下写法
    • 而且苹果有统一的命名规范,但凡是单例统一是用shareInstance

        /*
        static var onceToken: dispatch_once_t = 0;
        static var _instance: NetworkTools?
      
        class func shareNetworkTools() -> NetworkTools {
            print(onceToken)
            dispatch_once(&NetworkTools.onceToken, {
                _instance = NetworkTools()
            })
            return _instance!
        }
        */
      
        /*
        static let _instance: NetworkTools =  NetworkTools()
        class func shareNetworkTools() -> NetworkTools {
            return _instance
        }
        override init() {
            print("++++++")
        }
        */
      static let shareInstance: NetworkTools =  NetworkTools()

    • 16.UITableView基本使用
    // 1.在Swift中遵守协议直接利用逗号隔开
    class ViewController: UIViewController {
    
        override func loadView() {
            let tableView = UITableView()
            tableView.dataSource = self
            tableView.delegate = self
            view = tableView
        }
    
        // MARK: - 懒加载
        lazy var  listData: [String]? = {
           return ["gcy", "cyh", "wy", "sz", "lsl", "fbk"]
        }()
    }
    
    // extension 相当于OC的 Category
    extension ViewController: UITableViewDataSource, UITableViewDelegate
    {
    
        // MARK: - UITableViewDataSource
        func numberOfSectionsInTableView(tableView: UITableView) -> Int {
            return 1
        }
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // TODO: 有问题, 开发中不应该这样写
            return (listData?.count)!
        }
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            var cell = tableView.dequeueReusableCellWithIdentifier("cell")
            if cell == nil
            {
                cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
            }
    
            cell?.textLabel?.text = listData![indexPath.row]
    
            return cell!
    
        }
        // MARK: - UITableViewDelegate
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            print(listData![indexPath.row])
        }
    }


    17.变量类型推导

    • 在 swift 开发中,对象的类型是自动推导
    • 对象的准确类型由创建对象时的代码决定
    • 如果定义变量时,右侧代码具有歧义,可以在左侧以 :类型 指定变量的类型,例如:

      // 个人推荐
      let i: CGFloat = 10
    • 也可以写成

      var j = 10 as? CGFloat
    • 如果某些方法返回的数据类型是 AnyObject/AnyClass,则需要在右侧使用 as 类型 表明类型,并且根据返回值是否是可选项,添加 ! 或者 ?,例如:

      let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
    • 如果某些方法返回类型是 AnyObject/AnyClass,但是对象类型是动态生成的,也就是说,编码时同样无法确定改对象的准确类型,可以在左侧使用 : AnyObject 或者 : AnyClass 告诉编译器暂不处理,例如:

      let cls: AnyClass = NSClassFromString(ns + "." + vcName)!

    提示:as?as! 是刚接触 swift 最令人烦恼的语法之一,苹果也在这个语法规则上多次做过调整,在学习时建议:

    • 多用 option + click 查看对象信息
    • 借助 Xcode 智能提示修改

    • 18.guard--守护者--是swift2.0推出的。这样就不会有嵌套中还有嵌套,避免了强制解包和循环嵌套
    • 在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
    • 动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
    • 将AnyClass类型转换为UIViewController类型
            /*
            苹果也意识到了, Swift会不知不觉的形成多层嵌套, 然代码变得非常丑陋
            所以Swift2.0的时候专门推出了一个条件语句(guard)来解决这个问题
            格式:
            guard 条件表达式 else {
                需要执行的语句
                return
            }
            特点: 只要条件为假才会执行else中的代码
            作用: 用于过滤数据
            对比if else 这哥们只有else{}没有if{}
            */


    19.try catch

    • 在OC中处理异常是通过传入一个NSError的指针来保存错误
    • Swfit中提供了专门处理异常机制 throws -> AnyObject
    • Swift中提供 try catch, 将有可能发生错误的代码放到do中, 如果真的发生了异常就会执行catch
      • try作用: 如果抛出(throws)异常, 那么就会执行catch
      • try!作用: 告诉一定一定没有错误, 不需要处理, 但是如果使用try!发生了错误, 那么程序就会崩溃, 开发中不推荐使用
      • try?作用: 告诉系统可能有错也可能没有错, 如果发生错误会返回一个nil, 如果没有发生错误, 会将数据包装成可选类型
    • 在开发中如果说你抛了一个异常之后,你不想进行任何处理,就可以用try?这样的话try?返回一个nil,程序也不会崩溃。对你就无关痛痒了。
    • 但是你如果用try!抛异常就会崩溃;
    • 但是如果你有try就一定要do...catch否则就会报错

    throw catchXcode 7.0 对错误处理的一个非常大的变化

    • 范例代码

      // 2. 反序列化
      // 1.获取json文件路径
            let jsonPath = NSBundle.mainBundle().pathForResource("MainVCSettings.json", ofType: nil)
            // 2.加载json数据
            let jsonData = NSData(contentsOfFile: jsonPath!)
            // 3.序列化json
            do{
                 // throw是Xcode7最明显的一个变化, Xcode7之前都是通过传入error指针捕获异常, Xocode7开始通过try/catch捕获异常
                let dictArray = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers)
      
                // 遍历字典时候需要明确指明数组中的数据类型
                for dict in dictArray  as! [[String:String]]
                {
                     // 由于addChildVC方法参数不能为nil, 但是字典中取出来的值可能是nil, 所以需要加上!
                    addChildViewController(dict["vcName"]!, title: dict["title"]!, imageName: dict["imageName"]!)
                }
            }catch{
                print(error)
                addChildViewController("HomeTableViewController", title: "首页", imageName: "tabbar_home")
                addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center")
                addChildViewController("DiscoverTableViewController", title: "发现", imageName: "tabbar_discover")
                addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile")
            }
    • 如果能确保代码执行正确,可以强行 try!

    let array = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)

    不过需要注意的是,一旦解析错误,程序会直接崩溃!



    20.private

    • 在swift中,里面的东西都是能够全局访问的,如果你只想只在本文件访问的话,就加上一个private
      • 如果在方法前面加上private, 代表这个方法只能在当前文件中访问
      • 如果在属性前面加上private, 代表这个属性只能在当前文件中访问
      • 如果在类前面加上private, 代表这个类只能在当前文件中访问
    • private也有利于程序员之间的沟通,一看到就知道是和本文件有关,不提供外界使用,点击事件加上@objc就可以知道是关于点击事件
    • 提高阅读性,记得用// MARK: - 内容


    21.便利构造器

    • 在Swift开发中, 如果想快速创建一个对象, 那么可以提供一个便利构造器(便利构造方法--用于快速创建对象)
    • 只要在普通构造方法前面加上一个convenience, 那么这个构造方法就是一个便利构造方法
    • 注意: 如果定义一个便利构造器, 那么必须在便利构造器中调用指定构造器(没有加convenience单词的构造方法)
      • 遍历构造方法的参数不能指定默认参数
    import UIKit
    
    extension UIButton
    {
        class func create(imageName: String, backImageName: String) -> UIButton
        {
            let btn = UIButton()
            // 1.设置背景图片
            btn.setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal)
            btn.setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted)
    
            // 2.设置普通图片
            btn.setImage(UIImage(named:backImageName), forState: UIControlState.Normal)
            btn.setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted)
    
            btn.sizeToFit()
    
            return btn
        }
        // 虽然以上方法可以快速创建一个UIButton对象, 但是Swift风格不是这样写的
        // 在Swift开发中, 如果想快速创建一个对象, 那么可以提供一个便利构造器(便利构造方法--用于快速创建对象)
        // 只要在普通构造方法前面加上一个convenience, 那么这个构造方法就是一个便利构造方法
        // 注意: 如果定义一个便利构造器, 那么必须在便利构造器中调用指定构造器(没有加convenience单词的构造方法)
    
        /*
        定义便利构造器步骤:
        1.编写一个构造方法
        2.在构造方法前面加上 convenience
        3.在构造方法中调用当前类的其他"非便利构造器"初始化对象
        */
        convenience init(imageName: String, backImageName: String)
        {
            self.init()
    
            // 1.设置背景图片
            setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal)
            setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted)
    
            // 2.设置普通图片
            setImage(UIImage(named:backImageName), forState: UIControlState.Normal)
            setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted)
    
            sizeToFit()
        }
    }



    文/Tuberose(简书作者)
    原文链接:http://www.jianshu.com/p/5e2d4c34f18e
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 相关阅读:
    基于redission的分布式锁
    kafka在Windows环境下启动
    Synchronized优化总结
    mysql死锁总结
    索引是建立得越多越好吗?
    show processlist 详解
    RocketMQ高可用机制同步刷盘、异步刷盘和同步复制、异步复制
    Redlock红锁总结
    C#2.0泛型
    《解剖PetShop》系列之四:PetShop之ASP.NET缓存 (转)
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5400627.html
Copyright © 2020-2023  润新知