Swift复习(一)基本语法和特性(一)

一、Swift发展史

  • 2014.8 1.0
  • 2015.9 2.0
  • 2016.9 3.0
  • 2017.9 4.0
  • 2019.3 5.0

Swift 2.0

  • Error handing 增强
  • guard语法
  • 协议支持扩展

Swift 3.0

  • 新的GCD和Core Graphics
  • NS前缀从老的Foundation类型中移除
  • 内联序列函数sequence
  • 新增fileprivate和open两个权限控制
  • 移除了诸多弃用的特性,比如++、--运算符

Swift 4.0

  • extension中可以访问private的属性
  • 类型和协议的组合类型
  • Associated Type可以追加Where约束语句
  • 新的Key Paths语法
  • 下标支持泛型
  • 字符串增强

Swift 5.0

  • ABI稳定
  • Raw strings
  • 标准库新增Result
  • 定义了与Python或Ruby等脚本语言互操作的动态可调用类型

二、Swift和OC的比较

编程范式

  • Swift可以面向协议编程、函数式编程、面向对象编程
  • OC以面向对象编程为主,当然你可以引入类似RAC的类库来进行函数式编程

类型安全

  • Swift是一门类型安全的语言。鼓励程序员在代码中清除明确值的类型。如果代码中使用一个字符串String,那么你不能错误地传递一个整形Int给它。因为Swift是类型安全的,它会在编译的时候做类型检查,并且把所有不匹配的类型作为一个错误标记出来。这样使得程序员在开发中尽可能早地发现和修正错误。
  • 而OC则不然,你声明一个NSString变量,仍然可以传一个NSNumber给它,尽管编译器抱怨,但是你仍然可以作为NSNumber来使用它。

值类型增强

  • 在Swift中,典型的有struct、enum以及tuple都是值类型。而平时使用的Int、Double、Float、String、Array、Dictionary、Set其实都是用结构体实现的,也是值类型。
  • OC中,NSNumber、NSString以及集合类对象都是指针类型。

枚举增强

  • Swift的枚举可以使用整形、浮点型、字符串等,还能拥有属性和方法,甚至支持泛型、协议、扩展等等。
  • OC里面的枚举则鸡肋很多

泛型

  • Swift支持泛型,也支持泛型的类型约束等特性
  • 苹果推出了Swift2.0版本,为了让开发者从OC更好地过渡到Swift上,苹果也为OC带来了generics泛型支持,不过OC的泛型约束也仅停留在编译器警告阶段。

协议和扩展

  • Swift对协议的支持更加丰富,配合扩展、泛型、关联类型等可以实现面向协议编程,从而大大提高代码的灵活性。同时,Swift中的protocol还可以用于值类型,如结构体和枚举
  • OC的协议缺乏强约束,提供的optional特性往往成为很多问题的来源,而如果放弃optional又会让实现代价过大

函数和闭包

  • Swift中函数是一等公民,可以直接定义函数类型变量,可以作为其它函数参数传递,可以作为函数返回值返回
  • OC里面函数仍然是次等公民,需要selector封装或者使用block才能模拟Swift中类似的效果

三、swiftc--强大的命令行工具

llvm编译过程.jpg
swift编译过程.jpg

swiftc常用命令

  • 生成可执行文件 swiftc -0 main.out main.swift
  • Swift Abstract Syntax Tree(AST) swiftc main.swift -dump-ast
  • Swift INtermediate Language(SIL) swiftc main.swift -emit-sil
  • LLVM Intermediate Repressentation(LLVM IR) swiftc main.swift -emit-ir
  • Assembly Language swiftc main.swift -emit-assembly

四、REPL

  • Xcode 6.1 引入了另外一种以交互式的方式来体验Swift的方法
  • Read Eval PrintLoop,简称REPL
  swift build      Build Swift packages
  swift package    Create and work on packages
  swift run        Run a program from a package
  swift test       Run package tests
  swift repl       Experiment with Swift code interactively

操作:

  • 命令行中,输入swift repl即可进入REPL
  • 退出 :quit
  • 帮助 :help
  • 将光标移动到当前行的开始处 control+A
  • 将光标移动到当前行的结束处 control+E

五、Playground

  • Swift Playground首次公布于WWDC2016
  • 最开始是为了让人人都能愉快的学习Swift编程
  • 但发展至今,这个工具越来越强大
  • iPad APP Playgrounds

六、声明变量和常量

  • 使用let来声明常量
  • 使用var来声明变量

类型标注

  • 在声明一个变量或常量的时候提供类型标注,来明确变量或常量能够储存值的类型
  • 添加类型标注的方法是在变量或常量的名字后面加一个冒号,再跟一个空格,最后加上要使用的类型名称
  • 可以在一行中定义多个相关的变量为相同的类型,用逗号隔开,只要在最后的变量名字后边加上类型标注
