Swift学习总结
基础部分
程序是指令的集合,写程序就是协议系列的指令去控制计算机做我们想做的事情。
编译:将程序设计语言转换成计算机能够理解的机器语言,或者是某种中间代码的过程。
冯诺依曼体系结构的计算机
1.使用二进制
2.程序存储执行
变量和常量
定义变量和常量是为了保存数据。变量和常量就是某种类型的值得存储空间
var a:String = "w"
a = "c"
var b:Int
b=20
var d=1000
let d:Int = 10
//d=100 //编译错误 常量不可以再赋值
let c = 100
说明
1.Swift 有非常强大的类型推断,所以定义变量或者常量时,如果可以的话应该直接使用类型推断,不用手动指定类型
2.如果可以的话应该尽可能使用常量而不是变量
语言元素
var a:Int = 10
关键字:有特殊含义的单词
标识符:给变量、常量、函数、类、结构、协议、枚举、方法、属性等起的名字。
1.字母数字和下划线 数字不能开头
2.严格区分大小写,大小写敏感
3.不能用关键字做标识符
4.使用驼峰书写原则(命名变量、常量、函数、方法、属性第一个单词小写,以后每个单词的首字母大写,命名类、协议、结构、枚举每个单词首字母都大写)
5.见名知意
6.在命名私有的属性和方法时,以下划线开头
运算符:Swift中的运算符其实都是函数
1.赋值运算符 (=、+=、-=、%=、*=、/=...)
2.算术运算符 (+、-、*、/、%)
3.比较运算符 (==、!=、<=、>=、<、>)
4.逻辑运算符 (&&、||、!)
5.条件运算符 (x ? a : b)
6.其他运算符 ([]、.、??、?、!)
字面(常)量:
1.整数字面量:10、1_234_567、0x10、0o10、0b10
2.小数字面量:1.2345、1.2345e2、0xab.cdp2
3.字符字面量:"c"、"\n"、"\u{41}"、"\u{e9}"
4.字符串字面量:"hello"
5.布尔字面量:true、false
6.空值字面量:nil
7.类型字面量:String.self、UILabel.self
分隔符:将不同的语言元素符号分开
说明:Swift每个语句后面的分号是可写可不写的,写代码时尽量保证一行只有一条语句这样就可以省略掉分号。
分支和循环
分支
- if...else...
var thyAnswer:Int
thyAnswer = 10
var answer=Int(arc4random_uniform(100)+1)
if thyAnswer>answer{
print("大了")
}else if thyAnswer
- switch...case...default
var score = Int(arc4random_uniform(10)+1)
switch answer{
case 1...4:print("学的什么狗屎")
case 5:print("努把力就及格啦")
case 6:print("继续加油啊,及格啦")
case 7:print("继续保持,再多花点功夫")
case 8:print("注意细节啊!")
case 9:print("别马虎了!很不错了")
default:print("你这么厉害 我天")
}
循环
- while
var sum = 0
var i = 0
while i<=100{
sum+=i
}
print(sum)
- repeat...while...
var sum = 0
var i = 0
repeat{
sum+=i
i+=1
}while i<=100
print(sum)
- for...
var sum = 0
for i in 0...100{
sum+=i
}
print(sum)
穷举法:
穷尽所有可能性直到找到正确的答案
下面的程序实现了百钱白鸡问题的求解。
for i in 0...20{
for j in 0...33{
let z = 100-i-j
if i*5 + j*3 + z/3 == 100 && z % 3 == 0 {
print("公鸡:\(i),母鸡:\(j),小鸡:\(z)")
}
}
}
说明
在循环中可以使用break关键字来提前终止循环,也可以使用contin关键字使循环直接进入下一轮,但是应该尽量减少对break和continue的使用,因为他们不会让你的程序变得更好。
综合案例:Craps赌博游戏。
func roll() ->Int{
return Int(arc4random_uniform(6)+1)
}
var needGoOn = false
var playerFirst = roll() + roll()
print("玩家摇出了\(playerFirst)")
switch playerFirst{
case 7,11:print("玩家获胜")
case 2,3,12:print("庄家获胜")
default:needGoOn=true
}
while needGoOn {
var playerSecond = roll() + roll()
print("玩家摇出了\(playerSecond)")
if playerSecond == playerFirst{
print("玩家获胜")
needGoOn=false
} else if playerSecond == 7{
print("庄家胜")
needGoOn=false
}
}
容器
数组
数组是使用连续的内存空间保存多个同类型的元素的容器,因为数组中的元素在内存中是连续的,所以可以使用下标运算来访问数组中的元素,实现随机存取。
- 创建数组
let array1:[Int]=[]
let array2:Array = []
let array3 = [1,2,3,4,5,6]
let array4 = [Int](count:5,repeatedValue;0)
let array5 = Array(count:5,repeatedValue;0)
- 添加元素
array1.append(2)
array3.insert(1,atIndex:0)
array += [5]
- 修改元素
array3[1]=100
- 删除元素
array1.removeLast()
array1.removeAll()
array3.removeAtIndex(2)
array3.removeRange(1...2)
- 遍历数组
1.方式1
for i in 0..
2.方式2 只读循环
for temp in array3[1...3]{
print(temp)
}
说明:
for-in循环是一个只读循环,这也意味着在循环的过程中不能对数组中的元素进行修改
3.方式3
for (index,value) in array3.enumerate(){
if index==0{
array3[index]=1
}
print("\(index).\(value)")
}
说明:
操作数组时最重要的是不要越界访问元素。数组对象的count属性表明了数组中有多少个元素,那么有效的索引范围是0到count-1
数组中的元素可以是另外的数组,那么我们就可以构造多维数组。最常见的是二维数组,它相当于是一个有行有列的数组。二维数组可以模拟表格、矩阵、棋盘、格子类游戏的地图。所以在实际开发中使用非常的广泛
下面是用二维数组模拟表格的例子
let names = ["杨慧","潘金莲","王宝强","宋喆","马蓉"]
let coursesArray = ["如何出轨","怎样当好经纪人","怎么做一个贱人"]
var scoresArray = [[Double]](count: 5, repeatedValue:[Double](count: 3, repeatedValue: 0))
func randomInt(min:UInt32,max:UInt32) ->Int{
return Int(arc4random_uniform(max-min+1)+min)
}
for i in 0..
集合
集合在内存中是离散的。集合中的元素通过计算HASH CODE(哈希码或散列码)来决定存放在内存中的什么位置,集合里面不允许有重复元素。
- 创建集合
var set:Set=[1,2,3,4,5,4,5,2,1]
- 添加元素
set.insert(100)
- 删除元素
set.remove(5)
set.removeFirst()
- 遍历元素
for (index,temp) in set.enumerate(){
print(index,temp)
}
- 集合运算(交集、并集、差集)
var set1:Set=[1,2,3,4,5,4,5,2,1]
var set2:Set=[34,31,3,4,5,4,5]
set1.intersect(set2)
set1.union(set2)
set1.subtract(set2)
字典
字典是以键值对的方式保存数据的容器,字典中的每个元素都是键值对的组合,通过键可以找到对应的值。
- 创建字典
var dict:[Int:String] = [
1:"hello",
2:"good",
3:"wonderful",
5:"heihei"
]
- 添加元素和删除元素
dict[4]="yo"
dict[5]=nil
dict.removeValueForKey(5)
- 修改元素
dict[4]="yo"
dict[4]="heheihei"
- 遍历元素
for key in dict.keys{
print("\(key)--->\(dict[key]!)")
}
for value in dict.values{
print(value)
}
for (key,value)in dict.enumerate(){
print("\(key).\(value.0)--->\(value.1)")
}
- 字典排序之后是数组
let result = dict.sort { $0.1<$1.1 }
print(result)
重要操作
- 排序
1.sort //返回一个新的排好序的
2.sortInPlace //在原来的基础上直接排序
说明:
排序方法的参数是一个闭包,该闭包的作用是比较数组中的两个元素谁大谁小。
let array = [1,2,3,2131,23,12,312,3,123,12]
let newarray=array.sort { $0>$1 }
let newarray3=array.sort({$0<$1})
let newarray1=array.sort { (one:Int, two:Int) -> Bool in
return one
- 过滤
let array = [1,2,3,2131,23,12,312,3,123,12]
//筛选掉不满足条件的数据
let newarray3=array.filter { $0>50}
- 映射
let newarray=array.map{ sqrt(Double($0)) }
//通过映射对数据进行变换处理
print(newarray)
- 归约
let result1=newarray.reduce(0, combine: +)
print(result1)
函数和闭包
函数是独立的可重复使用的功能模块,如果程序中出现了大量地重复代码,通常都可以将这部分功能封装成一个独立的函数。在Swift中函数是“一等公民”,函数可以作为类型来使用,也就是说函数可以赋值给一个变量或者常量,可以将函数作为函数的参数或者返回值,还可以使用高阶函数。
func 函数名(参数列表?)(throws|rethrows?) -> 返回类型(如果是void 返回类型也可以省略){
函数的执行体
(return 表达式?)
}
- 外部参数名
func mymin(a x:Int,b y:Int) ->Int{
return x < y ? x : y
}
//调用的时候要写外部参数名
print(mymin(a:3,b: 5))
- inout参数
inout 输入输出参数(不仅将数据传入函数还要从函数中取出数据)
func creX(inout x:Int) ->Int{
函数体
}
//inout 类型的参数前要加上&符号
creX(&x)
print(x)
- 可变参数列表
//Swift中函数的参数列表可以是可变参数列表(参数的个数可以是任意多个)
func vari(a:Int...) ->Int {
函数体
}
print(vari(90,8,87,56,456,3474))
闭包就是没有名字的函数或者称之为函数表达式(Lambda表达式),在OC中与之对应的概念叫block。如果一个函数的参数类型是函数,我们可以传入一个闭包表达式,如果一个函数的返回类型是函数,我们可以返回一个闭包。如果一个类的某个属性是函数,我们也可以将一个闭包表达式赋值给它。
{(参数列表?) (->?) in 代码}
面向对象编程(oop)
基本概念
对象:接收消息的单元,对象是一个具体的概念。
类: 是对象的蓝图和母版,是一个抽象的概念。
消息:对象之间沟通的一种手段,通过给对象发消息,可以让对象执行对应的动作来解决问题。
四大支柱
抽象:定义类的过程就是一个抽象的过程,需要做数据抽象和行为抽象,数据抽象找到对象的属性(保存对象状态的存储属性),行为抽象找到对象的方法(可以给对象发的消息)。
封装:
- 观点1:我们在类中写的方法,其实就是在封装AP1给别人用,方法的内部实现可能会很复杂,但是这些对调用者来说,是不可见的,调用者只能看到方法一有一个简单清晰的接口,这就是封装。
- 观点2:将对象的属性和操作这些属性的方法绑定在一起。
- 观点3:隐藏一切可以隐藏的实现细节,只提供简单清晰的编程接口(界面)。
继承:从已有的类创造新类的过程
多态:同样的引用,做了不一样的事情。
同样对象类型(Pet类型)接受相同的消息(调用相同的方法)
但是做了不一样的事情
实现多态的关键步骤:
1.方法重写(自乐在继承父类的过程中对父类已有的方法进行重写,而 且不同的子类给出各自不同的实现版本)
2.对象造型(将子类对象当成父类型来使用)
三个步骤
1.定义类
- 数据抽象
- 存储属性
- 行为抽象
- 方法(写到类里面的函数或者说是跟对象绑定的行为就是方法)
- 对象方法:给对象发的消息
- 类方法:给类发的消息,与对象的状态无关的方法 - 计算属性
- 方法(写到类里面的函数或者说是跟对象绑定的行为就是方法)
- 构造器
- 便利构造器
- 指派构造器(convenience)
- 必要构造器(required)
2.创建对象
3.给对象发消息
class Triangle{
var a:Double
var b:Double
var c:Double
init(a:Double,b:Double,c:Double){
self.a=a
self.b=b
self.c=c
}
//类方法(发给类的消息与对象的状态无关)
//此处的static也可以换成class作用相同
static func isValid(a:Double,b:Double,c:Double) ->Bool{
return a+b > c && a + c > b && b + c > a
}
//对象方法(发给对象的消息与对象的状态有关)
var perimeter:Double{
get{return a+b+c}
}
}
let a = 1.0
let b = 2.0
let c = 3.0
//在创建对象前先调用类方法判定给定的三条边能否构成三角形
//类方法是发给类的消息所以不用创建对象直接通过类名调用
if Triangle.isValid(a, b: b, c: c){
let t=Triangle.init(a: a, b: b, c: c)
//对象方法是发给对象的,所以对象要先被创建了才能被调用
print(t.perimeter())
}else{
print("构不成啊")
}
相关内容
- 枚举
enum Gender {
case Male
case Female
}
- 结构(体)
struct Student2{
var name:String
var age:Int
func study(coursename:String){
print("\(name)正在学习。")
}
mutating func get(){
age+=1
}
}
总结结构和类的区别以及使用的环境:
结构和类的区别
区别1:结构对象是值类型,类对象是引用类型,结构会重新创建一个新对象,而类不会。
值类型在赋值的时候会在内存中进行对象的拷贝
引用类型在赋值的时候不会进行对象拷贝,只是增加了一个引用
结论:我们自定义新类型时优先考虑使用类而不是结构除非我们要定义的是一种底层的市局结构(保存其他数据的类型)
区别2:值类型会自动初始化
区别3:结构中的方法在默认情况下是不允许修改结构中的属性 如果一定要修改要加行mutating
- 类扩展(extension)
extension String {
var length: UInt32 {
get { return UInt32(self.characters.count) }
}
subscript(index: Int) -> Character {
get { return self[self.startIndex.advancedBy(index)] }
}
}
- 运算符重载
func +(one:fraction,two:fraction) ->fraction{
return one.add(two)
}
- 下标运算(subscript)
subscript(index: Int) -> Character {
get { return self[self.startIndex.advancedBy(index)] }
}
- 访问修饰符
- private
- public
- internal
面向协议编程(pop)
协议
协议是方法的集合(计算属性相当于就是方法)
他可以把看似不相关的对象的公共行为放到一个协议中
协议在Swift的开发中 大致有三种作用:
1、能力 - 遵循了协议就意味着具备了某种能力
2、约定 - 遵循了协议就一定要实现协议中的方法
3、角色 - 一个类可以遵循多个协议,一个协议可以被多个类遵循,遵循协议就意味着扮演了某种角色,遵循了多个协议就代表你可以扮演多个角色
Swift 中的继承是单一继承(一个类只能有一个父类),如果希望让一个类具备多重能力就可以使用协议来实现(c++是通过多重继承来实现)
protocol 协议名:(父协议1,父协议2?){
//方法的集合(计算属性相当于就是方法)
}
依赖倒转原则
设计模式-委托回调
一个对象想做什么事情,但是自身没有能力做这件事情就可以使用委托回调。
- 代理模式
- 委托回调
- 设计一个协议,让被委托方遵循协议并实现协议中的方法
- 委托方有一个属性是协议类型的,通过该属性可以调用协议中的方法。
注意:委托方的协议类型的属性通常要写成weak引用,而且要是可空类型
其他
- 协议组合: protocol<协议1,协议2,协议3...>
- 可选方法:
- 协议扩展: 对协议中的方法给出默认实现
-可以再协议扩展中给协议中的方法提供了默认实现
也就是说 如果某个类遵循了协议但是没有实现这个方法就直接使用默认实现
那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)
泛型
让类型不再是程序中的硬代码,可以设计出更通用的代码
泛型(generic) 让类型不再是程序中的硬代码(写死的东西)
定义一个虚拟类型T,调用函数时传入的参数类型来决定T到底是什么
- 泛型函数
func mySwap(inout a:T,inout _ b:T){
(a,b) = (b,a)
}```
- 泛型类、结构、枚举
###相关知识
- 泛型限定
泛型限定 可以解决泛型中出现的不能比较大小写等情况
```swift
//泛型限定
//限定T类型必须要遵循了Comparable协议的类型
func myMin(a:T,b:T) ->T{
return a < b ? a : b
}
- where子句
错误处理
enum MyError:ErrorType{
case 1
case 2
case 3
}
- throw
- throws、rethrows、
- do
- catch
- try
- 详情请看1702
边角知识
- ARC
- 正则表达式
- 高级运算符
- 字符串
- 嵌套类型