swift的基础语法
这样吧,先把swift4.0教材的先分享给大家。swift4和swift3的基本上没有多大的差别。
1.初识swift
1.1swift中的如何导入框架
在oc中导入框架或其他头文件一般都是
#import <UIKit...>
#import "xxxxx"
而使用swift的方式进行导入
import UIKit ....
1.2定义标识符
在swift中定义标识符需要明确告知这个标识符是常量或是变量,不能不写,不然编译器也不能通过。在swift中定义可以使用let
var
来定义常量和变量。
声明的格式:
// 类型是放在标识符的后面,且第一个字母的大写的,有别于OC的写法
let\var 标识符名称 : 类型 = 值
常量声明:
let a : Int = 10
变量声明:
var b : Int = 20
注意的地方是在在swift中Int类型和OC的中的NSInteger类似的,而Double类型和OC中的double类型类似。
1.3语句结束的标识
在swift中,一句代码结束了,可以写;
,也可不写,但是这要看情况的;
比如在一行中只有一条代码,那么就可以不写分号作为结束的标识,但是写了也报错。
又比如在一行中有多条代码,那么这就必须要写分号,但是这种做法并可取。
1.4打印
在OC中打印是:
NSLog(@"xxxxx");
在C中的打印就是:
printf("xxxxxx");
在swift中的则是:
print("xxxxx")
oc中的NSLog方式在swift中不可取,而且@
符号在swift中也基本上不怎么使用,除非是特殊情况下。
print(a) // 打印a
print("a is \(a)") // 将a作为一个参数打印出来->相当于NSLog(@"%@", a);
2.常量和变量的使用
常量的本质就是保存在内存中的地址是不可以修改的。。
在定义的时候,可以优先使用常量let进行定义,如有必要的时候进行修改,这样是有利于数据安全的。
定义基本的数据类型:
let m : Int = 10
定义对象
let view : UIView = UIView() // -> alloc init,这就是默认的构造方法
在swift中创建一个view,不像是在OC中创建,不需要写alloc init,直接写类名,然后在后面添加一个小括号就可以了。这一步就相当于是OC中的alloc init了。
定义了这个view后就不能再次进行创建了。
view = UIView() // 这是不行的,view是常量。。
获取这个常量中的内部属性:
view.backgroundColor = UIColor.red
view.frame = CGRect(origin: CGPoint(x:100, y:100), size: CGSize(width:100, height:40)) // CGRectMake(0, 0, 100, 100)
view.alpha = 0.5
// swift中增加了Bool类型 (true/false)
view.isHidden = true
在swift中没有类似于OC的那种setXxxxx方法:
[view setBackgroundColor ..... ];
而且在swift中新增了Bool类型,和OC的BOOL对应。
3.类型推导
在swift中的有的时候,声明的变量可以不用写明标识符的数据类型,因为swift是强类型的语言,他可以自己推导出前面标识符的数据类型(前提是这个标识符后面紧跟着值)。
let i : Int = 10 --> 可以修改成 let i = 10
因为这个10就是Int类型的数据,他会倒推回来这个i就是Int类型的数据。我们还可以查看这个数据的类型:
同样定义对象也是可以的:
let view = UIView()
let button = UIButton()
当声明的时候如果没有马上赋值的时候,就需要跟上这个数据的类型,假如不跟类型:
// 这是会报错的
let x
x = 20
所以就必须要给出数据的类型。
let x : Int
x = 20
这样写就相当于是x在内存中进行了初始化,但是没有马上进行赋值操作,而后再给x进行赋值,如果再次对x赋值的话就会报错,因为这个x是let类型的常量,不能做更改的。
4基本运算
需要提到的是在swift中,没有类似于OC的那种隐式转换的模式,所有的运算中,都是需要数据是有相同的类型的,不匹配的数据类型进行运算,编译器会报错。
整型转浮点型:
let m = 10
let n = 2.2
let tempM = DoubleI(m)
let result = n+ tempM
这里对m进行了类型强转,在OC中,对类型的转换一般的写法是(double) xxxx
,而在swift中的写法则是Doublel(x)
,把需要转换的数据类型写在括号里。
浮点型转整型:
let tempN = Int(n)
let results = m + tempN
这和OC一样的,在浮点型转为整型的时候,不会进行四舍五入的。
5.逻辑分支
在OC中的逻辑控制,一般都会提到,if,if...else,switch等,而在swift中多了一个guard...else。
5.1 if 、 if...else
在swift中这个条件是不需要括号的,这条件两边的空格也就相当于是个隔断了。判断语句也不在有类似于OC的那种非零\nil即真的说法了,必须有个明确的判断真或假的条件语句。
let a = 10
if a > 0 {
print("\(a)大于0")
}
let a = 10
if a > 0 {
print("\(a)")
} else {
print("小于10")
}
5.2 else...if
这基本就和OC是一样的。
let score = 92
if score < 0 || score > 100 {
print("不合理的分数")
} else if score < 60 {
print("不合格")
} else if score >= 60 && score < 75 {
print("中")
} else if score >= 75 && score < 90 {
print("良")
} else if score <= 100 {
print("优")
}
5.3三目运算符
let m = 30
let n = 20
var result = 0
// if方式进行判断
if m > n {
result = m
} else {
result = n
}
// 三目运算符方式进行判断
result = m > n ? m : n
5.4 guard...else
这是在swift2.0之后推出的,能使用if...else的地方都能使用guard...else,但是反过来未必。guard一定要和else一起使用,而且使用的地方也必须是在函数中。
语法结构:
guard *判断语句* else {
*****
break\return\continue...
}
语句组
当判断语句的条件满足的时候,就会去执行语句组,但是在不满足的情况下,就会去执行else中的语句,并且必须写上break、return、continue等关键字作为结束符。
// 需求:满了18岁就可以去网吧上网
let age = 20
// 函数->if模式
func judgeCanOnline(_ age : Int) {
/// 判断年龄可以上网
if age >= 18 {
// 如果条件比较多嵌套的话,这样的可读性就不好
print("可以上网")
} else {
print("回家")
}
}
---------------------------------------------------------------------------------------------
// guard
func online(_ age : Int) {
// 1.判断年龄
guard age >= 18 else {
print("回家找妈妈")
return
}
print("上网")
}
// 最后需要调用这个函数
上面的两种方法看似没什么区别,仿佛if的方法来写,流程更加的清楚,但是如果需求如下:满了18岁可以才可以到网吧上网,而且必须带有身份证,且身上带有现金才能上网,这样做,就会在if里面嵌套很多的if,这样做的话,可读性就不好,但是如果使用guard方法来做,就会清晰明了很多。
// guard方式
func online(_ age : Int, card : Bool, money : Bool) {
// 1.判断年龄
guard age >= 18 else {
print("回家找妈妈") // 不满足就返回
return
}
// 2.带身份证
guard card else {
print("回家拿身份证") // 没带身份证就回家去拿,进行返回
return
}
guard money else {
print("拿钱去") // 在有钱的条件才能进行上网。
return
}
// 流程走下来,要依次满足上面的三个条件才能进行上网。
print("上网")
}
调用函数...
online(10, card: true, money: true)
online(20, card: false, money: true)
online(age, card: true, money: true)
// 打印结果
回家找妈妈
回家拿身份证
上网
这样做,整个流程都是比较清晰的,而且代码量也比if嵌套的少。
5.5 switch
在swift中,也有switch的用法,而且在swift中,对switch有了更加优化的处理,比OC多了一些功能。
在swift中有,switch后面的括号是可以不需要的,case后面的break语句也是可以不需要的,swift是默认添加了的。如果希望case穿透的话,那就需要添加一个fallthrough关键字。
5.5.1基本用法
/// 0:男, 1:女
let gender = 0
switch gender {
case 0 :
print("男")
case 1 :
print("女")
default :
print("其他")
}
5.5.2case后面进行多个条件判断
在cese后面进行多个条件的判断,在OC中是不能的,但是在swift中是可以的。
switch gender {
case 0 , 1 :
print("正常人")
break
default :
print("其他")
break
}
5.5.3浮点型的判断
let a = 3.14
switch a {
case 3.14:
print("π")
default:
print("not π")
}
5.5.4判断字符串
在OC中,对于字符串的判断,是不允许的,如果要进行判断,一般都会使用isEqualTo:
来进行判断,但是在swift中就可以进行字符串的判断。
let m = 20
let n = 30
let operation = "+"
var result = 0
switch operation {
case "+" :
result = m + n
case "-" :
result = m - n
case "*" :
result = m * n
case "/" :
result = m / n
default :
print("非法操作")
}
5.5.5区间判断
在swift中引入了一个区间的概念,区间一般都表示的数字区间。
开区间的表示方式:0..<10
闭区间的表示方式: 0...10
以上就只有这两种表达方法。
let score = 88
switch score {
case 0..<60 :
print("不及格")
case 60..<75 :
print("中")
case 75..<90 :
print("良")
case 90...100 :
print("优")
default :
print("不合理分数")
}
其中在swift中,默认的case和switch是对齐的。
6.流程控制
在流程控制中,一般会使用到for循环,while,以及do...while来控制流程。在swift中也是类似的。
6.1for循环
swift中的for循环相比于OC中的for循环来讲就是可以不写括号,基本的使用方法也是差不多的:
for var i = 0; i < 10; i += 1 {
print(i)
}
以上的代码可以优化成for in形式,也是较为常见的形式。
for j in 0..<10 { // 优化成区间的模式来做
print(j)
}
下面假如说我们在遍历循环的时候,不需要使用这个下标,我们就可以用_
来替代这个i
或j
:
for _ in 0...9 {
print("hha")
}
6.2 while & do...while
首先while中的判断语句和if中的判断语句是类似的,没有非假即真的说法,必须是一个明确的Bool值,即为true和false。同样的是判断语句也不需要用小括号括起来。
6.2.1 while
var a = 10
while a > 0 {
a -= 1
print(a)
}
6.2.2 repeat...while
在OC中有do...while,但是在swift中,用repeat来代替do,其他的用法都是一致的。
repeat {
a += 1
print(a)
} while a < 20
7.字符串
字符串在任何一门语言中应该说是用的比较多的了,在OC中,字符串的类型是NSString,是一个对象,而在swift中,字符串是是String,是一个结构体,性能更高了。在OC中声明一个字符串得使用@" "
进行声明,而在swift中,使用的是" "
。在swift中还提供了NSString和String的无缝转换。
7.1定义一个字符串
let operation = "+" /// 根据类型推导
let name : String = "wj"
声明一个字符串和声明其他的数据的方法是一致的。
7.2遍历字符串
for c in name.characters {
print(c)
}
以上的遍历字符串的在OC并没有以上类似的方法。
7.3拼接字符串
7.3.1两个字符串进行拼接
在OC中药拼接两个字符串,一般会使用[someString stringByAppendingString:]这个方法,但是在swift中,假如要拼接两个字符串,直接使用+
就可以了。
let str1 = "wj"
let str2 = " is good"
var str = str1 + str2 /// 用加好就可以进行拼接
7.3.2字符串和其他的数据类型进行拼接
在OC中,假如使用到拼接其他的数据类型的数据到字符串中,我们一般会使用到,
[NSString stringWithFormat:@"......"];
但是在swift中,我们使用的是另一种方法进行拼接。
let age = 18
let height = 1.88
str = "my name is \(name), age is \(age), height is \(height)"
也就是把需要引用的数据用\()
进行括起来。
7.3.3拼接字符串的同时进行格式化处理
有的时候,在拼接数据的时候,往往要对数据本身进行处理之后再进行拼接。比如说,我们定义一个时间是02:03
。如果我们使用的是以上的那种格式进行处理,
let minute = 2
let second = 3
let time = "0\(minute):0\(second)"
虽说这样显示的数值是正确的,但是在9s以后,显示的数值将会变成02:010
这样显然是不符合要求的。
所以我们就需要使用到swift中String的方法进行处理。
let time = String(format: "%02d:%02d", arguments:[minute, second])
以上的format和OC中的类似,在占位符后面添加02
表示用两位表示,不足位的用0表示。参数则是用一个[ ]
括起来。
7.4字符串的截取
在OC中要截取字符串的做法是定位到字符串的子字符串所在开始位置和字符串的长度就可以截取,但是在swift中有下列的方法就可以进行截取。
// 以下的方法就可以得到子字符串
urlString.substring(from: <#T##String.Index#>)
urlString.substring(with: <#T##Range<String.Index>#>)
urlString.substring(to: <#T##String.Index#>)
我们还可以让String类型的字符串转换成NSString类型的字符串然后得到子字符串String as NSString
,这样一来,整个String类型的字符串就变成了NSString类型的字符串了。
(urlString as NSString).substring(to: <#T##Int#>)
(urlString as NSString).substring(with: <#T##NSRange#>)
(urlString as NSString).substring(from: <#T##Int#>)
在转成NSString类型的字符串后,进行处理的时候,我们传入的参数就简化了,传入是Int类型,NSRange类型,就比之前的String.Index类型简单一些。
let urlString = "www.baidu.com"
/// 将String类型转为NSSstring类型 String as NSString
let header = (urlString as NSString).substring(to: 3)
let body = (urlString as NSString).substring(with: NSMakeRange(4, 5))
let footer = (urlString as NSString).substring(from: 10)
print("header is \(header), body is \(body), footer is \(footer)")
8.数组
数组是由有相同的有序的元素构成的,Array,是泛型集合。
8.1定义数组
定义数组的方式很多,这里就简单介绍下。
8.1.1定义不可变的数组
定义不可变数组,使用let修饰标识符定义出来的数组就是不可变的数组。
let array = ["name", "wj", "haha", "12weqw", "asdasdas"] // 推导方法
let array1 : Array<String> = ["name", "wj", "haha"]
let array2 : [String] = ["name", "wj", "haha"]
按住Option键会显示的是数组类型,而数组中装的是String类型的元素。
let intArray = [1, 2, 4, 5]
// 这个数组所装的元素都是Int类型的。
8.1.2定义可变数组
定义可变数组,使用var修饰标识符定义出来的数组就是可变的数组。
//var arrayM = Array<String>()
var arrayM = [String]() // 创建了一个可变数组,使用的较多的方法
以上创建的数组中的元素类型是String的。
8.2可变数组的基本操作
对于可变数组,可以进行增删改以及取出元素等操作。在OC中,也有类类似的的方法。
8.2.1添加元素
array.append(<#T##newElement: String##String#>)
在后面的括号中写上需要添加的元素。
// 在数组的最后添加一个元素
arrayM.append("we")
arrayM.append("wee")
arrayM.append("weee")
// 在数组的第1个位置插入一个元素
arrayM.insert("insert", at: 1)
8.2.2删除元素
删除元素,一般来说都是删除全部或者是按照下标的方式进行删除。
arrayM.removeAll() // 删除全部的元素
arrayM.remove(at: 0) // 删除弟0个元素
8.2.3修改元素
修改元素的时候需要拿到所要修改元素的下标,然后进行修改。
arrayM[0] = "123"
8.2.4取出数组的元素
要取出在数组的元素,同理是需要用到数组的下标,但是在swift中有别于OC的做法,没有setObject等放,直接就是用下标进行取值。
arrayM[0]
8.3遍历数组
8.3.1根据下标进行遍历
for i in 0..<array.count {
print(array[i])
}
加入需要用到下标的情况下,使用以上的这种方法进行遍历比较好。
8.3.2直接遍历数组
for name in array {
print(name)
}
在不需要使用下标的情况下,使用上面的这种方法。
8.3.3遍历部分的数组
在swift中提供了遍历部分的数组,
for name in array[0..<2] {
print(name)
}
这样的写法就是在array后面写一个区间,相当于就是重新生成了一个新的数组,然后在新的数组中进行遍历。但是这种写法并不常见。
8.4数组的合并
数组的合并的方法,按照之前OC的是思路就是把一个数组挨着遍历然后把里面的数组拼接到另一个数组中。arrayAddArray:
这些方法不予以考虑。
就像之前的字符串的拼接,我们同样可以使用+
进行拼接,这样同样可以把两个数组进行合并。
// 需要注意的是:数组的合并必须是由相同类型的数组进行合并,不同类型的数组不能由`+`连接进行合并的。
let result = array + arrayM
9.字典
字典的创建和数组的创建的方式类似,同样是分为可变和不可变的类型。
9.1字典的定义
9.1.1定义一个不可变的字典
同数组一样,使用let关键字定义的字典为不可变的字典。
let dict = ["name" : "wj", "age" : "12", "heigth" : "1.88"]
let dict1 : Dictionary<String, Any> = ["name" : "wj", "age" : 12, "heigth" : 1.88]
let dict2 : [String : Any] = ["name" : "wj", "age" : 12, "heigth" : 1.88]
注意的是使用的是中括号,不是大括号,系统会自动判断是放置的一个个元素还是一对对的键值对。 -> 类型是[String : String]
9.1.2创建可变字典
//var dictM = Dictionary<String , NSObject>()
//var dictM = [String : NSObject](); // -> 用于创建对象
var dictM = [String : Any](); // -> 用于指定类型
以上就是创建字典的方法
9.2可变字典的操作
9.2.1可变字典的添加
dictM["name"] = "wj"
dictM["age"] = 12
dictM["height"] = 1.88
dictM["weight"] = 40
并没有OC中的setObject:forKey: 的写法。
9.2.2删除
//dictM.removeAll()
dictM.removeValue(forKey: "weight")
9.2.3修改
dictM["name"] = "WJ"
如果字典中有了对应的key,则会修改掉原来对应的key的value,如果没有则会添加key/value到字典中
9.2.4取出元素
dictM["age"]
9.3遍历字典
字典中的顺序是无序的,所以不能对下标进行操作,而且也没有下标进行操作。
/// 3.1遍历字典中所有的key
for key in dictM.keys {
print(key)
}
/// 3.2遍历字典中所有的值
for value in dictM.values {
print(value)
}
/// 3.3遍历所有的键值对
for (key, value) in dictM {
print(key)
print(value)
}
9.4合并字典
合并字典并不能用+
进行合并,即使是相同类型的数据的字典,只能采用遍历的方式进行添加数据。
for (key , value) in dict1 {
dictM[key] = value
}
10.元组
元组是swift中独有的,OC没有对应的类型;元组定义的是一组数据类型,组成元组数据类型的可以称之为“元素”。而元组的使用,一般是用做于函数中的返回值。
10.1元组的使用
元组最基本的写法,是有别于字典或数组的中括号,而是用的小括号:
let info = ("name", 18, 1.88)
// 取出元组中的各元素
info.0
info.1
info.2
这样的好处就是不用想数组那样用中括号然后再取下标,元组就直接的用点语法的模式直接取下标拿到对应的值。
10.2给元组中的每个元素去个别名
有的时候,我们希望把元组中的每个元素都对应一个别名,这样有利于编码,元组提供了这种便利,这是最常见的写法;
let info1 = (name : "wj", age : 18, height : 1.88)
info1.name
info1.age
info1.height
需要注意的是,其中的书写格式类似字典的定义,但是这却有别于字典,首先是字典是用中括号进行括起来的,而元组是用小括号进行括起来的;其次就是字典的这个key一般用字符串,当然也可能用其他的数据类型,但是在元组中,这个别名没有特殊的要求,随便怎么写都是可以的,在用到info这个元组的时候直接用点语法就可以将别名显示出来,和最基本的写法的底层是一致的。
10.3元组中的元素就是元组中的元素
let (name, age, height) = ("wj", 18, 1.88)
name
age
height
这样就直接写元组中的元素名就可以指定后面定义的元组中各个元素。
以上在使用到元组中各元素的时候,编译器都会指示出各个元素的数据类型。
11.可选型
可选型可以说也是swift中的特有的,在OC或其他的语言中都没有的,所以比较难以理解。
由于在swift中nil也是特殊的类型,因为和真实的类型不匹配,所以就不能进行赋值,比如说是一个nil类型的不能赋值给String类型的数据。
在swift中对象中任何属性在创建的时候,都必须有明确的初始化值。
class student : NSObject {
/// 类里面的属性,一般定义成var
// var name = "wj"
// 暂时不赋值
var name : String // 这是不允许的,因为必须有初始化值。而且这时编译器会报错。
}
11.1定义可选型
11.1.1常规方法(不常用)
var name : Optional<String> = nil // 可选类型中自能放一个字符串
11.1.2语法糖(常用)
var age : Int? = nil
11.2可选型进行赋值
name = "wj"
age = 18
11.3取出值
print(name)// ->拿到的是可选型
print(age)
以上拿出来的值是可选类型的Optional("wj")
Optional(18)
,往往我们想要的只是可选型中的数据,外面的Optional是不需要的。这时就需要用到强制解包,把里面的数据取出来;
强制解包的格式 : 可选类型 + !
print(name!)
print(age!)
注意的是强制解包是非常危险的,往往在解包的时候,如果可选型为nil的时候,会导致系统崩溃,所以在解包的时候需要注意。
所以建议在解包的时候,需要判断下可选型是否为nil
if name != nil {
print(name!)
}
if age != nil {
print(age!)
}
每次用的时候都要解包,所以比较麻烦,所以我们可以采取下面的做做法。
11.4可选型绑定
写法一:(不常用)
if let tempName = name {
print(tempName)
}
这样写其实是分为了两个步骤:
(1)判断name是否有值,如果没有值就不执行大括号中的内容;
(2)如果name有值的话,就把name进行解包,然后会把name解包后的内容赋值给tempName。但是比较麻烦的是每次创建一个中间值,所以可以采取下面的做法:
写法二:(常用)
if let name = name {
print(name)
}
采用的是就近原则,会使用定义比较近的那个name
以上就是可选型的基本使用,后面的使用会在项目中慢慢锻炼。
11.5可选型的应用场景
通过一个字符串创建一个NSUrl字符串
// 类型不一致,不能进行直接赋值
//let url : NSURL = NSURL(string: "http://www.baidu.com")
let urlStr : NSURL? = NSURL(string: "http://www.baidu.com")
// 根据NSUrl创建一个NSUrlRequset对象
//if urlStr != nil {
// let request = NSURLRequest(URL: urlStr!) // 这种方式已经不能使用了。
//}
if let urlStr = urlStr {
let request = URLRequest(url: urlStr as URL)
print(request)
}
12.函数
在OC中叫方法,在swift中又叫回了函数这个名称
12.1函数的基本定义
12.1.1没有参数,没有返回值的函数
func about() -> Void {
print("iphone 6")
}
about()
func about1() {
print("asdas")
}
about1()
12.1.2没有参数,有返回值的函数
func readMessage() -> String {
return "wj is handsome"
}
readMessage()
12.1.3有参数,没有返回值的函数
func callPhone(_ phonenumber: String) {
print("call phone to \(phonenumber)")
}
callPhone("+86 110")
12.1.4有参数,有返回值的函数
func sum(_ number1: Int, number2: Int) ->Int {
return number1 + number2
}
sum(20, number2: 30)
12.2函数的基本注意点
12.2.1内部参数和外部参数
func sum(_ num1: Int, num2: Int, num3: Int) -> Int {
return num1 + num2 + num3
}
sum(10, num2: 20, num3: 40)
在使用的时候,第一个参数是没有名字的,其他的都是有名字的。
内部参数:内部参数是用于函数内部的,在默认情况下,所有的参数都是内部参数。
外部参数:外部参数是用于函数外部的,在默认情况下,从函数的第二个参数开始,所有的参数即是外部参数也是内部参数。
如果我们要第一个参数在外部使用的时候,也展示一个名字,我们可以把参数中的_
删掉。就默认添加了函数。
12.2.2swift中参数的默认值
在有的情况下,我们需要给参数一个默认值,比方说是在选择饮料的时候,如果有特殊要求就给所要求的饮料,如果没有特殊要求就返给默认的饮料就可以了。
func makeACupOfCoffee(coffeeName: String) -> String {
return "制作了一杯\(coffeeName)咖啡"
}
makeACupOfCoffee(coffeeName: "拿铁")
以上的函数只能处理特殊的给定的情况,并不能处理默认的情况,所以需要给参数一个默认值。
func makeACupCoffee(_ coffeeName: String = "雀巢") -> String {
return "制作了一杯\(coffeeName)咖啡"
}
makeACupCoffee()
给参数一个默认值,然后调用的时候不传参,打印的值就是制作了一杯雀巢咖啡
。
12.2.3可变参数
有的时候,我们不确定在后面传入的参数的个数,我们就希望参数个数是可变的。
func sumAction(num1: Int...) -> Int {
var result = 0
for n in num1 {
result += n
}
return result
}
sumAction(num1: 19,20,44)
在参数类型后面加上三个...
就让Int类型相当于转换成了只包含Int类型的数组,然后在调用函数的时候可以随便加参数的个数。
12.2.4指针类型
由于在swift中,数据交换往往都是指针的交换,但往往有的时候却是值传递的方式,并不能达到两个数值交换,所以就要用到指针类型的参数。
// 定义了两个参数,是可变的。
var m = 30
var n = 20
func swapNmuber(m : inout Int, n : inout Int) {
let temp = m
m = n
n = temp
}
这里用到了inout关键字,代表的就是取到m和n的地址。
在调用的时候,都是取的地址。
swapNmuber(m: &m, n: &n)
// 以上就把m和n进行值的交换了。
12.2.5函数的嵌套
函数的嵌套在实际的开发中是不推荐使用的,由于嵌套的函数逻辑比较混乱所以是不推荐使用的。
func test() {
func demo() {
print("demo")
}
print("test")
demo()
}
test()
打印结果
test
demo
13.类、对象、枚举
13.1类与对象
13.1.1类的创建
在项目中会创建很多个类,这么多个类有的会继承自其他的类,有的本身就是基类。而在类中可以创建于类相关的属性,当然包括了些计算属性或存储属性等,还可以创建一些函数等。
// 类的创建
class wjShape {
// 相当于是这个类的属性
var numberOfSides = 0
func simpleDescription() -> String {
return "a shape with \(numberOfSides) sides"
}
let area = 1
func areaDescription() -> String {
return "a shape area is \(area) squaremeter"
}
// 这个类中没有本个类的初始化方法,
}
以上的这个类是没有初始化方法的。在没有初始化方法的类自己创建一个默认的构造方法,以后会讲到的。
类创建好了,就该调用本个类了。
var shape = wjShape() // 这就是默认的构造方法
shape.numberOfSides = 6 // 调用类中的属性
var shapeDescription = shape.simpleDescription()
print(shapeDescription)
print(shape.areaDescription())
13.1.2带有初始化方法的类的创建
以上的类是没有初始化方法的,但往往有的时候是需要创建的类提供一个简便的构造方法,所以创建一个带有初始化方法的类是有必要的。
// 带有初始化方法的类的创建
class wjNamedShape {
var numberOfSides = 0
var name : String
init(name : String) { // 这是官方文档说的designated initializer
self.name = name
}
func wjDescription() -> String {
return "a shape with \(numberOfSides) sides"
}
}
13.1.3子类集成父类
在创建多数的类中,相当一部分的类基本上有自己的父类,只要在创建的类后面添加: 某个父类
就可以了。
和OC一样,只要继承自父类,父类的属性等都继承下来了,如果需要修改父类的方法的,就只需要在复写父类方法前加上override
关键字。
// 子类继承父类
class wjSquare : wjNamedShape {
var sideLength : Double
init(sideLength : Double, name : String) {
self.sideLength = sideLength // 这里需要注明的是在调用父类的初始化方法之前,所有的属性必要赋值或者说是有一个初始值。
super.init(name: name)
numberOfSides = 4
}
func wjArea() -> Double {
return sideLength * sideLength
}
// 重写父类的方法必须要加上override关键字
override func wjDescription() -> String {
return "a square with side length of \(sideLength), and area is \(wjArea()) squaremeter"
}
}
调用
let square = wjSquare(sideLength: 4.0, name: "square")
square.wjArea()
square.wjDescription()
13.1.4创建setter和getter方法
在OC中创建一个属性的setter方法和getter如下的实例:
- (xxx)xxInstance {
return xxxInstance;
}
- (void)setXxxInstance:(xxxx) {
_xxx = xxx;
...
}
但是在swift中就不用这样写,
// 属性的setter和getter方法
var perimeter : Double {
get {
return 3.0 * sideLength
}
// set {
// sideLength = newValue / 3.0 // newValue是默认指定的一个值,不需要声明
// }
set(newSideValue) {
// 也可以声明一个明确的名字,声明的名字放在set后面的小括号中
sideLength = newSideValue / 3.0
}
}
13.2枚举
枚举在swift中的存在就OC使用的更加方便了,他的创建和创建类都是差不多的,基本一样的创建方法,关键字enum
。
enum xxx : type {
case xx
case yy
...
}
以上就是基本的创建的例子,创建的枚举中的值默认是从0开始计数的,但是也可以自己指认一个初始值:
enum pocker : Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func wjPockerDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
以上的例子就是自己制定从1开始的,其中每个case都绑定了一个rawValue,也就是每个case所对应的index,通过这个rawValue就可以取到每个case对应的值。
14.构造方法的创建
在OC中的构建方法一般都是有返回值的,但是在swift中的创建方法是没有返回值的,在swift中创建构造方法也就是为了保证新创建的实例在使用之前能够正确的完成初始化。
14.1构造器(initializers)
在swift中创建新的实例会使用到关键字init
。
init() {
// do somethings
}
上面的这个例子中的构造方法是不带参数的。
“struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit”
在结构体中创建了一个不带参数的构造器,而且将结构体中的存储属性temperature进行了初始化。其实上面的例子中,也可以用默认属性值,即temperature = 32.0
。
13.2自定义的构造器
在OC中创建自定义的构造方法一般如下:
-/+(instancetype)xxxWithxxx:(xxx)xxx sss:(sss)sss {
...
return xxxx
}
在swift中的创建方法如下:
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
上面的方法有需要说明的是,
- 这个构造方法含有一个参数,但是有两个名字,一个是外部参数名
fromFahrenheit
,一个是内部参数名fahrenheit
,如果在外部参数不要展示的话可以用_
进行替代。 - 在构造方法是将构造方法中的参数进行处理,然后赋值给存储属性,然后并保存。
- 如果在构造方法中需要传入多个参数的话就在一个参数后面接着写就是了,只是用逗号分隔开来,和创建函数的方式是一致的。
13.3可选型的属性的处理
在创建属性的时候,往往会遇到一些属性可能有值,可能没有值的情况,这种情况下,就可以将属性作为可选型。上面说到了,创建构造方法的任务就是为了创建新的实例的时候,能保证正确的进行初始化,所以在构造方法中,对于存储属性一般需要进行初始化。然而对于可选型的属性的处理,往往会把可选型的属性赋值为nil,所以在构造方法中不用对可选型进行处理。
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
} }
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?") cheeseQuestion.ask()
// 输出 "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
13.4默认构造器
在swift中的类或者结构体中如果所有的属性都有默认值,而且也没有自定义的构造方法,那么swift会默认的创建一个构造方法。这个默认的构造方法所做的事情也就是把这个类或者是结构体中的属性都设置一个默认的值。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
13.4类的继承的构造方法
在swift中有指定构造器(Designated Initializers)和遍历构造器(Convenience Initializers),其中指定构造器是类中最主要的,一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用 同一个类中的指定构造器,并为其参数提供默认值。
创建指定构造器:
init(parameters) {
statements
}
便利构造器,加上convenience
关键字
convenience init(parameters) {
statements
}
Swift 采用以下三条规则来限制构造器之间的代理调用
- 规则 1 指定构造器必须调用其直接父类的的指定构造器。
- 规则 2 便利构造器必须调用同一类中定义的其它构造器。
- 规则 3 便利构造器必须最终导致一个指定构造器被调用。
也就是:
• 指定构造器必须总是向上代理
• 便利构造器必须总是横向代理
图片来说明:
13.5构造器的继承
其中子类继承了父类,对于子类如果要使用父类的初始化的方法,一般会加上override的关键字。但是如果父类有个便利构造器,但是子类不能直接使用父类所创建的便利构造器,所以在使用的时候也就不必加上override的关键字。其原理从上图也可以看出。
如果在父类写了指定构造器,如果在子类又有新的属性,如果这时候要重写的话,需要在新的属性进行初始化后才能调用super.init()的方法,
也就是:
规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
} }
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name) // 父类的指定的构造方法
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
} }