Swift初见
一些Swift
的学习记录
类型 Type 隐式转换
Swift
不允许类型的隐式转换,如果你需要修改一个变量的类型,请显示转换。
1 2 3 let label = "The width is " let width = 94 let widthLabel = label + String(width)
假如将第三句的String
类型去掉,则会报错:
1 2 3 let label = "The width is " let width = 94 let widthLabel = label + width
类型定义 Type
Swift
定义的类型都是以大写开头的,这是Swift
的一个习惯,所以在自定义类型的时候,需要把第一个字母大写
字符串 String
在OC
中。NSString
是继承于NSObject
,是一个对象,而在Swift
中String
是一个结构体,所以它的性能要比NSString
更好。
字符串遍历
1 2 3 4 5 let hello : String = "Hello World!" for letter in hello.characters{ print (letter) }
字符串常见用法 1 2 3 4 5 6 7 8 9 hello.substring(to: hello.characters.count - 1 ) hello.substring(from: 0 ) let start = hello.index(hello.startIndex, offsetBy: 2 )let end = hello.index(hello.endIndex, offsetBy: - (length - 6 ))var subStr = hello.substring(with: start..< end)var hasSuf = hello.hasSuffix("World!" )var hasPre = hello.hasPrefix("Hello" )
条件判断 Condition
在if
判断条件中,可以省略条件的”()”,
判断条件只能为Bool
值
1 2 3 4 5 if num == 1 { print (num) }else { ... }
控制流 Flow
使用switch
语句,可以添加falltrough
直接跳转到下个case的代码;
case
判断可以使用字符串;
case
如果没有以break
结束,在swift
中也不会穿透;
可以在case
中定义变量,而无需添加{}
作用域;
default
语句放最后,大部分情况不能省略。
元组 Tuple
元组可以组合不同的数据类型,比如String
、 Int
、 CGFloat
等
元组的数据是有顺序的,从0开始
元组的数据可以使用命名,类似字典
使用二维元组遍历字典时候,元组第一个元素代表字典的key
,第二个元素代表字典的value
。1 2 3 4 5 6 7 8 9 let infoTuple = (name: "FFur", age: "18", height: 1.88) print(infoTuple.name); print(infoTuple.age); print(infoTuple.height); //另一种赋值 let (name, age, height) = ("FFur", "18", 1.88) //遍历 for (k, v) in dict{ }
可选类型Optionals
可选类型是Swift
对值缺失问题的解决方案。可选类型的变量允许指向值或nil
针对变量的安全性: Optionals
类型
顾名思义,只有可选类型才能赋值nil
Optionals
将变量的默认值设置为nil
例子:
1 2 3 4 5 var result: Int ? = 30 print (result)var name : String? = nil name = Optional ("FFur" )
Optionals
解绑
Use ! - NOT RECOMMENDED, IF NIL VALUE YOUR APP WILL CRASH
,强制解包,非常危险。
Use an "If Let"
,使用可选绑定
Use a "Guard Statement" - Typically used in methods/functions
if let
语法
可选绑定: 去掉了可选类型,如果可选类型中包含值,该值便会赋值给解绑的变量,然后执行if
代码块,在里面你可以安全的使用已经解绑的变量; 如果不包含值,则执行else
代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if let tempName = varName{ print (tempName) } if let varName = varName{ print (varName) } if let tempName = dict[“key”] as? String { print (tempName) } if let authorName = authorName, let authorAge = authorAge { print ("The author is \(authorName) who is \(authorAge) years old." ) } else { print ("No author or no age." ) }
guard
语法
有时你只想检查的一些条件,只当条件为真时执行对应的逻辑, 则guard
提供了很好的解决方式
1 2 3 4 5 6 7 8 9 10 11 12 guard 判断条件 else { return } guard let varName = varName else { return } guard age >= 18 else { print ("未成年" ) }
简便操作符
在 Swift
中,有一个非常有用的操作符,可以用来快速地对 nil
进行条件判断,那就是 ??
。这个操作符可以判断输入并在当左侧的值是非 nil
的 Optional
值时返回其 value
,当左侧是 nil
时返回右侧的值,比如: 一种简便解包的写法,使用“??
”
1 2 3 let num1 = 2 let num = num1 ?? 0 print (num)
可选链
它的可选性体现于请求或调用的目标当前可能为空(nil)
如果可选的目标有值,那么调用就会成功;
如果选择的目标为空(nil),则这种调用将返回空(nil)
多次调用被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
可选链的使用
在可选类型后面放一个问号,可以定义一个可选链。
这一点很像在可选值后面放一个叹号来强制拆得其封包内的值
它们的主要的区别在于当可选值为空时可选链即刻失败
然而一般的强制解析将会引发运行时错误。
因为可选链的结果可能为nil,可能有值.因此它的返回值是一个可选类型.
可以通过判断返回是否有值来判断是否调用成功
有值,说明调用成功
为nil,说明调用失败
1 2 3 4 5 6 7 8 9 10 let person = Person (name: "小明" )let dog = Dog (color: UIColor .yellow)let toy = Toy ()toy.price = 100.0 person.dog = dog dog.toy = toy
需求:获取小明的大黄宠物的玩具价格
取出的值为可选类型,因为可选链中有一个可选类型为nil,则返回nil
因此结果可能有值,可能为nil.因此是一个可选类型
1 2 let price = person.dog? .toy? .priceprint (price)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 / *强制解包: 该写法非常危险: let dog= p.dog let toy = dog! .toy let price toy! .price */ / *使用多层可选绑定:该写法非常麻烦 if let dog = p.dog { if let toy = dog.toy { let price = toy.price } } */ if let price = p.dog? .toy.price{ print (price) } p.dog? .toy.price = 50
闭包 Closure
闭包是一种没有名字的函数,用一个常量/变量来将它赋值。 闭包可以获取,存放,修改代码块作用域内的任何变量和常量
1. 闭包的定义: 1 2 3 4 5 6 7 8 9 10 11 var closureVariable: (parameters) -> returnType; closureVariable = { (parameters: paraType) -> returnType in statements } var closureVariable: (parameters) -> returnType = { (parameters: paraType) -> returnType in statements } closureVariable(parameters)
闭包的定义看起来类似于函数声明,但有细微的区别。虽然闭包和函数都有相同的参数列表,->
符号和返回类型。但在在闭包下,这些都是在闭包括号内的,第二个是返回类型后面有一个in
关键字。
2. 闭包的格式
闭包的设计是比函数轻量级一些的,更加方便,有一些方法可以简写
1 2 3 4 5 6 { (形参列表) -> (返回类型) in }
2.2 闭包作为函数的最后一个参数时候,可以写在”()”后面
1 2 3 4 5 6 7 8 func loadData (finished : () -> ()) { print ("耗时操作" ) finished() } loadData (){ () -> () in print ("闭包被执行了" ) }
2.3 闭包作为函数的参数(尾随闭包),如果只有这一个闭包参数,则函数的“()”可以省略,如果闭包没有参数,没有返回值,可以省略”in”
1 2 3 4 5 6 7 8 9 10 11 12 func loadData (finished : () -> ()) { print ("耗时操作" ) finished() } loadData { () -> () in print ("闭包被执行了" ) } loadData { print ("刷新UI" ) }
几种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let say:(String ) -> Void = { (name: String ) -> Void in print ("hi \(name) " ) } say("lnj" ) let say2:(String ) ->Void = { (name: String ) in print ("hi \(name) " ) } say2("lnj" ) let say3:() ->Void = { print ("hi lnj" ) } say3()
例子
1 2 3 4 5 6 7 8 9 10 override func touchesBegan (_ touches : Set <UITouch >, with event : UIEvent ?) { loadData { print ("刷新UI" ) } } func loadData (finished : () -> ()) { print ("耗时操作" ) finished() }
循环引用
闭包中属性必须要使用self
强引用,但是直接用self
很容易会引起循环引用,解决这个问题,需要使用weak var weakSelf = self
改成在闭包内对属性的弱引用。如果弱引用,一般是可选类型,在执行闭包内的代码的时候,需要确认weakSelf
是否存在。
1 2 3 4 5 weak var weakSelf = self loadData { () -> () in print ("回到主线程更新UI" ) weakSelf! .view.backGroudColor = .red }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var myClass : MyClass myClass = MyClass () myPerson.loadMyData(){[weak self ] () in self ? .view.backgroundColor = UIColor .red } myClass? .loadData({[unowned self ] () in self .view.backgroundColor = UIColor .red })
weak
在修饰对象销毁后会指向nil
, 只有可选类型才能指向nil
,所以这边的weakSelf
是可选类型。
关键字@escaping
escaping
表示逃逸的, 这边在闭包内部执行,闭包的回调如果在其他的闭包内,需要添加关键字@escaping
,“逃出”了控制范围。
swift
要求闭包内的参数都是内部参数,前面加上”_”
1 2 3 4 5 6 7 8 9 10 11 func loadPersonalData (callBackBlock : @escaping (_ jsonData: String ) -> ()){ DispatchQueue .global().async { print ("Thread is \(Thread.current) " ) DispatchQueue .main.sync { print ("Thread is \(Thread.current) " ) callBackBlock() } } }
swift闭包默认持有变量的reference
swift闭包默认在执行时才计算捕获变量的值
可在swift闭包中修改捕获变量的值
使用capture list,做变量的constant copy捕获。
参考:Swift闭包实战 - 简书
构造方法 方法重载 方法名称相同,形参可以不同. 例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person : NSObject { var name:String ? var age:Int ? override init () { self .name = "mzn" self .age = 27 } init (name : String , age :Int ) { self .name = name self .age = age } }
1 2 3 4 5 6 7 override func touchesBegan (_ touches : Set <UITouch >, with event : UIEvent ?) { let per = Person () print ("\(per.age! ) \(per.name! ) " ) let p1 = Person (name: "del" , age: 12 ) print ("\(p1.age! ) \(p1.name! ) " ) }
如果自定义构造方法,但是没有重写(override
)父类的构造方法,则默认的构造方法失效,会被替换成自定义的构造方法。比如将override init
注释后,Person
类原来的构造方法将失效:
在Swift
中如果需要在构造方法里使用KVC
给属性赋值,如setValuesForKeys
,调用之前需要调用父类的初始化:super.init()
,用以给属性(对象)分配存储空间。但是如果是基本数据类型,比如Int
类型是不会的,如下图: 所以解决的方式就是,基本类型需要赋值,比如直接将Person
的属性赋值var age:Int = 0
函数 Function 函数类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func myFunction () -> Void { print ("myFunction" ) } func myFunction (phoneNum : String ) -> Void { print ("打电话给\(phoneNum) " ) } func readMsg () -> String { return "吃饭了吗?" } func sum (num1 : Int , num2 : Int ) -> Int { return num1 + num2 }
内部参数&外部参数
内部参数: 只能在函数内部使用参数
看到的标识符名称,该标识符就是内部参数
外部参数: 在函数外部能看到的标识符名称,该标识符就是外部参数,只能在函数外部使用
默认情况下,所有的参数都是内部参数,也是外部参数
修改外部参数: 在标识符前加上外部名称即可
如果不希望显示外部参数,可以在标识符前加上“_”.
在参数前面加上#
相当于该参数即是内部参数, 也是外部参数
1 2 3 4 5 6 7 8 9 10 11 12 func sum (externalPara num1 : Int , num2 : Int ) -> Int { return num1 + num2 } let result = sum(num1: 10 , num2: 20 )let result1 = sum(externalPara: 1 , num2: 2 )func sum1 (_ num1 : Int , _ num2 : Int ) -> Int { return num1 + num2 } func sum1 (_ num1 : Int , _ num2 : Int ) -> Int { return num1 + num2 } let result3 = sum1(2 , 3 )
可变参数
swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数
它们必须具有相同的类型
我们可以通过在参数类型名后面加入(…)的方式来指示这是可变参数
1 2 3 4 5 6 7 8 9 10 11 func sum3 (nums : Int ... ) -> Int { var total = 0 for n in nums{ total += n } return total } let fub = sum3(nums: 20 , 30 , 40 )print (fub)
默认参数
某些情况,如果没有传入具体的参数,可以使用默认参数
1 2 3 4 5 func makeCoffee (coffeeName : String = "雀巢" ) -> String { return ("制作了一杯\(coffeeName) " ) } makeCoffee() makeCoffee(coffeeName: "猫屎" )
指针参数
引用类型(指针的传递)
默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址
必须是变量,因为需要在内部改变其值
Swift提供的inout
关键字就可以实现
对比下列两个函数
1 2 3 4 5 6 7 8 9 10 11 12 var m = 20 var n = 30 func swapNum (num1 : inout Int , num2 : inout Int ) { let temp = num1 num1 = num2 num2 = temp } swap (& m, & n)print ("m:\(m) n:\(n) " )
类 Class
定义一个类后,初始化类时,需要对该类的所有属性初始化,一般,值类型(String
, Int
)等,给一个默认控制,如果是对象,则改成可选类型,后面加?
Deinit
相当于Objective-C
的dealloc
方法
访问权限
Swift 中的访问控制模型基于模块和源文件这两个概念
internal : 在本模块中都可以进行访问
fileprivate : 在当前源文件中可以访
private : 在当前class中可以访问(但是extension
中不可以访问)
open : 在其他模块中可以访问
访问级别 Swift
提供了3种不同访问级别,对应的访问修饰符为:public
、internal
和private
。这些访问修饰符可以修饰类、结构体、枚举等面向对象的类型,还可以修饰变量、常量、下标、元组、函数、属性等内容。 (为了便于描述,我们把类、结构体、枚举、变量、常量、下标、元组、函数、属性等内容统一称为“实体”。)
public
。可以访问自己模块中的任何public
实体。如果使用import
语句引入其他模块,我们可以访问其他模块中的public
实体。
internal
。只能访问自己模块的任何internal
实体,不能访问其他模块中的internal实体。internal可以省略,换句话说,默认访问限定是internal。
private
。只能在当前源文件中使用的实体,称为私有实体。使用private修饰,可以用作隐藏某些功能的实现细节。
注意:
按钮的事件点击是由Runloop
监听,并以消息机制
进行传递的,因此,按钮点击事件不要设置为private
懒加载
使用lazy
关键字
lazy
用于修饰变量var
例子:
1 2 3 4 5 6 7 8 9 10 11 12 let exBlock = { (num: Int ) -> Int in print ("exBlock is excuted." ) return num } lazy var number : Int = self .exBlock(123 )override func touchesBegan (_ touches : Set <UITouch >, with event : UIEvent ?) { print (number) print (number) print (number) }
打印结果:
1 2 3 4 5 6 7 8 9 exBlock is excuted. 123 123 123 ---- 123 123 123 ----
可以发现,在用lazy
修饰number
这个变量后,多次打印,exBlock
闭包只在第一次调用,实现了懒加载的目的。**
#selector 例子
#selector
需要声明方法的作用域,在方法名前面加上所属的类
1 2 3 4 5 6 7 8 9 10 11 12 13 @IBOutlet weak var cyanButton: UIButton !override func viewDidLoad () { super .viewDidLoad() cyanButton.addTarget(self , action: #selector (ViewController .cyanButtonClick), for: .touchUpInside) } func cyanButtonClick () { print (#function ) }
#selector()
的好处是不再需要使用字符串来构造。因为当使用字符串构造时,若传入的字符串没有对应的方法名,那么程序在执行时就会直接崩溃:「unrecognized selector sent to instance」
。
若当前作用域构造 Selector
的方法名唯一时,可以直接使用方法名,而省略作用域。
1 2 3 cyanButton.addTarget(self , action: #selector (cyanButtonClick), for: .touchUpInside)
当遇到上述存在歧义的相同方法名时,也可以使用强制类型转换来解决:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @IBOutlet weak var cyanButton: UIButton !@IBOutlet weak var anotherCyanButton: UIButton !override func viewDidLoad () { super .viewDidLoad() let methodA = #selector (cyanButtonClick as () -> ()) let methodB = #selector (cyanButtonClick as (UIButton ) -> ()) cyanButton.addTarget(self , action: methodA, for: .touchUpInside) anotherCyanButton.addTarget(self , action: methodB, for: .touchUpInside) } func cyanButtonClick () { print (#function ) } @objc private func cyanButtonClick (_ button : UIButton ) { let btnLabel = button.titleLabel? .text ?? "nil" print (btnLabel) print (#function ) }
协议 Protocol 协议的格式
1 2 3 protocol SomeProtocol { }
1 2 3 4 class SomeClass : SomeSuperClass , FirstProtocol , AnotherProtocol { }
协议的基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protocol SportProtocol { func playBasketball () func playFootball () } class Person : SportProtocol { var name : String ? var age : Int = 0 func playBasketball () { print ("人在打篮球" ) } func playFootball () { print ("人在踢足球" ) } }
1 2 3 4 5 6 7 8 protocol CrazySportProtocol { func jumping () } protocol SportProtocol : CrazySportProtocol { func playBasketball () func playFootball () }
协议在代理模式中的使用
定义协议时,协议后面最好跟上: class
,表明是类的协议,避免和枚举类型混淆。
delegate
代理的属性最好用weak
修饰,避免循环引用。
Swift
建议协议的方法都是必须实现的,如果需要可选方法Optional
,则需要在protocal
和可选方法前面加上@objc
,在可选方法前加上Optional
。
循环 Loop
Swift 3.0 去掉 C 风格循环后怎么办? 关于 C 风格循环, 就是类似这样的语句:
1 2 3 let numberList = [1 , 2 , 3 , 4 , 5 ]for var i = 0 ; i < numberList.count; i++ {}
循环中如果不关心索引参数,可以使用_
进行忽略
1 2 3 for _ in array{ //excute loop code . }
for .. in 语法 第一个替代方案, 我们可以使用 for .. in 这样的语法:
1 2 3 4 5 let numberList = [1 , 2 , 3 , 4 , 5 ]var result = "" for num in numberList { result += "\(num) " }
这样就完成了对数组的遍历了, 但是还有另一个情况, 如果我们想知道每次遍历的索引怎么办呢, 还有一种方法:
1 2 3 for num in numberList.enumerate() { result += "[\(num.index) ]\(num.element) " }
可以使用这个集合类型的 enumerate
方法,将这个数组的索引和对应的元素都取了出来,然后在循环中就可以对索引项进行引用了, num.index
和 num.element
分别代表对应的索引和元素。
上面这个循环还可以再改写一下:
1 2 3 for (index, item) in numberList.enumerate() { result += "[\(index)]\(item) " }
不难看出,其实循环中的每一项都是一个元组(Tuple
),这个元组的第一项是当前的索引, 第二项是当前的数组元素。 那么就可以推理出, enumerate
函数其实就是对 numberList
数组做了一个变换,原来它是一个 Int
类型的数组,经过变换后,成为了(Int, Int)
元组类型的数组。
仔细看下, 它只不过是对集合类的一个集成, 这个集合每一项是一个元组 (n, x)
, n 代表索引, x 代表数组元素。
那么,还可以做点更有意思的事情:
1 2 3 for (index, item) in numberList.enumerate().reverse() { result += "[\(index) ]\(item) " }
调用 enumerate
, 之后再调用 reverse
方法, 就可以对一个数组进行反向遍历。
1 2 3 for (index, item) in numberList.enumerate().reverse() { result += "[\(index) ]\(item) " }
还可以:
1 2 3 for (index, item) in numberList.enumerate().filter({ (index, item) in index % 2 == 0 }) { result += "[\(index) ]\(item) " }
调用 filter
函数,过滤某些索引, 只遍历符合条件的那些元素。
区间(Range)循环 除了刚才说的这些, Swift
还提供了更方便的循环语法, 叫做 Range
循环。 比如这样:
1 2 3 4 5 var rs = "" ;for i in 0 ... 10 { rs += "\(i) " } print (rs)
这个语句会输出 0 到 10 之间的所有数字, 0…10 这个表示 Range 区间的范围。 当然,对于刚才的数组遍历来说, 一般数组索引都是数组长度减去 1, 用这个区间处理起来就会比较麻烦, 不过好在 Swift 给我们提供了另外一种 Range
方法:
1 2 3 for i in 0 ..< numberlist.count { rs += " " \(i)"" }
这次换成了 0..<numberlist.count
, 这种形式会排除闭区间最后那个数组,然后就可以在循环中用索引进行访问啦(注意符号=”” ..<=”” 两边不要有空格)。
数组 Array 三种遍历方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let arr = ["1" , "2" , "3" ]for item in arr{ print (item) } for i in 0 ..< arr.count{ print (arr[i]) } for (index, item) in arr.enumerated(){ print ("index = \(index) " ) print ("item = \(item) " ) }
相同类型的数组才能用“+”合并。
map()映射函数的介绍 1 func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
map()
是 Array
提供的方法,它接收一个函数作为参数,旧数组中的每个元素都会被拿去执行这个函数而变成新数组的对应元素,这是一种让数组从 [X] 转化为 [Y] 的方式,你需要提供的就是 X -> Y
的转化方式,而不必新建一个临时数组。
transform
参数:一个映射闭包。转换接受这个序列的一个元素作为它的参数,并返回一个相同类型或不同类型的转换值。
速记闭包语法一开始就很难做到这一点。map函数有一个参数,它是一个闭包(函数),当它在集合上循环时调用它。此闭包从集合中获取元素作为参数并返回结果。map函数返回数组中的这些结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 return jsonItems.map { (itemDesc: NSDictionary ) -> ListItem in let item = ListItem () if let icon = itemDesc["icon" ] as? String { item.icon = UIImage (named: icon) } if let title = itemDesc["title" ] as? String { item.title = title } if let urlString = itemDesc["url" ] as? String , let url = NSURL (string: urlString) { item.url = url } return item }
可以看出,可以用$0、$1、$2来表示调用闭包中参数,$0指代第一个参数,$1指代第二个参数,$2指代第三个参数,以此类推$n+1指代第n个参数,$后的数字代表参数的位置,一一对应。
但是我们现在 jsonItems.map 里面传入的参数函数的类型为 NSDictionary -> ListItem?,最后我们得到的是一个 [ListItem?] 数组,那些原来是不可用 NSDictionary 的位置就被我们替换成了 nil。比原来要好一些了,但还不够。
使用flatMap() 这个时候就轮到 flatMap() 来救场了。
flatMap() 与 map() 相似,但 flatMap() 用的是 T->U? 转化而不是 T->U 转化,而且如果转化出的数组元素是 nil 的话,就不会被添加到最后的结果数组里面。 在语法上,你可以这么理解,flatMap 就是你先使用 map 然后把结果数组“压平”(毕竟函数名就是这个意思),也就是从输出数组里去掉那些 nil。
字典 Dictionary Swift
字典的类型发现
使用let
修饰不可变字典,可var
修饰可变字典
定义一个空字典 empty collection literal requires an explicit type
1 var emptyDict: [String : Int] = [:]
如果字典中key
对应的值不存在就会新增
1 2 3 var dict = ["name" : "Sam", "age" : 12] as [String : Any] dict["gender"] = "man" print(dict) //"["name": "Sam", "age": 12, "gender": "man"]\n"
所以合并2个字典,可以通过对遍历另个字典的key复制来合并字典
如果字典是多种混合类型的话,需要指定说明类型
1 var dict = ["name" : "Sam", "age" : 12]
这样会报以下错误:
1 2 3 4 heterogeneous collection literal could only be inferred to '[String : Any]'; add explicit type annotation if this is intentional var dict = ["name" : "Sam", "age" : 12] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ as [String : Any]
指明类型:
1 var dict : [String : Any] = ["name" : "Sam", "age" : 12]
字典操作方法 1 2 3 4 5 6 7 8 9 var dict = ["key" : "value" ] dict.updateValue("myValue" , forKey: "key" ) dict.removeValue(forKey:"key" ) dict.removeAll() let index = dict.index(forKey: "key" ) dict.remove(at: index! ) print (dict)
字典遍历 1 2 3 4 5 6 7 8 9 10 11 12 for key in dict.keys{ print (key) } for value in dict.values{ print (value) } for (key, value) in dict{ print (key, value) }
字典合并 1 2 3 4 5 6 var mergeDict = ["key1" : "value1" ] as [String : Any ]let anotherDict = ["key2" : "value2" ] as [String : Any ]for (key, value) in anotherDict{ mergeDict[key] = anotherDict[key] } print (mergeDict)
枚举类型 Enum
概念介绍
枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值。
在 C/OC 语言中枚举指定相关名称为一组整型值
Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值.也可以提供一个值是字符串,一个字符,或是一个整型值或浮点值
枚举类型的语法
使用enum关键词并且把它们的整个定义放在一对大括号内1 2 3 enum SomeEnumeration { }
枚举类型的定义
case关键词表明新的一行成员值将被定义
不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值1 2 3 4 5 6 enum CompassPoint { case North case South case East case West }
定义方式二:多个成员值可以出现在同一行上
1 2 3 enum Planet { case Mercury , Venus , Earth , Mars , Jupiter , Saturn , Uranus , Neptune }
给枚举类型赋值
枚举类型赋值可以是字符串/字符/整型/浮点型
注意如果有给枚举类型赋值,则必须在枚举类型后面明确说明具体的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 enum CompassPoint : Int { case North = 1 case South = 2 case East = 3 case West = 4 } enum Planet { case Mercury = 1 , Venus , Earth , Mars , Jupiter , Saturn , Uranus , Neptune } let p = Planet (rawValue: 3 )if let p = p { switch p { case .Mercury : print ("Mercury" ) case .Venus : print ("Venus" ) case .Earth : print ("Mercury" ) case .Mars : print ("Mars" ) case .Jupiter : print ("Jupiter" ) case .Saturn : print ("Saturn" ) case .Uranus : print ("Uranus" ) case .Neptune : print ("Neptune" ) } }
结构体 Struct 结构体的介绍
概念介绍
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合
结构体(struct)指的是一种数据结构
结构体是值类型,在方法中传递时是值传递
结构的定义格式
结构体的使用
1 2 3 4 5 6 7 8 struct Location { var x : Double var y : Double } let location = Location (x: 90 , y: 90 )
结构体的增强
扩充构造函数
默认情况下创建Location时使用Location(x: x值, y: y值)
但是为了让我们在使用结构体时更加的灵活,swift还可以对构造函数进行扩充
扩充的注意点
在扩充的构造函数中必须保证成员变量是有值的
扩充的构造函数会覆盖原有的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct Location { var x : Double var y : Double init (x : Double , y : Double ) { self .x = x self .y = y } init (xyString : String ) { let strs = xyString.componentsSeparatedByString("," ) x = Double (strs.first! )! y = Double (strs.last! )! } } let location = Location (x: 100 , y: 100 )let location1 = Location (xyString: "100,100" )
为结构体扩充方法
为了让结构体使用更加灵活,swift的结构体中可以扩充方法
例子:为了Location结构体扩充两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct Location { var x : Double var y : Double init (x : Double , y : Double ) { self .x = x self .y = y } init (xyString : String ) { let strs = xyString.componentsSeparatedByString("," ) x = Double (strs.first! )! y = Double (strs.last! )! } mutating func moveH (x : Double ) { self .x += x } mutating func moveV (y : Double ) { self .y += y } }
注意:
如果我们使用的Location不是自己定义的,但是我们仍旧希望在自己的项目里扩展Location的操作
Swift也能帮我们达成,这个机制,叫做extension
Struct 出来的变量是 Immutable 的,用一个方法去改变变量里面的值的时候必须要加上一个关键词 mutating
,mutating
会创建一个可变类型指针,和给定的不可变指针指向同一块内存。
####s Swift中mutating关键字 Swift中protocol的功能比OC中强大很多,不仅能再class中实现,同时也适用于struct、enum。 使用 mutating 关键字修饰方法是为了能在该方法中修改 struct 或是 enum 的变量,在设计接口的时候,也要考虑到使用者程序的扩展性。所以要多考虑使用mutating来修饰方法。
结构体,枚举类型中的方法�声明为mutating
extension
中的方法声明为mutating
protocol
方法声明为mutating
1 2 3 4 5 6 7 8 9 extension Location { mutating func moveH (x : Double ) { self .x += x } mutating func moveV (y : Double ) { self .y += y } }
Swift Tips
开发中,优先使用常量let
, 减少变量所指向的内存地址被修改的可能。
少写self
对当前类的引用,减少在闭包或者方法块中循环引用的概率。
在某个类定义属性的时候,如果是基本数据类型,最好要初始化。(KVC的赋值问题)
1 2 var finished: () -> ()?// 错误写法,代表闭包的返回值为可选类型var finished: (() -> ())?// 正确写法,代表闭包为可选类型
官方建议在写代理方法或者协议时候,可以使用extension
的形式(相当于Objective-C
中的category
),将代码整理到一起,比如常见的UITableView
代理:
1 2 3 4 5 6 7 8 9 10 11 extension ViewController : UITableViewDataSource , UITableViewDelegate { func tableView (_ tableView : UITableView , cellForRowAt indexPath : IndexPath ) -> UITableViewCell { var cell = tableView.dequeueReusableCell(withIdentifier: "Cell" , for: indexPath as IndexPath ) as! UITableViewCell return cell } func tableView (_ tableView : UITableView , numberOfRowsInSection section : Int ) -> Int { return 10 } }
例子 Sample 网络请求
封装Alamofire
网络请求类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import Alamofireclass HomeViewController : UIViewController { override func viewDidLoad () { super .viewDidLoad() setupNaigationBar() loadData() } } extension HomeViewController { fileprivate func setupNaigationBar () { navigationController? .navigationBar.setBackgroundImage(UIImage (named: "navigation_background" ), for: .default) navigationItem.titleView = UIImageView (image: UIImage (named: "navigation_logo" )) navigationItem.rightBarButtonItem = UIBarButtonItem (image: UIImage (named: "navigation_logo" ), style: .plain, target: self , action: #selector (searchItemClick)) } } extension HomeViewController { @objc fileprivate func searchItemClick () { print ("\(#function ) " ) } }
调用网络请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 extension HomeViewController { fileprivate func loadData () { NetworkTools .requestData(URLString: "http://www.xxxx.com/article/0-21.html" , type: .get){ (result : Any ) in guard let resultDict = result as? [String : Any ] else { return } guard let dataArray = resultDict["key" ] as? [[String : Any ]] else { return } for dict in dataArray { self .newsModels.append(NewsModel (dict : dict)) } } } }
3.请求和解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import UIKitimport Alamofireclass HomeViewController : UIViewController { fileprivate lazy var newsModels : [NewsModel ] = [NewsModel ]() override func viewDidLoad () { super .viewDidLoad() setupNaigationBar() loadData() } }
参考 Refer
Beginning Swift 3 - Part 2: Variables | Ray Wenderlich
Intermediate Swift 2 - Part 1: Introduction | Ray Wenderlich
Programming in Swift - Part 1: Introduction | Ray Wenderlich