let a,b,c : Int
let x = 1,y = 1.1,z = "zzz"
a = 10
b = 11
c = 12
print("a = \(a),b = \(b),c = \(c)")
print("x = \(x),y = \(y),z = \(z)")

/*
a = 10,b = 11,c = 12
x = 1,y = 1.1,z = zzz
*/

OC中定义常量常用方法:1.宏定义 2.const 3.类中属性设置为readonly(如果继承重写会失效)

常量和变量命名

  • 常量和变量的名字几乎可以使用任何字符,甚至包括Unicode字符
  • 变量和常量的名字不能包含空白字符、数学符号、箭头、保留的(或者无效的)Unicode码位、连线和制表符。也不能以数字开头,尽管数字几乎可以使用在名字其它的地方。

打印print 和字符串插值

使用\(插值)添加字符串插值,如"a\(b)d"

七、Swift中的数值类型

整数

  • Swift提供了8、16 、 32 、64位编码的有符号和无符号整数
  • 命名方式:例如8位无符号整数的类型是UInt8,32位有符号整数的类型是Int32
  • 通过min和max属性来访问每个证书类型的最小值和最大值

OC中有char、short、int、long、NSInteger类型,分别对应8 、 16 、32 、 64位整形,无符号类型一般为在前面加上unsigned
Swift中的Int对应于OC的NSInteger,在32位CPU为32位,在64位CPU为64位

浮点类型

  • Double:64位浮点数,至少有15位数字的精度
  • Float:32位浮点数,至少有6位数字的精度
  • 在两种类型都可以的情况下,推荐使用Double类型

OC中除了有来自于C的float、double外,还有CGFloat,CGFloat在32位CPU为32位,在64位CPU为64位

数值范围.jpg

Bool

  • Bool: 只有两个值,true或false
  • Swift的类型安全机制会阻止你用一个非布尔量的值替换掉Bool

OC中有0为假,非0为真的概念,Swift中没有

类型别名

  • 类型别名是为一个已存在的类型定义一个可选择的名字
  • 使用关键字typealias定义一个类型的别名
  • 当你想通过在一个在上下文中看起来更合适可具有表达性的名字来引用一个已存在的类型时,这时别名就非常有用了
// 声音采样
typealias AudioSample = UInt8
let sample: AudioSample = 32

八、元组(Tuple)

  • 元组可以把多个值合并成单一的复合型的值
  • 元组内的值可以是任何类型,而且可以不必是同一个类型

元素命名

  • 元组中的每个元素可以指定对应的元素名称
  • 如果没有指定名称的元素也可以使用下标的方式来引用
let error = (statuCode: 404, errorMsg: "服务器在这个路径找不到指定资源")
print(error.statuCode) //404
print(error.1) //服务器在这个路径找不到指定资源

Tuple修改

  • 用var定义的元组就是可变元组,let定义的元组就是不可变元组
  • 不管是可变还是不可变元组,元组在创建后就不能增加和删除元素
  • 可以对可变元素的元素值进行修改,但是不能改变其类型
  • any类型的值可以赋任何类型值

Tuple分解

  • 可以将一个元组的内容分解成单独的常量或变量
  • 如果只需要使用其中的一部分数据,不需要的数据可以用下划线_代替
let error = (404, errorMsg: "服务器在这个路径找不到指定资源")
let (statuCode,errorMsg) = error
print(statuCode)
print(errorMsg)

let (_,errorMsg1) = error
print(errorMsg1)

九、Optional的使用

为什么需要Optional

  • OC里的nil是无类型的指针
  • OC里面的数组、字典、集合等不允许放入nil
  • OC所有对象变量都可以为nil
  • OC只能用在对象上,而在其他地方又用其他特殊值(例如NSNotFound)表示值的缺失

Optional

可以通过给可选变量赋值一个nil来将之设置为没有值

  • 在OC中nil是一个指向不存在对象的指针
  • 在Swift中,nil不是指针,它是值缺失的一种特殊类型,任何类型的可选项都可以设置成nil而不仅仅是对象类型

Optional-If语句以及强制展开

  • 可选项是没法直接使用的
  • 需要用!展开之后才能使用(意思是我知道这个可选项里面肯定有值,展开吧),如果使用了!却没有值,会抛出异常崩溃
  • 可以用if 来可选绑定接收的方式使用
let a: Int?
//a!//expression resolves to an unused variable a!
a = 10
if let a = a{
    print(a)
}

