随着WWDC2017大会的结束不出所料苹果重磅推出Swift4.0.在4.0之前的语言其实是很让开发人员头疼的,首先版本差异大,底层结构变化,更可气的是上下不兼容.不更新Xcode你不能对最新版本的手机系统开发,更新了,低版本的Swift语言报错.所以了解一下Swift前后版本的语言差异对整个app的开发以及swift语言的认知是很有帮助的.那么既然这样我们为什么要学那?因为Swift语言与大部分的开发语言形式上更加相似,对于iOS程序员在学习其他语言会提供很大的帮助,对于oc的语言我想大部分其他程序员看起来还是很头疼的,还有Swift语言更加严谨,更加安全,而且苹果公司最新文档的更新早就不在用oc了,可见其推动Swift语言的决心而且很多公司也在做Swift的人才储备。得了好处我就不多说了,感兴趣的同学可以谷歌一下。而且可喜的是Xcode9兼容了Swift3.0与Swift4.0开发选择,所以一下我只对Swift2.0与Swift3.0的语法总结做一下规整,希望对新学Swift的同学有一些帮助,在Swift2.0的总结中我会与oc的语言做一下简单的对比,帮助oc开发人员加深理解:
Swift2.0 总结:
一.swift与oc的不同之处
1.与oc工程的一些不同点
(1).swift中没有了 .h/.m文件 只有一个 .swift文件 声明和实现都需要在同一个文件中
(2).在swift中 没有非零既真的概念 只有true 和 false
(3). ( )表示初始化 也可以表示执行
(4).在swift中 默认所有的文件共享 所有的对象的方法也是默认可以调用
(5).命名空间: 在同一个项目下 叫做命名空间 在同一个命名空间下 所有的文件共享
(6).swift 属性默认都是 强引用的
(7).swift中 init 方法 就是构造方法,并且所有的构造方法 都叫 init
2.类的声明
oc的
@interface ViewController : UIViewController
swift的
class ViewController: UIViewController { }
3.可选项
可选项: ?(有值吗) 1.有值 2.没有值(nil)
必选项: ! 程序员承诺 一定有值 同样也表示 强制解 包
每添加一个 '!'的时候都需要思考为什么,安全吗 程序员承诺一定有值
经典报错:fatal(致命的) error: unexpectedly(意外的,不希望) found nil while unwrapping(解包) an Optional(可选的) value
(1).可选项在打印的时候 会自动带上 Optional(10)
(2).可选项不能够直接参与运算
声明可选项 在类型后面追加 '?"
var a: Int?
4.常量和变量
let: 常量 一经赋值 就不能被更改
var: 变量 可以被更改
尽量使用常量 等到需要修改的时候在改成 使用 var 来修饰
- 数据类型
(1).swift中 数据的类型是自动推导的 根据 '=' 右边的类型来去推断变量的具体类型
option + click 最常用的热键值 没有之一
swift 是一个类型极其苛刻的语言
(2).可以提前声明类型: let 变量名: 类型
func demo7() {
let a=10
let b=12.4
//swift中 不支持 隐含形式的转换类型,直接a+b会报错哦
// let c = a+b
// print(c)
}
二.分支结构
swift中推荐使用的分支结构
1.switch case
(1).每个case 分支 必须要有一段可以执行的代码
(2).临时变量 的定义不需要加在 {} 中
(3).OC中只能判断基本数据 swift中可以判断任意类型
(4).可以同时case 多个选项
(5).不需要写 break
func demo7() {
let m = "22200"
switch m {
case "18000", "22200":
print("高级工程师")
case "12000":
print("中级工程师")
case "8000":
print("初级工程师")
default:
print("你是猴子派来的吗")
}
}
/*
1.条件语句 没有 '()'
2.{} 不能省略
3.在swift中 没有非零既真的概念 只有true 和 false
*/
func demo2() {
let a = 10
// if a > 5
// print("haha")
// print("hehe")
if a > 0 {
print("大于0")
} else {
print("小于0")
}
}
2.??操作符: 快速判断可选项是否为 nil 如果为nil 给定默认值,在判断字符串 或者基本数据类型的时候用处很大
例如:tableView 数据源方法 list?.count ?? 0 这里list代表一个数组对象如果为nil那么给一个默认值0;
三.解决可选项问题
推荐两个方法 if let 和 guard let..else
if let 快速赋值 并且判断是否为空 不为空 才能够进入分支 意味着值就是一个必选项
func demo4() {
//url中包含中文 需要转义
let urlString = " [http://www.douni.com?type](http://www.douni.com?type) "
if let url = NSURL(string: urlString) {
//当option + click string 时可以看到url是可选项
convenience init? 便利的构造函数 有可能能够实例化一个对象 有可能无法实例化一个对象(nil)
let request = NSURLRequest(URL: url)
print(request)
}
}
guard(守卫) let ... else 和if let的 作用刚好相反
能够减少一层分支嵌套
Xcode 7.0 swift2.0 才推出的语法
在项目使用的非常多 我自己也推荐这种写法
func demo5() {
let urlString =" [http://www.douni.com?type=
中国](http://www.douni.com?type=中国) "
guard let url = NSURL(string: urlString) else{
//如果url为nil当前代码 就直接返回
return
}
//程序运行到这里'守卫'的对象 就一定有值
let request = NSURLRequest(URL: url)
print(request)
}
四.循环
现代写法 apple推荐的写法
// 0..<10 0 ~ 9 表示范围 不会向后包含
func demo1() {
for i in 0..<10 {
print(i)
}
}
// _ 表示忽略
func demo3() {
for _ in 0..<10 {
print("嘿嘿")
}
}
// 0...10 会向后包含 范围 0 ~ 10
func demo2() {
for i in 0...10 {
print(i)
}
}
五.字符串
OC : NSString 类 继承 NSObject
swift: String 结构体 更加高效,可以通过热键查看
func demo1() {
let str: String = "叶良辰"
print(str)
//实例化一个字符串
var str1 = String()
str1 = "hello world"
print(str1)
}
//oc
-(void)demo1{
NSString * str = @"叶良辰";
NSLog(@"%@",str);
}
//字符串的一些基本特性
func demo2() {
//1.长度 一个中文 字符串的长度 是3 个字节
let str = "你若安好"
//获取字节长度
let l = str.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
//获取字符串的长度
// let l = str.characters.count
print(l)
//2.字符串的拼接
let str1 = "便是晴天"
// NSString stringWithFormat:"%@%@"
let str2 = str + str1
print(str2)
//字符串的判断
//与oc方法的 isEqualToString方法一样
if str == str1 {
print("======")
} else {
print("!=====")
}
//ASC 值进行比较
let s1 = "abc"
let s2 = "xyz"
if s1 > s2 {
print("大于")
} else {
print("小于")
}
//字符串的转义
let i = 10
let str4 = "\(i)"
print(str4)
//字符格式化
// 1 23 45 拼接成 01:23:45
//format 格式化的类型 arguments: 可变参数
let h = 1
let m = 23
let s = 45
let str5 = String(format: "%02d:%02d:%02d", h,m,s)
print(str5)
}
我们可以看到相对于oc来讲字符串的拼接比较等等得到了大大的简化我们不需要再使用那长长的方法来进行比较
六.字典和数组
OC:NSArray [xxx]
swift: [元素]
let 修饰的数组 是不可变的数组
var 修饰的数组 是可变的数组
OC:字典 NSDictionary {}
swift: [key1 : value1, key2 : value2 ]
let 修饰的不可变的字典
var 修饰的可变的字典
func demo() {
//数组的定义 数组的类型也是可以自动推导
//数组中不推荐存放不同类型的元素 数组是通过索引来访问元素
let array = ["张三","lisi","王五",16]
//实例化一个数组
var array1 = [String]()
//添加元素
array1.append("赵六")
array1.append("张三")
print(array1)
//改
//通过索引来去修改元素
array1[0] = "郭敬明天见"
array1[1] = "夏洛特烦恼"
//删
// array1.removeFirst()
// array1.removeLast()
array1.removeAll()
//查
for str in array1 {
print(str)
}
//通过索引查询数据
//fatal error: Array index out of range
// let str = array1[1]
// print(str)
}
//数组的拼接
func demo1() {
var array1 = [String]()
let arr1 = ["夏洛特烦恼","郭敬明天见"]
let arr2 = ["哈哈","呵呵"]
array1 += arr1
array1 += arr2
//拼接
let arr = arr1 + arr2
print(array1)
}
//字典的基本操作
func demo2() {
//声明
let dict = ["name": "范冰冰","age" : 18,"title": "女神"]
//实例化字典类型
var dict1 = [String : NSObject]()
dict1["name"] = "张学友"
dict1["title"] = "歌神"
dict1["age"] = 18
// print(dict1)
//删
dict1.removeValueForKey("age")
//改
dict1["name"] = "周杰伦"
//查 在OC中遍历 字典 使用block
//()内键值的名字自己随意取 但是一个对应的是 键(key) 第二个对应是值(value)
for (key, value) in dict1 {
print(key, value)
}
let name = dict1["name"]
print(name)
}
七.函数
swift中调用本类的函数和属性 self可以省略 也可以加上 闭包内调用本类的函数和属性时 必须加 self
override func viewDidLoad() {
super.viewDidLoad()
//函数的调用
//函数的第一个参数名称可以省略
// sum(10, b: 20)
// let result = square(width: 10, height: 20)
// print(result)
demo3()
}
//函数没有返回值的三种写法
//1.没有返回值的第一种写法
func demo1() -> () {
print("哈哈哈")
}
//2.第二种写法 Void V要大写
func demo2() -> Void {
print("嘻嘻嘻")
}
//3.函数没有返回值的第三种写法
func demo3() {
print("黑hi额hi额")
}
//函数的外部参数
//外部参数 width是提供给调用方来进行使用的
//内部参数 a 是提供给函数内部使用的
func square(width a: Int,height b: Int) -> Int{
return a * b
}
//加法的函数
func sum(a: Int,b: Int) -> Int {
return a + b
}
析构函数
deinit {
//和OC dealloc 方法类似
print("VC 886")
}
八.闭包的基本使用
OC中block有哪些特点
1.可以定义为变量 block可以是一段提前准备好的可以执行代码块
let finishedCallBack = {
//就是一段可以执行的代码块
print("我就是回调执行的代码块")
}
2.可以当做函数的参数传递
loadData1("sisi", finished: finishedCallBack)
3.在需要执行的时候执行 可以产生回调的效果 (协议/代理,通知) block 可以有返回值 但是用的不多
(1).() -> () 闭包的最基本的类型 没有参数 没有返回值的闭包
(2).系统将闭包进行了简写
(3).必须是闭包参数是函数的最后一个参数 才可以进行闭包的简写 -> 将可执行的闭包挪动到参数体外面 ,形成 '尾'随闭包
最多使用的还是这一种方法
loadData("sisi") { (dataString) -> () in
//在闭包中智能提示 不是很好使 通常 需要生写
print(dataString)
}
如果有闭包参数 建议 将闭包参数写在最后一个 可以写成尾随闭包
demo({ () -> () in
print("回调")
}, userId: "sisi")
}
func loadData(userId: String, finished: (dataString: String) -> ()) {
//耗时操作
dispatch_async(dispatch_get_global_queue(0, 0)) {
//在全局队列中执行耗时操作
print("开始加载数据")
//将结果回调到主线程
dispatch_async(dispatch_get_main_queue(), {
//执行回调
// print("===========")
//执行函数 其实闭包 知识一个匿名函数
// {
// print("回调的结果")
// }()
finished(dataString: "办证: 138XXXXXXX")
print("+++++++++++")
})
}
}
.....................................................................................
loadData1("思思") { () -> () in
print("回调的结果")
}
//返璞归真
loadData1("思思", finished: { print("回调结果") })
//比较残暴一种写法
loadData1("思思") {
print("回调的结果")
}
func loadData1(userId: String, finished: () -> ()) {
//耗时操作
dispatch_async(dispatch_get_global_queue(0, 0)) {
//在全局队列中执行耗时操作
print("开始加载数据")
//将结果回调到主线程
dispatch_async(dispatch_get_main_queue(), {
//执行回调
// print("===========")
//执行函数 其实闭包 知识一个匿名函数
// (): 标示初始化 也可以标示执行
// {
// print("回调的结果")
// }()
finished()
print("+++++++++++")
})
}
}
4.在block中 使用self 需要考虑 循环引用
函数只是 一种特殊闭包 在swift 中闭包才是老大(第一公民)
九.闭包的循环引用
解决方式有两种[unowned self]和[weak self]但是[unowned self]当有网络延时的时候会崩
十.懒加载
懒加载 什么时候调用 什么时候在初始化
在swift 懒加载有特殊的写法
就是一个可以被执行闭包这样可以在里面进行一些设置
lazy var nameLabel: UILabel = { () -> UILabel in
print("我懒了吗")
let l = UILabel()
return l
}()
简单写法
lazy var ageLabel: UILabel = UILabel()
十一 单例
一般在音频,文件,网络请求,管理类都适合用单例
由于单例这较为特殊那我就贴出代码大家比较一下:
第一种 : oc的方法
在.h文件中实现
在.m文件中实现
在桥接文件里导入头文件
去文件里使用单例
第二种Swift方法
一般我们用下边这个方法就好,一句话搞定(swift中单例对象很简单 最主要需要 依赖 let 一经赋值就不可以被更改)
//简单写法
static let sharedTools: SoundTools = SoundTools()
十二.面向对象必选属性的构造
自定义对象一般作为一个模型保存数据 创建对象的类继承NSobject
如: let p=person()
我们创建一个name属性 var name: String 因为这样所以name 是一个必选属性,系统会报错,解决方式有两种
- 第一种
var name: String = ""
- 第二种
初始值为nil 没有分配内存空间 延迟分配控件 什么时候用 什么分配内存控件 这种方式 是后面会大量运用到
var name: String?
3.第三种 构造方法解决,比较麻烦
override: 重写 覆盖, 父类已经存在这个函数 子类需要在父类的这个函数进行其他的一些操作
var name: String
//person的构造方法
override init() {
self.name = "李冰冰"
super.init()
//description 是NSobject的属性所以super.init之后
self.description
// self.name = "李冰冰"他是子类或本类的属性所以在super.init之前
}
let s =Student()
在创建一个子类对象的时候就会调用本身的init构造方法,而init构造方法中有[super init]就会往下调用父类的init构造方法一次类推形成遍历直到NSObject调用了init方法,对象才算构造完成,就可以使用"s"对象了这时对象"s"中就会拥有本类和父类的一切属性或方法
十三.函数的重载
我们可以看到在上述的构造方法中我们很容易把对象的属性写死导致参数不灵活所以可以利用函数的重载
函数的重载: 函数名相同 函数的参数的类型或者参数的个数不同 就形成了函数的重载
重载不仅仅发生在构造函数中
注意: 不能够和 重写混合记忆
重载有啥好处呢?
1.可以简化程序员记忆 wash(member:洗衣粉)
2.重载可以简化编译 操作符 减少大量使用不同的操作符 可以提升编译的效率
重载是面向对象的重要标记之一
class Person: NSObject {
//初始值都是 nil 等到super.init结束之后 还是没有值
var name: String
//age 整数类型也是nil oc整数的默认值是 0
//this class is not key value coding-compliant for the key age.'
//但是Int 在OC中是基本数据类型 不会主动调用 '初始化'方法 ,直接设置初始值之后,就会分配内存空间 一般基本数据类型都设初始值
var age: Int = 0
override init() {
self.name = "李冰冰"
self.age = 18
super.init()
}
//swift所有的构造函数 都是 init
/*
构造函数 有责任也有义务 确保 必选属性必须有值
*/
//函数的重载名字一样,参数类型或个属不同
init(name: String, age: Int) {
self.name = name
self.age = age
super.init()
}
十四 字典转模型 kvc (这是基本接近于底层的字典转模型了,但是他还是依据的oc的运行时原理Swift3.0,4.0不知底层是否发生变化,望有大神给予指点)
//字典转模型 使用KVC设置初始值
init(dict: [String : NSObject]) {
//kvc设置初始值 在运行时 间接给 '对象' 发送 setValue: forKey:
super.init()
//遍历字典中的键值对 给对象转发 setValue: forKey:
setValuesForKeysWithDictionary(dict)
}
//会检查 字典中的键值对应的属性的key 是否存在 如果存在就只直接赋值 不存在就会将消息转发给 setValue: forUndefinedKey:
override func setValue(value: AnyObject?, forKey key: String) {
super.setValue(value, forKey: key)
}
//如果没有重写 默认会调用父类的这个方法
//就是起过滤作用
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
print(value,key)
// super.setValue(value, forUndefinedKey: key)
}
十五.便利的构造函数
override init() {
self.name = "李冰冰"
self.age = 18
super.init()
}
- 可以返回nil
2.必须使用 '本类' 的构造函数来进行实例化 需要使用 'self' 来调用构造函数
3.遍历的构造函数本质上 是基于本类 '指定' 的构造函数(必须实例化对象) 来进行快速的扩展
convenience init?(name: String, age: Int) {
//通常 年龄需要做合法性的检查 让一个人年龄在一个合法区间内
if age < 0 || age > 1000 {
//不能够实例化该对象 必须是失败的构造器才能够返回 nil
return nil
}
//保证对象在 满足条件下 能够被实例化
//生写 指定构造函数
self.init(dict: ["name" : name, "age" : age])
}
//字典转模型 使用KVC设置初始值
init(dict: [String : NSObject]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
十六.getter setter
//使用 didSet 重写set方法
var name: String? {
//值设置完毕之后会调用到didSet
didSet {
//通常在swift开中 使用这个方法来替代重写 set方法
//一般这里面操作 模型数据的绑定
print(name)
}
}
//只读属性 readonly
//在swift 中又特殊的叫法: 计算型属性
//必须依赖其他的属性进行计算,每次执行都会调用
var title: String? {
get {
//只读属性是没有存储空间
return "Mr" + (name ?? "")
}
}
//是否成年
// var isAdult: Bool {
// get {
// return age > 18
// }
// }
//计算型属性可以简写 可以将 getter省略掉
var isAdult: Bool {
return age > 18
}
好吧swift2.0整理到此结束,好累。。。下面Swift3.0(这里我总结的代码就以截图形式呈现吧,因为通过Swift2.0的总结我发现对于我们程序员来说在阅读注释方面的还是喜欢Xcode下的格式更舒服一些)
Swift3.0总结
- Swift 中取消了预编译指令包括宏
- Swif取消了objective-c 的指针及其他不安全访问的使用
- 舍弃早起应用Smalltalk语法,全面改为点语法
- 3.0对Foundation框架做了重大调整 ,去除了NS前缀 ,将绝大部分class(类)转换为struct(结构体)
- switch支持任意类型的数据以及各种比较操作,不仅仅是整数以及测试相等。
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。
6.你可以使用for-in来遍历字典,需要两个变量来表示每个键值对。字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
二,变量与常量
三,可选项
四,switch的用法
五,字符串的长度和遍历
六,通过闭包回调传递参数
七,循环引用
八,必选属性的构造过程
九,可选项思维导图
Swift4.0
暂不更新原理基本和2.0 ,3.0大同小异大家可以用新版的Xcode进行书写来发现其中的异同点,这样对Swift的理解是非常重要的,有时大家看10篇文章不如写上一段代码理解的透彻现附上WWDC相关视屏及资料
http://mp.weixin.qq.com/s/DTl9INDMrkivciJwJSWd5A