可選項(xiàng)
Optional可以讓我們擺脫很多不必要的判斷和取值谈截,聲明變量在類型后面加上?
的語(yǔ)法
class Toy {
let name: String
init(name: String) {
self.name = name
}
}
class Pet {
var toy: Toy?
}
class Child {
var pet: Pet?
}
if let toyName = xiaoming.pet?.toy?.name {
// 通過多級(jí)守護(hù)安全取到值
}
還有很多高級(jí)用法簸喂,后面慢慢研究。
if let & guard let
用于守護(hù)操作
// MARK: - if let / gucard let 用法
func ifletDemo() {
let i:Int? = 10
if let a = i {
// 此處a 解包了i,如果a解包是nil則不成立
print(a)
} else {
print("i is nil")
}
// 此處使用guard 減少邏輯分支層級(jí),a在后續(xù)使用已經(jīng)被解包
guard let a = i else {
print("i is nil")
return
}
print(a)
}
switch
swift 的switch比OC要靈活很多唉锌,支持多種數(shù)據(jù)類型袄简,Int泛啸、String候址、浮點(diǎn)岗仑,結(jié)構(gòu)體荠雕, 并且支持使用元組值綁定稳其,結(jié)構(gòu)體值綁定驶赏。
let n = 10
switch n {
case 1:
print("\(n)")
case 10:
print("\(n)")
default:
print("default")
}
// 多種類型, 字符串、Bool既鞠、結(jié)構(gòu)體煤傍、浮點(diǎn)
var str = "my"
switch str {
case "my":
print("case my")
fallthrough
case "d":
print("1") //被輸出
default:
print("default")
}
// 運(yùn)算區(qū)間
switch n {
case 0 ..< 5 :
print("0~5")
case 5 ... 10 :
print("5~10")
default:
print("default")
}
// Value Binding 值綁定,適合和enum合用
let request = (6,"success")
switch request {
case (let errorCode, let state):
print("\(state),\(errorCode)")
case (let errorCode, _):
"error code is \(errorCode)"
}
// enum 結(jié)構(gòu)值綁定
enum Result {
case Success(json: String, isSuccess: Bool)
case Fail(error: String)
}
let r = Result.Success(json: "{}", isSuccess: true)
switch r {
case Result.Success(let json, let isSuccess):
print("\(json)")
default:
print("default")
}
for
swift 3.0 已經(jīng)去掉了 C 語(yǔ)言風(fēng)格的for循環(huán)嘱蛋。
let array = [1, 12, 9, 4]
// 遍歷數(shù)組
for i in array {
print(i)
}
// 條件區(qū)間
for i in 0...10 {
print(i)
}
字符串
swift 中的字符串操作蚯姆,如果遇到不方便操作的時(shí)候記得轉(zhuǎn)為NSString
// 遍歷字符串
for c in str.characters {
print(c)
}
// 反序
for c in str.characters.reversed() {
print(c)
}
// 轉(zhuǎn)換成NSString
let ocStr = str as NSString
// 字符串長(zhǎng)度
print(ocStr.length)
// 字節(jié)長(zhǎng)度
print(str.lengthOfBytes(using: .utf8))
// 截取字符串
let substr = ocStr.substring(with: NSMakeRange(0, 5))
print(substr)
數(shù)組
// MARK: -遍歷數(shù)組
func arrayDemo() {
let myArray = [1, 2, 3]
// 遍歷
// 元組1
print("-----遍歷數(shù)組1....")
for e in myArray.enumerated() {
print("\(e.offset), \(e.element)")
}
// 元組2
print("-----遍歷數(shù)組2....")
for (of, el) in myArray.enumerated() {
print("\(of),\(el)")
}
// 直接遍歷
print("-----遍歷數(shù)組3....")
for item in myArray {
print(item)
}
// 反轉(zhuǎn)數(shù)組
print("-----反轉(zhuǎn)數(shù)組....")
for item in myArray.reversed() {
print(item)
}
print("-----")
for (of, el) in myArray.enumerated().reversed() {
print("\(of),\(el)")
}
}
// MARK: - 數(shù)組拼接
func arrayAppendDemo() {
print("------數(shù)組拼接")
var array = ["1", "test"]
print(array)
// 數(shù)組添加元素
array.append("x")
let array1 = ["a", "b", "c"]
array += array1
print(array)
print("------")
// 數(shù)組的拼接需要在同類型下進(jìn)行
var array2: [Any] = [2, "tt"] as [Any]
let array3: [Any] = ["a", "b", "c"]
array2 += array3
print(array2)
}
字典
// 字典遍歷
func demo() {
// Swift3.1 必須要轉(zhuǎn)為一個(gè)確定的類型 as [String:Any]
let dic = ["name":"alienjunx", "age":20] as [String:Any]
print(dic)
// 直接遍歷
for (k,v) in dic {
print("\(k),\(v)")
}
print("-----")
for item in dic {
print("\(item.key),\(item.value)")
}
}
// 合并
func demo2() {
print("-----合并")
var dic1 = ["name":"alienjunx", "age":20] as [String:Any]
print(dic1)
let dic2 = ["height":90, "name":"老萬"] as [String:Any]
print(dic2)
for (k,v) in dic2 {
dic1[k] = dic2[k]
}
print(dic1)
}
// 刪除
func removeDemo() {
print("-----刪除")
var dic1 = ["name":"alienjunx", "age":20] as [String:Any]
print(dic1)
dic1.removeValue(forKey: "name")
print(dic1)
}
函數(shù)
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// 函數(shù)返回一個(gè)String 類型
func myFunc() -> String {
let i = "myFunc"
return i
}
// 函數(shù)返回一個(gè)閉包
func loadMyName(name: String) -> ((String)->()) {
print("\(name)")
let result: ((String)->()) = {(rname: String)->() in
print("\(rname),\(name)")
}
return result
}
loadMyName(name: "test")("my")
// 異步處理回調(diào)函數(shù)
// 參數(shù)的閉包需要進(jìn)行聲明 @escaping 逃逸閉包
func funcCallBack(completion: @escaping (_ json: String)->()) {
DispatchQueue.global().async {
//// 耗時(shí)處理
sleep(2)
// 主線程回調(diào)
DispatchQueue.main.async(execute: {
completion("ok")
})
}
}
funcCallBack { (resultStr) in
print(resultStr)
}
閉包
與OC中的block一樣洒敏,都是一段提前準(zhǔn)備好的代碼龄恋,通常用于參數(shù)傳遞進(jìn)行處理回調(diào)結(jié)果
// closure 是 '() -> ()' 一個(gè)閉包的類型
// '=' 后面是實(shí)現(xiàn)這閉包 '閉包聲明 in 實(shí)現(xiàn)'
let closure: () -> () = {() -> () in
print("closure")
}
/*
1.如果閉包是無參數(shù)無返回,則實(shí)現(xiàn)時(shí)可以省略 in和前面的聲明
2.按照swift的類型推導(dǎo)凶伙,closure1的所屬類型也可以不顯示聲明
*/
let closure1 = {
print("closure1")
}
// 調(diào)用閉包
closure()
// 有參數(shù)的閉包
let closure2 = { (name: String) -> () in
print(name)
}
closure2("alienjunx")
//---------------------------
// 函數(shù)中傳遞閉包作為回調(diào)
closureFunc(callBack: { (name) -> () in
print(name)
})
// 如果函數(shù)的最后一個(gè)參數(shù)是閉包篙挽,可以使用尾隨閉包的方式簡(jiǎn)寫
closureFunc { (name) in
print(name)
}
// callBack 是一個(gè) (String) -> () 類型的閉包
func closureFunc(callBack:(String) -> ()) {
let myName = "testName"
callBack(myName)
}
??、?镊靴、铣卡!
let i:Int? = 10
// ?? 操作符,用于解包偏竟,如果Option中是nil煮落,則返回 后面的值
let num = i ?? 0
問號(hào)?
-- 聲明時(shí)添加踊谋?蝉仇,告訴編譯器這個(gè)是Optional的,如果聲明時(shí)沒有手動(dòng)初始化殖蚕,就自動(dòng)初始化為nil
-- 在對(duì)變量值操作前添加轿衔?,判斷如果變量時(shí)nil睦疫,則不響應(yīng)后面的方法害驹。
嘆號(hào)!
-- 聲明時(shí)添加蛤育!宛官,告訴編譯器這個(gè)是Optional的,并且之后對(duì)該變量操作的時(shí)候瓦糕,都隱式的在操作前添加底洗!
-- 在對(duì)變量操作前添加!咕娄,表示默認(rèn)為非nil亥揖,直接解包進(jìn)行處理
構(gòu)造函數(shù)
構(gòu)造函數(shù)在swift中都是以init命名
override init() {
name = ""
// 會(huì)隱式調(diào)用super.init()
}
init(name: String) {
self.name = name
// 會(huì)隱式調(diào)用super.init()
}
重寫
只要父類提供了可重寫的函數(shù),那么在子類中可以重新實(shí)現(xiàn)此函數(shù)圣勒,在swift中
-- 子類先初始化后父類才初始化费变,這與oc完全是不一樣的
class Person: NSObject {
var name: String
init(name: String) {
self.name = name
// 可不寫super.init() 會(huì)隱式調(diào)用
}
}
// 繼承于Person
class Student: Person {
var no: Int = 0
// 重寫父類構(gòu)造函數(shù)
override init(name: String) {
super.init(name: name)
self.no = 10
}
}
let s = Student(name: "小明")
print("\(s.name) \(s.no)")
重載
函數(shù)名相同摧扇,參數(shù)類型和個(gè)數(shù)不同,重載不同的參數(shù)來滿足不同的初始化操作胡控,在OC中構(gòu)造函數(shù)重載用的非常多。
class Person: NSObject {
var name: String
var title: String?
// 構(gòu)造方法1
init(name: String) {
self.name = name
}
// 構(gòu)造方法2
init(name: String, title: String?) {
self.name = name
self.title = title
}
}
便捷構(gòu)造函數(shù)
作為構(gòu)造函數(shù)的一種補(bǔ)充方式旁趟。 本身不負(fù)責(zé)對(duì)象的創(chuàng)建和初始化工作昼激,只是負(fù)責(zé)一些值的設(shè)置,并且可以通過條件判斷锡搜,進(jìn)行是否需要初始化橙困,防止不必要的內(nèi)存開銷,只要不滿足條件可以直接返回nil耕餐。
-- 必須調(diào)用的是自己的init凡傅,而不是父類的init
-- 不能被重寫
-- 可以用于extension 來擴(kuò)展控件的初始化操作
// 便捷構(gòu)造函數(shù) (如果有初始化失敗的情況,記得init? 可選的情況)
convenience init?(name: String, age: Int) {
// 年齡小于0不滿足初始化條件
// 直接返回nil肠缔,表示初始化失敗
if age < 0 {
return nil
}
// 對(duì)象真正初始化
self.init()
// 賦新的值
self.name = name
}
析構(gòu)函數(shù)
類似OC中的dealloc函數(shù)夏跷,可以在對(duì)象銷毀前做一些事情。
-- 不可直接調(diào)用
-- 不可重載
-- 對(duì)象銷毀前自動(dòng)調(diào)用
deinit {
// 跟蹤對(duì)象的銷毀
// 必須釋放的資源
/*
-- 通知釋放明未,不釋放會(huì)造成內(nèi)存泄露
-- kvo釋放槽华,崩潰
-- NSTimer
-- CADisplay
*/
}
KVC
-- 不能對(duì)private修飾的屬性進(jìn)行賦值。
-- oc中是全部裸露趟妥,與swift不同
KVO
kvo 是基于kvc的猫态,都是OC運(yùn)行時(shí)的概念,在swift中還需要做額外的工作披摄,那就是將想要觀測(cè)的對(duì)象標(biāo)記為 dynamic亲雪。
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class MyClass: NSObject {
dynamic var date = Date()
}
class MyClass1: NSObject {
var date = Date()
}
class MyChildClass: MyClass1 {
dynamic override var date: Date {
get { return super.date }
set { super.date = newValue }
}
}
private var myContext = 0
class Class: NSObject {
var myObject: MyClass!
override init() {
super.init()
myObject = MyClass()
print("初始化 MyClass,當(dāng)前日期: \(myObject.date)")
myObject.addObserver(self,
forKeyPath: "date",
options: .new,
context: &myContext)
delay(3) {
self.myObject.date = Date()
}
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?)
{
if let change = change, context == &myContext {
let newDate = change[.newKey]
print("日期發(fā)生變化 \(newDate)")
}
}
}
let obj = Class()
private & fileprivate & @objc
-- private 在swift3開始疚膊,private 就是真正的私有义辕,只能在本類中訪問(在extension或其他類中都不能訪問)。
-- fileprivate 在當(dāng)前文件中私有寓盗,別的文件不能訪問此關(guān)鍵字修飾的屬性和函數(shù)终息。
-- @objc 允許這個(gè)函數(shù)在運(yùn)行時(shí)通過OC的消息機(jī)制被調(diào)用
-- open > public > interal > fileprivate > private
extension
-- 不能有屬性
-- 不能重寫父類方法
extension String {
func myFrist() -> String? {
let str = (self as NSString)
if str.length == 0 {
return nil
}
let f = str.substring(with: NSMakeRange(0, 1))
return f
}
}
錯(cuò)誤處理
try?(有異常會(huì)返回nil) / try! (如果有異常就崩潰 )
let json = try? JSONSerialization.jsonObject(with: data! as Data, options: [])
捕獲錯(cuò)誤
do {
let json = try JSONSerialization.jsonObject(with: data! as Data, options: [])
} catch {
print(error)
}
應(yīng)用實(shí)戰(zhàn)
一、runtime 獲取屬性
class func propertyList() -> [String] {
var count: UInt32 = 0
var ptyList: [String] = []
// 獲取屬性列表
let list = class_copyPropertyList(self, &count)
// 循環(huán)取出屬性
for i in 0..<Int(count) {
// 用guard let 來優(yōu)化 (贞让?周崭!)帶來的解包問題
// UnsafePointer<Int8>? -> Byte(8個(gè)字節(jié))- Char ,C語(yǔ)言的字符串
// 轉(zhuǎn)換成String
guard let pty = list?[i],
let cName = property_getName(pty),
let name = String(utf8String: cName) else {
continue
}
ptyList.append(name)
}
// 釋放 C 語(yǔ)言對(duì)象
free(list)
print("屬性數(shù)量\(count)")
return ptyList
}
二喳张、循環(huán)引用
// 模擬加載數(shù)據(jù)的函數(shù)
func loadData(completion: @escaping () -> ()) {
completion()
}
// weakSelf -> ViewController?
// OC的解決循環(huán)引用方法
weak var weakSelf = self
loadData {
print(weakSelf?.view)
}
// Swift 推薦方法
// [weak self] 表示 {} 中所有self都是弱引用续镇,需要注意解包
loadData { [weak self] in
print(self?.view)
}
三、懶加載
// 懶加載
// 懶加載的代碼只會(huì)在第一次調(diào)用的時(shí)候執(zhí)行閉包
// 如果label被置為nil销部,懶加載也不會(huì)再次執(zhí)行
/*
1.延遲創(chuàng)建
2.避免解包 摸航? 的煩惱
*/
lazy var label: MyLabel = MyLabel()
// 懶加載其實(shí)也是一個(gè)閉包
// 上面的懶加載方式展開如下
lazy var label = { () -> (MyLabel) in
let l = MyLabel()
return l
}()
統(tǒng)計(jì)代碼行數(shù)
在項(xiàng)目根目錄下 以下命令
find . -name "*.swift" | xargs wc -l