Optional-绑定

  • 可以使用可选绑定来判断可选项是否包含值,如果包含就把值赋给一个临时的常量或者变量
  • 可选绑定可以与if和while的语句使用来检查可选项内部的值,并赋值给一个变量或常量
  • 同一个if语句中包含多可选项绑定,用逗号分隔即可。如果任一可选绑定结果是nil或者布尔值为false,那么整个if判断会被看做else

Optional-隐式展开

  • 有些可选项一旦被设定值之后,就会一直拥有值,在这种情况下,就可以去掉检查的需要,也不必每次访问的时候都进行展开
  • 通过在声明的类型后边添加一个叹号(!)而非问号(?)来书写隐式展开可选项
  • 隐式展开可选项主要被用在Swift类的初始化过程中

Optional-可选链

  • 可选项后面加问号
  • 如果可选项不为nil,返回一个可选项结果,否则返回nil
let a: String? = "abcdef"
let count = a?.count
if let count = count{
    let lastIndex = count - 1
    print(lastIndex) // 5
}

十、Optional的原理

Optional-实现探究

  • Optional其实是标准库里的一个enum类型
  • 数据值其实是Optional枚举的关联值
  • 用标准库实现语言特性典型
public  enum Optional<Wrapped> : ExpressibleByNilLiteral {
  case none
  case some(Wrapped)
  
  @inlinable public var unsafelyUnwrapped: Wrapped { get } 
}
  • Optional.none就是nil
  • Optional.some则包装了实际的值
// 使用Optional枚举来定义可选值
// 和使用String?定义的一模一样
let a: Optional<String> = "abcdef"
  • 泛型属性 unsafelyUnwrapped,理论上我们可以直接调用unsafelyUnwrapped获取可选项的值
let a: Optional<String> = "abcdef"
let count = a.unsafelyUnwrapped.count
print(count) // 6

十一、创建和初始化字符串

初始化字符串

  • 字面量
  • 初始化器语法
  • isEmpty检查是否为空串
var emptyString = ""
var anotherEmptyString = String()
if (emptyString.isEmpty){
    print("这个字符串是空的")
}

字面量

  • 字符串字面量是被双引号("")包裹的固定顺序文本字符
  • Swift会为str常量推断类型为String
    let str = "a string"

多行字面量

  • 多行字符串字面量是用三个双引号引起来的一系列字符
  • 多行字符串字面量把所有行包括在引号内,开始和结束默认不会有换行符
  • 当你的代码中在多行字符串字面量里包含了换行,那个换行符同样会成为字符串里的值。如果你想要使用换行符来让你的代码易读,却不想让换行符成为字符串的值,那就在那些行的末尾使用反斜杠(\)
  • 要让多行字符串字面量起始或结束于换行,就在第一或最后一行写一个空行
  • 多行字符串可以缩进以匹配周围的代码。双引号(""")前的空格会告诉Swift其他行前应该有多少空白是需要忽略的
  • 如果你在某行的空格超过了结束的双引号("""),那么这些空格会被包含

