Swift 开发语法

文/Tuberose(简书做者)
原文连接:http://www.jianshu.com/p/5e2d4c34f18e
著做权归做者全部,转载请联系做者得到受权,并标注“简书做者”。


简介

  • 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
  • 示例json

    // 1.建立UIView let customView = UIView(frame: CGRect(x: 0, y: 0, width: 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)
  • 小结swift

    • 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(width: 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 语句条件的 () 能够省略
    • 可是 {} 不能省略
  • ifapp

    • 大部分用于和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)\n" /* 可选类型注意点: * 在开发中通常状况下尽可能不要强制解包一个可选类型, 不然会引起错误 */ //let url = NSURL(string: "http://www.baidu.com") //print(url) //let request = NSURLRequest(URL: url!) // "Optional(http://www.baidu.com/)\n" 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\n" //name = "gcy" print(name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) // "nil\n" print((name?.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) ?? 0)) //"0\n"
  • 小结
    • ?? 是一个很是有用的操做符,可以快速对 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("---") // "---\n" // 遍历 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"]\n" // 遍历每个元素 for a in arr { print(a) // (2 times) } // 像 OC 同样打印 print(arr as NSArray) // "(\n zhangsan,\n lisi\n)\n"
  • 数组中保存的对象类型
// 数组中保存的都是字符串 let arr = ["zhangsan", "lisi"] // 数组中保存的是 NSObject let arr1 = ["zhangsan", 1]
  • 小结

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

// 定义只能保存字符串类型数组 var array: [String] // 初始化数组 array = ["zhangsan"] // 添加元素 array.append("lisi") print(array) // "["zhangsan", "lisi"]\n" // 删除元素 array.removeAtIndex(1) // "lisi" print(array) // "["zhangsan"]\n" // 删除全部元素 array.removeAll(keepCapacity: true) // [] print(array.capacity) // "2\n" // 注意数组容量的变化 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"]\n"
  • 小结
    • 若是定义数组时指定了保存对象的类型,择不能向数组中添加其余类型的内容
    • 可使用 [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\n" print("\(str1) \(str2) \(i)") // "zhangsan lisi 10\n"
  • 小结
    • 直接在 "" 中使用 \(变量名) 的方式能够快速拼接字符串
    • 小伙伴们不再要考虑 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\n"
  • 建议写法
let str1: NSString = "beixinke" // "beixinke" print(str1.substringWithRange(NSMakeRange(0, 3))) // "bei\n"
  • 字符串
    • OC的字符串是NSString, Swift的字符串String
    • OC的字符串是一个对象, Swift字符串是一个结构体, 效率更高
    • OC中的字符串是一个\0结尾, Swift字符串不是以\0结尾
NSString *str = @"abc\0def"; NSLog("%@", str); // abc
  • Swift中的字符串支持遍历
let str = "abc\0def" // "abc�def" print(str) // "abc�def\n" // 遍历字符串 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\n" } say() // 若是没有返回值能够简写 func say1() -> () { print("hi") // "hi\n" } say1() func say2() { print("hi") // "hi\n" } say2() // 2.有参数没有返回值 // Swift2.0开始, 会自动将形参列表的第二个参数名称做为标签 // Swift2.0以前是没有这个特性的, 在Swift2.0以前若是须要显示标签须要在形参名称前面加上# func sum(num1: Int, num2: Int) { print(num1 + num2) // "30\n" } sum(10, num2: 20) // 3.没有参数有返回值 func getNumber() -> Int { return 998 } print(getNumber()) // "998\n" // 4.有参数有返回值 func sum2(num1: Int, num2: Int) -> Int { return num1 + num2 } print(sum2(50, num2: 50)) // "100\n" // 内部和外部参数 /* * 默认状况下全部形参都是内部参数, 也就是说只能在函数内部使用 * 从Swift2.0开始会自动将形参列表的第二个参数名称做为标签, 也就是说从第二个参数开始, 参数的名称既是内部参数又是外部参数 * 如何指定外部参数? */ func sum3(num1: Int, y num2: Int) { print("num1 = \(num1), num2 = \(num2)") // "num1 = 10, num2 = 20\n" print(num1 + num2) // "30\n" } //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\n" let temp = a // 10 a = b // 20 b = temp //10 print("a = \(a), b = \(b)") // "a = 20, b = 10\n" } var x = 10 var y = 20 print("x = \(x), y = \(y)") // "x = 10, y = 20\n" swap(&x, b: &y) print("x = \(x), y = \(y)") // "x = 20, y = 10\n" // 可变参数 // 只要参数是可变参数, 就能够传递一个或多个值 // 在其它语言中通常状况下可变参数只能是最后一个形参, 而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\n" } 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, width: UIScreen.mainScreen().bounds.width, height: 50)) sc.backgroundColor = UIColor.redColor() // 2.经过循环建立15个按钮 let count = 15 let width: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: width, height: height) btn.backgroundColor = UIColor.greenColor() // 3.将按钮添加到UIScrollview上 sc.addSubview(btn) } sc.contentSize = CGSize(width: 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, width: UIScreen.mainScreen().bounds.width, height: 50)) sc.backgroundColor = UIColor.redColor() // 2.经过循环建立15个按钮 let count = getNumber() let width: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: width, height: height) btn.backgroundColor = UIColor.greenColor() */ let subView = createView(index: i) subView.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height) // 3.将按钮添加到UIScrollview上 sc.addSubview(subView) } sc.contentSize = CGSize(width: 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() } }原文:http://www.jianshu.com/p/5e2d4c34f18e
相关文章
相关标签/搜索