原文地址:https://www.jianshu.com/p/06b9ddf748f7 (如有侵权,请联系我删除。)
小视频
001--swift简史小视频
002--Playground体验
003--常量&变量
一、swift简史
1、介绍
swift是苹果公司于2014年推出用于撰写OS和iOS应用程序的语言。它由苹果开发者工具部门总监“克里斯.拉特纳”在2010年开始着手设计,历时一年完成基本的架构。到后来苹果公司大力投入swift语言的研发,于2014年发布这一语言的第一版本。swift2.0之后的语法则趋于稳定,2017年发布的swift4.0虽有改动,但也只是增添了一些新特性。这些新特性需要在Xcode9上运行才能显示出效果。值得一提的是它支持unicode9,也就是说,可以用某些图片图标来充当变量。
例如:
2、特点
3、为什么要学习swift
-
swift作为面向协议语言,不仅能写移动端,也可以做到搭建服务器端。
-
纵观国内外iOS开发界,已经有许多公司直接或间接采用swift开发,使用swift语言开发已成为未来iOS开发的趋势。
-
swift以简洁、优雅等优点迅速俘获广大开发者的青睐。
二、用playground体验swift开发
打开Xcode,选择创建一个playground项目
创建一个普通的UIView对象
正如上图所示,playgound文件的左边是代码区,右边则是显示结果的区域。当点击用于眼睛时会实时显示出界面效果。
swift与objective-C的重大区别
-
在swift中是没有.h和.m文件之分的。所有的代码全部都存储在一个文件里面。
-
在swift中所有的代码都被封装在{}里面
-
OC使用alloc init进行初始化,而swift使用()
-
OC中使用[]来调用方法,而swift中采用点语法。比如UIColor.red
-
swift中不需要用分号分割语句
三、常量和变量
1、数据类型
在swift中也有各种数据类型来存储不同的信息。下表列举的是常见的数据类型变量。
但其实,在swift中,是不存在基本的数据类型的,所谓的数据类型,其实都只是结构体。这也是swift中的一个特点。
2、变量和常量
声明
swift中用let声明常量,用var声明变量。
1
2
3
4
|
var x = 10 ;
let y = 20 ;
let z //错误示范,let z 在声明的时候并没有赋值常量是不可改变的,只能在声明时赋值
|
在开发中,通常会优先选择使用let,因为不可变会更安全一点。所以建议在写代码之时,先选择let,等到需要变化的时候再改成var。
自动推导
创建一个UIView,不指定类型。可以看到控制台上会打印出UIView的信息。这个现象被称为swift的自动推导。事实上,在代码左侧定义的类型只是程序员希望的类型,而右侧才是程序真实的类型。
1
2
|
let z = UIView()
print(z)
|
也就是说,变量或常量的类型会根据右侧代码执行的结果,推导出对应的类型。
可以使用热键option点击查看类型。
swift对类型的严格要求
在swift中,任何不同类型的数据之间是不允许直接运算的。比如下面这段代码就会报错。
1
2
3
4
|
//错误示范
let a = 10
let b = 12.5
print(x + y)
|
如果非要让不同类型数据之间能够运算,可以将其中一个类型进行转换。
1
2
3
|
let a = 10
let b = 12.5
print(a + Int(b))
|
此时得到的结果就是22。在swift中,做类型转换时是将数据括起来,相当于swift结构体中的构造函数。
当然也可以将前面的整数转换成Double型。此时就能打印出小数来。
四、String类型和Bool类型
1、String类型
声明
直接用双引号将数据引起来
1
2
|
let str = "小仙女"
let str1: String = "hahh"
|
拼接
字符串的连接有两种方法,一种是通过加号来连接,另一种则是通过反斜杆进行插入。
1
2
3
4
|
let str = "小仙女"
let mesg1 = "one" +str //用加号的方式
let mesg2 = "two,\(str)" //反斜杠的方式
print(mesg1,mesg2)
|
在做字符串拼接时要注意加号和反斜杠后面都不能出现空格,不然会报错。
拼接字符串时格式的变化
假设在某些特定的地方需要输出特定位数的字符,比如或时间的输出,就需要使用占位符来调整字符串的格式。使用String的构造函数,调用format方法,%0后面加上数字就表示需要占多少位数。
1
2
3
4
|
let min = 2
let second = 10
String (format: "d:d" , min,second)
|
遍历
调用字符串的characters属性,采用for...in...的方式来遍历字符串。
1
2
3
4
5
6
7
8
9
|
for c in str{
print(c) //swift4中的遍历
}
print(str.count) //打印字符串长度
for char in myString.characters {
print(char) // swift3的遍历
}
print(str..characters.count) //swift3打印字符串长度
|
字符串的截取
最方便的方式就是将String类型转换成OC的NSString类型,再来截取。
1
2
3
4
5
6
|
let urlStr = "www.baidu.com"
let header = (urlStr as NSString).substring(to: 3 ) //截取前三位
let middle = (urlStr as NSString).substring( with : NSMakeRange( 4 , 5 )) //去除前四个字符截取,范围之后五位字符
let footer = (urlStr as NSString).substring(from: 10 ) //从第十个字符开始截取
|
2、Bool类型
与其他语言一样,Bool类型表示的就是真假,但是不同于Objective-C,swift中用true和false来表示真假。
五、可选类型
在Objective-C开发中,如果一个变量暂时不会使用到,可以将它赋值为0或者赋值为空,而在swift中,nil是一个特殊的类型,如果它和真实类型不匹配是不能进行赋值的。但是开发中将变量赋值为空是在所难免的事情,因此就推出了可选类型。
可选类型是swift的一大特色,在定义变量时,如果指定这个变量是可选的话,就是说这个变量可以有一个指定类型的值或者为nil。
1、定义一个optional的变量
1
2
|
let x:Optional = 10
print(x)
|
点击进去查看,可以发现Option其实是一个枚举类型。这个枚举有两个值,一个是none,表示没有值,而另一个是some,表示某一类值。
在输出的时候,可以看见控制台上的内容Optional(10),它的作用就是提示这是一个可选值。
而在实际开发中,一般不用上述方式创建可选值,而是指定一个类型,再在其后添一个问号。
1
2
3
4
|
let x:Optional = 10 //第一种写法
let x:Int? = 20 //第二种写法
print(x)
|
上述代码问号的意思就是定义一个可选的Int类型,可能没有值,也可能有一个整数。
2、 解包
试试将上面案例x和y相加,这个时候还能输出结果么?
此时可以看到编译器已经报错。在前面的教程中提到过,不同类型的值是不能直接运算的。而可选项有两种值的产生,若它的值为nil则不能参加计算。
因此引入解包的概念,“!”代表强制解包。它的意思是从可选值中强行获取对应的非空值。
3、解包常见错误
1
2
3
|
//错误示范1
let y : Int?
print(y)
|
使用let定义的是常量,在初始化时必须要给出值。
1
2
3
|
//错误示范2:
let y : Int? = nil
print(y)
|
强制解包是危险操作,如果可选值为nil,强制解包系统会奔溃。
4、let和var的可选项默认值
1
2
3
4
5
|
//默认值测试
let x: Int?
print(x)
var y :Int?
print(y)
|
用let做测试时会直接报错,说明let的可选值是没有默认值的,而用var做测试时,报错信息就变成了警告,运行的结果为nil。可以由此推测出var的可选项默认值为nil。
swift中有规定,对象中的任何属性在创建对象时,都必须有明确的初始化值。
5、可选绑定
用if let/var表示。它将变量赋值给一个临时变量,在这个操作中会做两步操作:首先判断变量是否有值,如果没有值,则直接不执行大括号里面的内容;如果有值,系统会自动将变量进行解包,并且将解包后的结果,赋值给临时变量。
比如下面这个例子:
通过一个字符串创建NSURL对象
接着创建NSURLRequest对象。强制解包非常危险,当url有中文的时候可能会变成nil。所以要判断url是否为空再对其进行解包。
1
2
3
|
if let url = url {
let request = URLRequest(url: url)
}
|
六、swift中的分支
1、if语句
在swift中,if语句是不用带小括号的,但是后面跟的语句必须有花括号,哪怕只有一行代码。许多公司的代码规范也是规定必须使用这一格式。
注意:在swift中没有非0即真的说法,所以不能写成if(num)这样的格式。
1
2
3
4
5
6
|
let x = 9
if x > 5 {
print( "小仙女" )
} else {
print( "妖精哪里跑" )
}
|
2、三目运算符
三目运算符的写法是表达式后跟一个问号,用冒号来隔开条件是否成立的值。
1
2
|
let x = 10
x > 5 ? print( "小仙女" ):print( "妖精" )
|
非常有意思的是,如果开发者只想处理条件成立的部分,此时可以在冒号后面用一个小括号来代替条件不成立的部分。
1
|
x > 5 ? print( "你都写了我两次啦" ):()
|
3、 三目运算符的简单模式
三目运算符的简单模式通常是用于处理可选项的。“??”的意思是说,如果表达式有值,就使用那个值,如果没有,就使用“??”后面的值来代替。
1
2
3
|
let x:Int? = nil
let y:Int? = 9
print((x ?? 0 ) + (y ?? 0 ))
|
运行之后的结果为9。
之后再来说说运算符的优先级。举个简单的栗子
1
2
3
|
let name: String ? = "安琪拉"
print((name ?? "" ) + "火烧屁屁咯" )
print(name ?? "" + "火烧屁屁咯" )
|
从运行的结果可以看到,“??”的优先级是最低的。如果没有小括号的约束,它会将后面的语句都当成是一个表达式。
4、 guard的用法
分支若是写得过多,就会导致代码可读性较差的问题。为了降低代码的层次,swift推出了guard。guard后面跟判断表达式,else后面写表达式不成立的代码。
需要注意的是guard必须写在函数内部,在最末尾出必须要跟关键字return/continue/break/throw中的一种。
1
2
3
4
5
6
7
8
9
|
import UIKit
let age = 20
func online(age : Int){
guard age >= 18 else {
print( "还未成年呢" )
return
}
print( "一起来开黑吖" )
}
|
这样或许看不到guard的特别之处,但若是像下面这样的代码出现呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
let age = 20
let money = true
let idcard = true
func online2(age : Int,money:Bool,idcard:Bool){
if age >= 18 {
if money {
if idcard {
print( "一起来开黑吖" )
} else {
print( "回去带身份证吧" )
}
} else {
print( "回去拿钱" )
}
} else {
print( "还未成年呢" )
}
}
//调用
online2(age: age, money: money, idcard: idcard)
|
如果用普通的分支方法,就会显得可读性太差。我们可以试着将它改成guard的写法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func online1(age : Int){
//判断年龄
guard age >= 18 else {
print( "还未成年呢" )
return
}
//判断是否有钱
guard money else {
print( "回去拿钱" )
return
}
//判断是否带了身份证
guard idcard else {
print( "回去带身份证吧" )
return
}
print( "一起来开黑吖" )
}
|
执行完所有的判断语句之后才执行代码库,阅读性也比if……else分支强。
5、 switch
最基本的用法
switch后面的小括号可以省略。用case关键字来表示不同的情形,case语句结束后,break也可以省略。
1
2
3
4
5
6
7
8
9
|
let sex = 0
switch sex {
case 0 :
print( "男" )
case 1 :
print( "女" )
default :
print( "其他" )
}
|
基础语法的补充
如果系统某一个case中产生case穿透,可以在case结束后跟上fallthrough
1
2
3
|
case 0 :
print( "男" )
fallthrough
|
case后面可以判断多个条件,这些条件以逗号分开
1
2
3
4
5
6
7
8
|
let sex = 0
switch sex {
case 0 , 1 :
print( "正常人" )
default :
print( "其他" )
}
|
switch可以判断浮点型、字符串类型和Bool类型
1
2
3
4
5
6
7
|
switch 3.14 {
case 0 :
print( "正常人" )
default :
print( "其他" )
}
|
1
2
3
4
5
6
7
8
9
|
let opration = "+"
switch opration {
case "+" :
print( "加法" )
case "-" :
print( "减法" )
default :
print( "其他" )
}
|
七、swift的for循环和表示区间
1、变化
在swift3开始,就已经弃用了var i = 0; i < 10; i++的这种写法。并且++这种写法也被取消掉了,改为+=代替。
2、表示区间
swift常见区间有两种,开区间用..<表示,闭区间用...表示。要注意的是数字和省略号之间是不能加空格的。
1
2
3
4
5
6
7
8
9
10
11
|
func demo1() {
for i in 0 ..< 5 {
print(i)
}
print( "^^^^^^^" )
for i in 0 ... 5 {
print(i)
}
}
demo1()
|
3、逆序操作
如果想要做逆序操作,只要在in后面的表达式后添加reversed()即可。
1
2
3
4
5
6
|
func demo1() {
for i in ( 0 ..< 5 ).reversed() {
print(i)
}
}
demo1()
|
八、swift中的数组
Swift语言提供了Arrays、Sets和Dictionaries三种基本的集合类型用来存储集合数据。数组是有序数据的集,集合是无序无重复数据的集,而字典则是无序的键值对的集。
数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
1、定义数组
用let定义出来的数组就是不可变的
1
2
|
//定义不可变数组
let array = [ "爱丽丝" , "小红帽" , "白雪公主" ]
|
使用var来定义可变数组。正确的写法是Array这样的形式。其中Element是这个数组中唯一允许存在的数据类型。但是为了简便,推荐使用[Element]()的写法。
1
2
3
4
|
//定义可变数组
var arrayM = [ String ]()
var arrayM1:[ String ]
var arrayM2 = Array ()
|
2、创建带有默认值的数组
swift中的array类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。开发者可以在里面指定它的数量和类型。
1
2
|
var threeDouble = Array (repeating: 0.0 , count: 3 )
print(threeDouble[ 1 ])
|
3、对可变数组的基本操作
使用append给数组添加元素
1
2
3
4
5
|
arrayM.append( "1" )
arrayM.append( "2" )
arrayM.append( "3" )
arrayM.append( "4" )
arrayM.append( "5" )
|
使用insert方法将值添加到具体索引值之前
1
|
arrayM.insert( "10" , at: 2 )
|
使用remove系列方法可以对数组做删除操作
1
2
3
4
|
arrayM.remove(at: 0 )
arrayM.removeSubrange( 1 ..< 3 )
arrayM.removeAll()
arrayM.removeLast() //可以去除最后一项,避免捕获数组count属性
|
通过取下标的方式对数组进行修改和查找
1
2
|
arrayM[ 0 ] = "小红帽"
print(arrayM[ 2 ])
|
利用区间对具体范围内的值替换
1
2
3
|
//替换第2项和第3项的值
arrayM[ 2 ... 4 ] = [ "22" , "33" ]
print(arrayM[ 3 ])
|
4、数组的遍历
若同时需要每个数据项的值和索引,可以使用数组的emumerated()方法来进行数组遍历。
1
2
3
|
for (index,value) in arrayM.enumerated(){
print( String (index+ 1 )+ ":" +value)
}
|
5、数组的合并
只有相同类型的数组才能进行合并。
1
|
let resultArray = arrayM + array
|
九、swift中的集合
集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
集合中的元素必须有确定的hashvalue,或者是实现了hashable协议。而swift提供的Int,String等类型其实都是实现了hashable协议的。hashable是equable的子协议,如果要判断两个元素是否相等,就要看他们的hashvalue是否相等。
1、定义集合
使用set定义。
Element表示集合中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
1
2
3
4
|
//创建空集合
var letters = Set()
//使用字面量创建集合
var favorite:Set = [ "绮罗生" , "意琦行" ]
|
要注意的是一个Set类型是不能直接后面跟的字面量被单独推断出来的,因此这个Set是必须要显示声明的。但是由于swift的自动推断功能,可以不用写出Set的具体类型。比如说上面那个例子,省去String,也能推断出Set的正确类型。
1
|
var favorite:Set = [ "绮罗生" , "意琦行" ]
|
2、访问和修改集合
通过.count属性知道集合的长度,通过isEmpty判断集合是否为空。
3、添加元素
1
2
|
favorite.insert( "寒烟翠" )
print(favorite.count)
|
4、删除元素
通过remove的方法删除元素,若这个值真的存在就会删除改值,并且返回被删除的元素。若集合中不包含这个值,就会返回nil。
1
2
3
4
5
|
if let removeBack = favorite.remove( "意琦行" ){
print(removeBack)
} else {
print( "没有找到值" )
}
|
5、集合操作
swift提供了许多数学方法来操作集合。
1
2
3
4
|
print(oddD.union(evenD).sorted()) //并集
print(oddD.intersection(evenD).sorted()) //交集
print(oddD.subtracting(siggleDPrime).sorted()) //取差值
print(oddD.symmetricDifference(siggleDPrime).sorted()) //去掉相同值
|
6、遍历集合
1
2
3
4
5
6
7
|
for item in favorite {
print(item)
}
//按照首字母的顺序输出
for item1 in favorite.sorted() {
print(item1)
}
|
7、集合的成员关系
用 ==来判断两个集合是否包含全部相同的值
用 isSubset(of:)来判断一个集合中的值是否也被包含在另外一个集合中
用 isSuperset(of:)来判断一个集合中包含另一个集合所有的值
用isStrictSubset(of:)或者isStrictSuperset(of:)方法来判断一个集合是否是另外一个集合的子集合或父集合并且两个集合不相等
十、字典
字典是一种存储多个相同类型的值的容器。每个值value都关联这唯一的键key。键就是这个字典的标识符。而且字典中的数据项并没有具体顺序。键集合不能有重复元素,而值集合是可以重复的。
1、定义字典
使用let定义不可变的字典,使用var定义可变字典。用字面量赋值时,系统会自动判断[]中存放的是键值对还是要一个个的元素。
1
2
3
4
5
6
|
let dict = [ 1 : "one" , 2 : "two" , 3 : "three" ] //定义不可变字典
var dictM = Dictionary() //定义可变字典
var dictM1 = [ String :NSObject]()
//AnyObject一般用于指定类型,NSObject一般用于创建对象
|
2、对可变字典做基本操作
添加、删除和获取元素
1
2
3
4
5
|
dictM1[ "name" ] = "小仙女" as NSObject
dictM[ "age" ] = 17 as NSObject
dictM.removeValue(forKey: "name" )
//获取:swift中只保留了最简单的写法,OC中有objectforkey的方法在swift中也被删除掉了。
dictM[ "name" ]
|
3、修改元素
若字典中已经有对应的key,操作的结果是直接修改原来的key中保存的value。若字典中没有对应的key,则会添加新的键值对。
4、遍历字典
可以通过范围for遍历所有的key和value。也可以遍历所有的键值对。
1
2
3
4
|
for (key,value) in dictM {
print(key)
print(value)
}
|
5、合并字典
合并字典时通过遍历的方式将第二个字典的内容添加到第一个字典中。绝对不能用相加的方式对字典进行合并。
1
2
3
4
5
6
7
|
var dict1 = [ "name" : "llx" , "age" : "17" ]
var dict2 = [ "num" : "007" ]
for (key,value) in dict2 {
dict1[key] = value
}
dict
|
十一、元组
元组是swift中特有的一种数据结构,用于定义一组数据,元组在数学中的应用十分广泛。
1、定义元组
使用()包含信息,组成元组类型的数据可以被称为“元素”。
1
2
|
//使用元组来描述个人信息
let info1 = ( "1001" , "张三" , 30 )
|
2、起别名
可以给元素加上名称,之后可以通过元素名称访问元素
1
2
3
|
//给元素加上名称,之后可以通过元素名称访问元素
let info2 = (id: "1001" ,name: "张三" ,age: 30 )
info2.name
|
元组一般用于作为方法的返回值。元组中元素的别名,就是元组的名称
1
2
|
let (name,age) = ( "张三" , 18 )
name
|
十二、函数
函数相当于Objective-C中的方法,是一段完成特定任务的独立代码片段。可以通过给函数命名来标志某个函数的功能。而这个名字可以用来在需要的时候“调用”该函数完成其任务。格式如下:
1
2
3
4
|
func 函数名(参数列表)-> 返回值类型 {
代码块
return 返回值
}
|
func表示关键字,多个参数列表之间用逗号隔开,也可以没有参数。使用->指向返回值类型。如果没有返回值,可以用Void代替,也可以省略。
1、定义无参无返回的函数
1
2
3
4
|
func phone()->Void {
print( "小米" )
}
phone()
|
2、定义有参无返回的函数
1
2
3
4
|
func phoneNum() -> String {
return "123456"
}
print(phoneNum())
|
3、定义有参无返回的函数
1
2
3
4
|
func callPhone(phoneNum: String ){
print( "打电话给\(phoneNum)" )
}
callPhone(phoneNum: "123456" )
|
4、定义有参有返回的函数
1
2
3
4
|
func sum(num1 : Int,num2 : Int) -> Int{
return num1 + num2
}
sum(num1: 30 , num2: 30 )
|
在swift4之后,调用函数的时候,能直观的看到参数。而在之前调用之时,只能看见第二个参数之后的名称,表达起来并不直观。如何解决这个问题呢?
可以采用给参数起别名的方式,在参数前面添加一个别名。
1
2
3
4
|
func sum(number1 num1: Int,number2 num2 : Int) -> Int{
return num1 + num2
}
sum(number1: 2 , number2: 4 )
|
5、默认参数
在swift中可以给方法的参数设置默认值。比如说买甜筒的时候,商店默认会给顾客准备原味冰淇淋。但是用户也可以选择指定口味。
1
2
3
4
5
|
func makeIceCream(flavor: String = "原味" ) -> String {
return "制作一个\(flavor)冰淇淋"
}
makeIceCream()
makeIceCream(flavor: "抹茶" )
|
6、可变参数
有些时候,在创建方法的时候,并不确定参数的个数,于是swift推出了可变参数。参数的类型之后使用...表示多个参数。
1
2
3
4
5
6
7
8
|
func sum(num:Int...) -> Int {
var result = 0
for i in num {
result += i
}
return result
}
sum(num: 18 , 29 , 3 )
|
7、引用传递
如果现在有这样一个需求:要交换两个数的值,不能使用系统提供的方法。要如何来完成呢?
如果按照上面的写法就会报错,可以按住option键查看,参数默认是不可变的。
而且就算可行,做到的也是值传递。为了解决这一问题,swift提供了关键字inout来声明数据地址传递,也被称之为引用传值。在swift3.0的时候,inout的位置发生了改变,被放置在标签位置。但是作用与之前相同。
1
2
3
4
5
6
7
|
func swapNum1( m : inout Int, n : inout Int) {
let tempNum = m
m = n
n = tempNum
}
swapNum1(m: &m, n: &n)
print( "m:\(m),n:\(n)" )
|
十三、类
swift用关键字class来定义类。通常情况下,定义类时,让它继承自NSObject,若没有指定父类,那么该类就是rootClass。类的格式如下:
1
2
3
|
class 类名:SuperClass {
//定义属性和方法
}
|
1、定义存储属性和创建类对象
对象的属性必须要赋值,用解包的方式赋值为nil。
1
2
3
4
5
6
|
class Person : NSObject {
//定义存储属性
var age : Int = 0
var name : String ? //对象的属性必须赋值,不赋值会报错的哦
}
let p = Person()
|
2、给类的属性赋值
可以直接赋值,也可以通过KVC进行赋值
1
2
3
4
5
|
p.age = 10
p.name = "llx"
if let name = p.name {
print(name)
}
|
3、定义方法
在swift中,如果使用当前某一对象的属性或者方法,可以直接使用,不需要加self
1
2
3
4
|
// 定义方法,返回平均成绩
func getAverage() -> Double {
return (mathScore + EnglishScore)* 0.5
}
|
1
|
let average = p.getAverage()
|
4、定义计算属性
通过别的方式计算到结果的属性,称之为计算属性。
1
2
3
|
var averageS : Double {
return (mathScore + EnglishScore) * 0.5
}
|
5、定义类属性
类属性是和整个类相关的属性,用static修饰,作用域是整个类。通过类名进行访问。
1
|
static var courseCount : Int = 0
|
在类外通过类名访问类属性
6、类的构造函数
构造函数类似于OC中的init方法。默认情况下创建一个类时,必定会调用一个构造函数。如果一个类继承自NSObjct,可以对父类的构造函数进行重写。
在构造函数中,如果没有明确super.init()。那么系统会默认调用super.init()。
1
2
3
4
5
6
7
8
9
|
class Person : NSObject {
var name : String ?
var age : Int = 0
override init() {
print( "hello world" )
}
}
let p = Person()
|
7、自定义构造函数
自定义构造函数可以传入参数,做赋值操作时采用self调用属性以示区分。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Person : NSObject {
var name : String ?
var age : Int = 0
// 自定义构造函数
init(name: String ,age:Int){
self.name = name
self.age = age
}
}
// 调用自定义的构造函数
let p1 = Person(name: "kaka" , age: 12 )
print(p1.age)
|
可以定义字典类型的构造函数。用KVC的方式将字典的值取出来,要调用系统的setValue方法就必须先调用系统的构造函数创建出对象。为了防止取出的对象没有属性而导致程序奔溃,需要重写系统的setValue方法。
如果用KVC的方式一定要先调用父类的构造函数。因为系统默认调用是放在方法最后面调用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Person : NSObject {
@objc var name : String ?
@objc var age : Int = 0
init(dict:[ String : Any]) {
super .init()
// 要调用系统的`setValue`方法就必须先调用系统的构造函数创建出对象
setValuesForKeys(dict)
}
// 防止奔溃
override func setValue(_ value: Any?, forUndefinedKey key: String ) {
}
}
let p2 = Person(dict:[ "name" : "lala" , "age" : 18 , "score" : 33 ])
p2.name
p2.age
|
由于swift与objective-c的编译方式不同,用KVC字典转模型构造函数时,需要在属性前面加上@objc。
8、类的属性监听器
在object-c中,我们可以重写set方法来监听属性的改变,而在swift中也可以通过属性观察者来监听和响应属性值的变化。通常用于监听存储属性和类属性的改变。对于计算属性则不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化。
可以通过设置以下观察方法并响应这种值的变化。
willSet:在属性值被存储之前设置,此时新属性值作为一个常量参数被传入。该参数名默认为newValue,开发者可以自己定义该参数名。
didSet:在新属性值被存储后立即调用,与willSet不同的是,此时传入的是属性的旧值,默认参数名为oldValue。
上面两个方法都只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Person : NSObject {
//属性监听器
var name: String ? {
willSet {
print(name as Any)
//如果想要查看接下来的新值,可以使用newValue
print(newValue as Any)
}
didSet {
print(name as Any)
}
}
}
let p = Person()
p.name = "llx"
|
十四、闭包
闭包是swift中非常重要的一个知识点。类似于objective-c中的block,其实函数就相当于一个特殊的闭包。闭包需要提前写好,在适当的时候再执行。
1、定义闭包
闭包的格式是(参数列表)->(返回值类型) in 实现代码
举一个最简单的栗子
用常量记录一个代码块,按住option键就能看到,b1是一个闭包。再到适合的地方去调用它。
1
2
3
4
|
let b1 = {
print( "干掉他们" )
}
b1()
|
再来看一个带参数的闭包。在闭包中,参数、返回值和实现代码都是写在花括号里面的。in是用来定义分割和实现的。
1
2
3
4
5
|
let b2 = {
(x: String )->() in print(x)
}
b2( "string" )
|
2、闭包案例
这个案例要模拟封装一个网络请求的类。利用闭包将jsonData类型的数据传递给展示页面。
创建一个新的项目,选择swift语言
封装一个网络请求的类HttpTool.swift继承自NSObject
用异步线程模拟网络数据请求,再回到主线程中回调闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class HttpTool: NSObject {
//闭包类型:(参数列表)->(返回值类型)
func loadData(callback:@escaping(_ jsonData : String )->()) {
DispatchQueue.global().async {
print( "发生网络请求:\(Thread.current)" )
}
DispatchQueue.main.async {
()->Void in
print( "获取到数据,并且回调:\(Thread.current)" )
callback( "jsonData数据" )
}
}
}
|
到需要接收数据的界面定义Httptool类的属性,设置一个初始化值,将初始值赋值给变量
在swift中是不需要引入头文件的,文件之间可共享
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import UIKit
class ViewController: UIViewController {
var tools : HttpTool = HttpTool()
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
//用闭包将json数据拿到
tools.loadData { (jsonData) ->() in
print( "在viewcontroller中拿到数据\(jsonData)" )
}
}
}
|
3、尾随闭包
尾随闭包用于需要将一个很长的闭包表达式作为最后一个参数传递给函数。也就是说如果按时的最后一个参数是闭包,那么在调用它的时候就可以把这个闭包写在括号外面,并紧跟括号,函数的其他参数则仍然写在括号之中。
1
2
3
4
5
6
7
8
9
10
11
12
|
//这个函数接受一个String和一个闭包
//函数体内调用闭包,并且将String作为参数传递给闭包
func myFunc(strP: String ,closeP:( String )->Void) {
closeP(strP)
}
//普通调用
myFunc(strP: "hello" , closeP: {(string) in print(string)})
//尾随闭包
myFunc(strP: "hello" ) {
(string) in print(string)
}
|
4、逃逸闭包
当一个闭 |
请发表评论