字符串里的特殊字符

  • 转义特殊字符\0(空字符),\\(反斜杠),\t(水平制表符),\n(换行符),\r(回车符),\"(双引号),\'(单引号)
  • 任意的unicode标量,写作\u{n},里面的n是一个1-8位的16进制数字,其值是合法unicode值
  • 可以在多行字符串字面量中包含双引号(")而不需要转义。要在多行字符串中包含文本""",转义至少一个双引号

扩展字符串分隔符(Raw String)

  • 在字符串字面量中放置扩展分隔符来在字符串中包含特殊字符而不让它们真的生效
  • 把字符串放在双引号(")内并由井号(#)包裹
  • 如果字符串里有"#则首尾需要两个##
  • 如果你需要字符串中某个特殊符号的效果,使用匹配你包裹的井号数量的井号并在前面写转义符号\
let str = #"Line 1\nLiine 2"#
let str1 = ##"Line 1\#nLiine 2"##
let str2 = ###"Line 1\###nLiine 2"###

print(str)
print(str1)
print(str2)

/*
Line 1\nLiine 2
Line 1\#nLiine 2
Line 1
Liine 2
*/

十二、字符串的常见操作

字符串的可变性

  • var 指定的可以修改
  • let 指定的不可修改
  • 对比OC(NSString和NSMutableString)

字符串是值类型

  • String值在传递给方法或者函数的时候会被复制过去
  • 赋值给常量或者变量的时候也是一样
  • Swift编译器优化了字符串使用的资源,实际上拷贝只会在确实需要的时候才进行

操作字符

  • for-in循环遍历String中的每一个独立的Character
  • Character类型
  • String值可以通过传入Character数组来构造

字符串插值

  • 字符串插值是一种从混合常量、变量、字面量和表达式的字符串字面量构造新String值的方法
  • 每一个你插入到字符串字面量的元素都要被一对圆括号包裹,然后使用反斜杠前缀
  • 类似于NSString的stringWithFormat方法,但是更加简便,更强大
  • 可以在扩展字符串分隔符中创建一个包含在其他情况下会被当做字符串插值的字符
  • 要在使用扩展分隔符的字符串中使用字符串插值,在反斜杠后使用匹配首尾井号数量的井号
print(#"Write a interpolated string in Swift using \(multiplier)."#)
print(#"6 times 7 is \#(6 * 7)."#)
/*
Write a interpolated string in Swift using \(multiplier).
6 times 7 is 42.
*/

十三、如何使用索引访问和修改字符串

字符串索引

  • 每一个String值都有相关的索引类型,即String.Index。它相当于每个Character在字符串中的位置
  • startIndex属性来访问String中第一个Character的位置。endIndex属性就是String中最后一个字符后的位置
  • endIndex属性并不是字符串下标脚本的合法实际参数
  • 如果String为空,则startIndex和endIndex相等
  • 使用index(before:)index(after:)方法来访问给定索引的前后
  • 要访问给定索引更远的索引,你可以使用index(_:offsetBy:)* 使用indices属性来访问字符串中每个字符的索引
let string = "abcdefg"
print(string[string.startIndex]) // a 
//print(string[1])//报错
print(string[string.index(before:string.endIndex)]) // g
print(string[string.index(after:string.startIndex)]) // b
print(string[string.index(string.startIndex,offsetBy:3)]) // d
print(string[string.index(string.endIndex,offsetBy:-3)]) // e

插入

  • 插入字符,使用insert(_:at:)方法
  • 插入另一个字符串的内容到特定的索引,使用insert(contentsOf:at:)方法
var string = "abcdefg"
string.insert("x",at:string.index(string.startIndex,offsetBy:2))
print(string)//abxcdefg
string.insert(contentsOf:"12345",at:string.index(string.endIndex,offsetBy:-2))
print(string)//abxcde12345fg

删除

  • 移除字符,使用remove(at:)方法
  • 移除一小段特定范围的字符串,使用removeSubrange(_:)方法
var string = "abcdefg"
string.remove(at: string.index(string.startIndex,offsetBy:2))
print(string)//abdefg
string.removeSubrange(string.index(string.endIndex,offsetBy:-4)..<string.index(string.endIndex,offsetBy:-2))
print(string)//abfg

十四、子字符串、字符串比较

子字符串

  • 使用下标或者类似prefix(_:的方法得到的子字符串是Substring类型(注意这里string的s是小写)
  • Substring拥有String的大部分方法
  • Substring可以通过String的构造器转成String类型
let string = "abcdefg"
let index = string.index(of: "e") ?? string.endIndex
let beginning = string[..<index]
let newString = String(beginning)
print(beginning) // abcd
print(beginning is String) // false
print(beginning is Substring)  // true
print(newString) // abcd
print(newString is String) // true
print(newString is Substring) // false
  • 子字符串重用一部分原字符串的内存
  • 修改字符串或者子字符串之前都不需要花费拷贝内存的代价
  • String和Substring都遵循StringProtocol协议,也就是说它基本上能很方便地兼容所有接受StringProtocol值的字符串操作函数

字符串比较

  • 字符串和字符想等性(==和!=)
  • 前缀相等性hasPrefix(_:)
  • 后缀相等性hasSuffix(_:)
let string = "abcdefg"
let string1 = "abcdefghijk"
print(string == string1) // false
print(string.hasPrefix("abcd")) // true
print(string1.hasPrefix("abcd")) // true
print(string.hasSuffix("efg")) // true
print(string1.hasSuffix("hijk")) // true
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 1.新建Xocode Swift 程序 2.(基本使用) 在Swift中没有.h 和.m文件 只有一个.Swift...
    圆小米123阅读 1,630评论 0 7
  • YES 2014WWDC发布 常量和变量使用注意 在实际过程中,建议先定义常量,如果需要修改再改变为变量(更加安全...
    南冯阅读 537评论 0 0
  • 1.常量和变量 常量用let修饰,定义之后值不以修改,变量用var修饰,定义之后值可以修改。Swift中定义常量和...
    你猜猜阅读 1,621评论 0 0
  • Swift 与 OC 语言的区别 1.Swift 面对协议编程、 面向函数编程、面向对象编程。 函数成了一等公民...
    Eddiegooo阅读 581评论 0 0
  • 一、Swift 构建对象 对于 Objective-C分为两步 alloc 分配内存 init 初始化 [[cla...
    老黑来袭阅读 1,800评论 2 1