Swift語(yǔ)法01

Swift 簡(jiǎn)介

查看Swift當(dāng)前版本

$ xcrun swift --version

簡(jiǎn)介

  • Swift 語(yǔ)言由蘋果公司在 2014 年推出,用來撰寫 OS X 和 iOS 應(yīng)用程序
  • 2014 年,在 Apple WWDC 發(fā)布
  • 2016.3月 Apple WWDC 支持 Linux系統(tǒng),國(guó)外已經(jīng)有人開始用Swift 寫服務(wù)端

歷史

  • 2010 年 7 月,蘋果開發(fā)者工具部門總監(jiān)克里斯·拉特納開始著手 Swift 編程語(yǔ)言的設(shè)計(jì)
  • 用一年時(shí)間居砖,完成基本架構(gòu)
  • Swift 大約歷經(jīng) 4 年的開發(fā)期,2014 年 6 月發(fā)布
  • 在 2015.12.3 開源, 現(xiàn)在GitHub上超過30000個(gè)星星

從發(fā)布至今,蘋果的每一個(gè)舉措都彰顯其大力推廣 Swift 的決心

版本

  • 正式版 Swift 3.0, Xcode 8.0

Swift 特色

  • Swfit3.0 是變化最大的一個(gè)版本,全面的去OC化了,Foundation框架的NS前綴去掉了
  • 可以使用現(xiàn)有的 CocoaCocoa Touch 框架
  • 蘋果宣稱 Swift的特點(diǎn)是:快速揩慕、現(xiàn)代、安全扮休、互動(dòng)迎卤,而且明顯優(yōu)于 Objective-C 語(yǔ)言
  • 取消了預(yù)編譯指令包括宏
  • Swift 取消了 Objective-C 的指針及其他不安全訪問的使用
  • 舍棄 Objective-C 早期應(yīng)用 Smalltalk 的語(yǔ)法,全面改為句點(diǎn)表示法
  • 提供了類似 Java 的名字空間(namespace)玷坠、泛型(generic)蜗搔、運(yùn)算對(duì)象重載(operator overloading
  • Swift 被簡(jiǎn)單的形容為 “沒有 C 的 Objective-C”(Objective-C without the C)

Swift 現(xiàn)狀

  • 目前國(guó)內(nèi)有些公司的新項(xiàng)目已經(jīng)直接采用 Swift 開發(fā)
  • 目前很多公司都在做 Swift 的人才儲(chǔ)備
  • 應(yīng)聘時(shí),會(huì) Swift 開發(fā)無疑會(huì)增加自身籌碼
  • 很多公司現(xiàn)在已經(jīng)深陷Swfit無法自拔了,包括唐巧的猿題庫(kù)

為什么要學(xué)習(xí) Swift八堡?

  1. 從4月份開始樟凄,蘋果提供的資料已經(jīng)沒有 OC 的了,這說明蘋果推動(dòng) Swift 的決心
  2. OC 源自于 smalltack-c兄渺,迄今已經(jīng)有 40 多年的歷史缝龄,雖然 OC 的項(xiàng)目還會(huì)在未來持續(xù)一段時(shí)間,但是更換成 Swift 是未來必然的趨勢(shì)
  3. 現(xiàn)在很多公司都注重人才儲(chǔ)備溶耘,如果會(huì)Swift二拐,就業(yè)會(huì)有很大的優(yōu)勢(shì),簡(jiǎn)歷中如果寫上會(huì) Swift凳兵,雖然面試中雖然不會(huì)怎么被問到百新,但對(duì)于薪資提升有很大幫助,同時(shí)可以從另外一個(gè)側(cè)面證明我們是有自學(xué)能力的人庐扫,這是所有企業(yè)都需要的
  4. Swift 里面融合了很多其他面向?qū)ο笳Z(yǔ)言的思想饭望,不像OC那么封閉仗哨,學(xué)會(huì) Swift,再轉(zhuǎn)其他語(yǔ)言會(huì)輕松很多
  5. Swift 畢竟也是出身自蘋果铅辞,整體程序開發(fā)思路和 OC 是一樣的厌漂,等 Swift 項(xiàng)目講完后,大家完全可以用同樣的思路寫出 OC 的來斟珊,而且在翻寫的過程中苇倡,能夠?qū)芏嘣竞雎缘?OC 基本功有很大的加強(qiáng)和改善

Swift 開發(fā)快速體驗(yàn)

目標(biāo)

  • playground 快速體驗(yàn) & 學(xué)習(xí)資源分享
  • 項(xiàng)目開發(fā)快速體驗(yàn),了解 Swift 基本程序結(jié)構(gòu)

學(xué)習(xí)資源

基本語(yǔ)法

目標(biāo)

  • 熟悉 Swift 基本語(yǔ)法
    • 常量 & 變量
    • 可選項(xiàng)
    • 控制流
      • if
      • 三目
      • if let
      • guard
      • switch
    • 字符串
    • 循環(huán)
    • 集合
      • 數(shù)組
      • 集合

變量和常量

需要掌握


定義

  • let 定義常量,一經(jīng)賦值不允許再修改
  • var 定義變量堵漱,賦值之后仍然可以修改
//: # 常量
//: 定義常量并且直接設(shè)置數(shù)值
let x = 20
//: 常量數(shù)值一經(jīng)設(shè)置综慎,不能修改,以下代碼會(huì)報(bào)錯(cuò)
// x = 30

//: 使用 `: 類型`勤庐,僅僅只定義類型示惊,而沒有設(shè)置數(shù)值
let x1: Int
//: 常量有一次設(shè)置數(shù)值的機(jī)會(huì),以下代碼沒有問題愉镰,因?yàn)?x1 還沒有被設(shè)置數(shù)值
x1 = 30
//: 一旦設(shè)置了數(shù)值之后米罚,則不能再次修改,以下代碼會(huì)報(bào)錯(cuò)岛杀,因?yàn)?x1 已經(jīng)被設(shè)置了數(shù)值
// x1 = 50

//: # 變量
//: 變量設(shè)置數(shù)值之后阔拳,可以繼續(xù)修改數(shù)值
var y = 200
y = 300

自動(dòng)推導(dǎo)

  • Swift能夠根據(jù)右邊的代碼,推導(dǎo)出變量的準(zhǔn)確類型
  • 通常在開發(fā)時(shí)类嗤,不需要指定變量的類型
  • 如果要指定變量糊肠,可以在變量名后使用:,然后跟上變量的類型

重要技巧:Option + Click 可以查看變量的類型

[圖片上傳失敗...(image-1c3941-1536302339170)]

沒有隱式轉(zhuǎn)換R怕唷;豕!

  • Swift 對(duì)數(shù)據(jù)類型要求異常嚴(yán)格
  • 任何時(shí)候精偿,都不會(huì)做隱式轉(zhuǎn)換

如果要對(duì)不同類型的數(shù)據(jù)進(jìn)行計(jì)算弧圆,必須要顯式的轉(zhuǎn)換

let x2 = 100
let y2 = 10.5

let num1 = Double(x2) + y2
let num2 = x2 + Int(y2)

let & var 的選擇

  • 應(yīng)該盡量先選擇常量,只有在必須修改時(shí)笔咽,才需要修改為 var
  • 在 Xcode 7.0 中搔预,如果沒有修改變量,Xcode 會(huì)提示修改為 let

Optional 可選值

需要掌握


  • 理解可選項(xiàng)的概念
  • 理解可選項(xiàng)的概念 --> 要么有值,要么為nil
  • 知道可選項(xiàng)的規(guī)則 --> 參與計(jì)算需要強(qiáng)制解包
  • 知道兩個(gè)符號(hào)
    1. '?' 定義可選項(xiàng)
    2. '!' 對(duì)可選類型進(jìn)行強(qiáng)制解包, 程序員一定要對(duì)解包負(fù)責(zé)
  • 常量的可選項(xiàng)使用前需要設(shè)置初始值
  • 變量的可選項(xiàng)默認(rèn)是nil

介紹

  • Optional 是 Swift 的一大特色叶组,也是 Swift 初學(xué)者最容易困惑的問題
  • 定義變量時(shí)拯田,如果指定是可選的,表示該變量可以有一個(gè)指定類型的值甩十,也可以是 nil
  • 定義變量時(shí)船庇,在類型后面添加一個(gè) ?吭产,表示該變量是可選的
  • 變量可選項(xiàng)的默認(rèn)值是 nil
  • 常量可選項(xiàng)沒有默認(rèn)值,主要用于在構(gòu)造函數(shù)中給常量設(shè)置初始數(shù)值
//: num 可以是一個(gè)整數(shù)鸭轮,也可以是 nil臣淤,注意如果為 nil,不能參與計(jì)算
let num: Int? = 10
  • 如果 Optional 值是 nil窃爷,不允許參與計(jì)算
  • 只有解包(unwrap)后才能參與計(jì)算
  • 在變量后添加一個(gè) !邑蒋,可以強(qiáng)行解包

注意:必須要確保解包后的值不是 nil,否則會(huì)報(bào)錯(cuò)

//: num 可以是一個(gè)整數(shù)吞鸭,也可以是 nil寺董,注意如果為 nil覆糟,不能參與計(jì)算
let num: Int? = 10

//: 如果 num 為 nil刻剥,使用 `!` 強(qiáng)行解包會(huì)報(bào)錯(cuò)
let r1 = num! + 100

//: 使用以下判斷,當(dāng) num 為 nil 時(shí)滩字,if 分支中的代碼不會(huì)執(zhí)行
if let n = num {
    let r = n + 10
}

常見錯(cuò)誤

unexpectedly found nil while unwrapping an Optional value

翻譯

在[解包]一個(gè)可選值時(shí)發(fā)現(xiàn) nil

?? 運(yùn)算符

  • ?? 運(yùn)算符可以用于判斷 變量/常量 的數(shù)值是否是 nil造虏,如果是則使用后面的值替代
  • 在使用 Swift 開發(fā)時(shí),?? 能夠簡(jiǎn)化代碼的編寫
let num: Int? = nil

let r1 = (num ?? 0) + 10
print(r1)

控制流

if

  • Swift 中沒有 C 語(yǔ)言中的非零即真概念
  • 在邏輯判斷時(shí)必須顯示地指明具體的判斷條件 true / false
  • if 語(yǔ)句條件的 () 可以省略
  • 但是 {} 不能省略
let num = 200
if num < 10 {
    print("比 10 小")
} else if num > 100 {
    print("比 100 大")
} else {
    print("10 ~ 100 之間的數(shù)字")
}

三目運(yùn)算

  • Swift 中的 三目 運(yùn)算保持了和 OC 一致的風(fēng)格
var a = 10
var b = 20
let c = a > b ? a : b
print(c)

適當(dāng)?shù)剡\(yùn)用三目麦箍,能夠讓代碼寫得更加簡(jiǎn)潔

可選項(xiàng)判斷

  • 由于可選項(xiàng)的內(nèi)容可能為 nil漓藕,而一旦為 nil 則不允許參與計(jì)算
  • 因此在實(shí)際開發(fā)中,經(jīng)常需要判斷可選項(xiàng)的內(nèi)容是否為 nil

單個(gè)可選項(xiàng)判斷

let url = NSURL(string: "http://www.baidu.com")

//: 方法1: 強(qiáng)行解包 - 缺陷挟裂,如果 url 為空享钞,運(yùn)行時(shí)會(huì)崩潰
let request = NSURLRequest(URL: url!)

//: 方法2: 首先判斷 - 代碼中仍然需要使用 `!` 強(qiáng)行解包
if url != nil {
    let request = NSURLRequest(URL: url!)
}


可選項(xiàng)條件判斷

//: 1> 初學(xué) swift 一不小心就會(huì)讓 if 的嵌套層次很深,讓代碼變得很丑陋
if let u = url {
    if u.host == "www.baidu.com" {
        let request = NSURLRequest(URL: u)
    }
}

//: 2> 使用 ',' 可以獲取前面賦值對(duì)象
if let u = url , u.host == "www.baidu.com" {
    let request = NSURLRequest(URL: u)
}
  • 小結(jié)
    • if let 不能與使用 &&诀蓉、|| 等條件判斷

多個(gè)可選項(xiàng)判斷

//: 3> 可以使用 `,` 同時(shí)判斷多個(gè)可選項(xiàng)是否為空
let oName: String? = "張三"
let oNo: Int? = 100

if let name = oName {
    if let no = oNo {
        print("姓名:" + name + " 學(xué)號(hào): " + String(no))
    }
}

if let name = oName, let no = oNo {
    print("姓名:" + name + " 學(xué)號(hào): " + String(no))
}

判斷之后對(duì)變量需要修改

let oName: String? = "張三"
let oNum: Int? = 18

if var name = oName, num = oNum {

    name = "李四"
    num = 1

    print(name, num)
}

guard

  • guard 是與 if let 相反的語(yǔ)法栗竖,Swift 2.0 推出的
let oName: String? = "張三"
let oNum: Int? = 18

guard let name = oName else {
    print("name 為空")
    return
}

guard let num = oNum else {
    print("num 為空")
    return
}

// 代碼執(zhí)行至此,name & num 都是有值的
print(name)
print(num)
  • 在程序編寫時(shí)渠啤,條件檢測(cè)之后的代碼相對(duì)是比較復(fù)雜的
  • 使用 guard 的好處
    • 能夠判斷每一個(gè)值
    • 在真正的代碼邏輯部分狐肢,省略了一層嵌套

switch

  • switch 不再局限于整數(shù)
  • switch 可以針對(duì)任意數(shù)據(jù)類型進(jìn)行判斷
  • 不再需要 break
  • 每一個(gè) case后面必須有可以執(zhí)行的語(yǔ)句
  • 要保證處理所有可能的情況,不然編譯器直接報(bào)錯(cuò)沥曹,不處理的條件可以放在 default 分支中
  • 每一個(gè) case 中定義的變量?jī)H在當(dāng)前 case 中有效份名,而 OC 中需要使用 {}
let score = "優(yōu)"

switch score {
    case "優(yōu)":
        let name = "學(xué)生"
        print(name + "80~100分")
    case "良": print("70~80分")
    case "中": print("60~70分")
    case "差": print("不及格")
    default: break
}

for 循環(huán)

  • OC 風(fēng)格的循環(huán) '++'語(yǔ)法已經(jīng)廢棄
var sum = 0
for var i = 0; i < 10; i++ {
    sum += I
}
print(sum)
  • for-in,0..<10 表示從0到9
sum = 0
for i in 0..<10 {
    sum += I
}
print(sum)
  • 范圍 0...10 表示從0到10
sum = 0
for i in 0...10 {
    sum += I
}
print(sum)
  • 省略下標(biāo)
    • _ 能夠匹配任意類型
    • _ 表示忽略對(duì)應(yīng)位置的值
for _ in 0...10 {
    print("hello")
}

字符串

在 Swift 中絕大多數(shù)的情況下妓美,推薦使用 String 類型

  • String 是一個(gè)結(jié)構(gòu)體僵腺,性能更高
    • String 目前具有了絕大多數(shù) NSString 的功能
    • String 支持直接遍歷
  • NSString 是一個(gè) OC 對(duì)象,性能略差
  • Swift 提供了 StringNSString 之間的無縫轉(zhuǎn)換

字符串演練

  • 遍歷字符串中的字符
for s in str.characters {
    print(s)
}
  • 字符串長(zhǎng)度
// 返回以字節(jié)為單位的字符串長(zhǎng)度壶栋,一個(gè)中文占 3 個(gè)字節(jié)
let len1 = str.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
// 返回實(shí)際字符的個(gè)數(shù)
let len2 = str.characters.count

  • 字符串拼接
    • 直接在 "" 中使用 \(變量名) 的方式可以快速拼接字符串
let str1 = "Hello"
let str2 = "World"
let i = 32
str = "\(i) 個(gè) " + str1 + " " + str2

我和我的小伙伴再也不要考慮 stringWithFormat 了 :D

  • 可選項(xiàng)的拼接
    • 如果變量是可選項(xiàng)辰如,拼接的結(jié)果中會(huì)有 Optional
    • 為了應(yīng)對(duì)強(qiáng)行解包存在的風(fēng)險(xiǎn),蘋果提供了 ?? 操作符
    • ?? 操作符用于檢測(cè)可選項(xiàng)是否為 nil
      • 如果不是 nil委刘,使用當(dāng)前值
      • 如果是 nil丧没,使用后面的值替代
let str1 = "Hello"
let str2 = "World"
let i: Int? = 32
str = "\(i ?? 0) 個(gè) " + str1 + " " + str2
  • 格式化字符串
    • 在實(shí)際開發(fā)中鹰椒,如果需要指定字符串格式,可以使用 String(format:...) 的方式
let h = 8
let m = 23
let s = 9
let timeString = String(format: "%02d:%02d:%02d", arguments: [h, m, s])
let timeStr = String(format: "%02d:%02d:%02d", h, m, s)

String & Range 的結(jié)合

  • 在 Swift 中呕童,StringRange連用時(shí)漆际,語(yǔ)法結(jié)構(gòu)比較復(fù)雜
  • 如果不習(xí)慣 Swift 的語(yǔ)法,可以將字符串轉(zhuǎn)換成 NSString 再處理
let helloString = "我們一起飛"
(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
  • 使用 Range<Index> 的寫法 前面不要試圖去掌握這段代碼,每一個(gè)版本都在變化,沒用
let str = "愛笑的人運(yùn)氣不會(huì)太差喲"
        let startIndex = str.index(str.startIndex, offsetBy: 2)
        let endIndex = str.index(str.endIndex, offsetBy: -1)
        let subStr1 = str.substring(to: startIndex)
        print(subStr1)

        let subStr2 = str.substring(from: endIndex)
        print(subStr2)

        let subStr3 = str.substring(with: startIndex..<endIndex)
        print(subStr3)

        //String 和  NSString 之間可以相互轉(zhuǎn)換 可以轉(zhuǎn)換為 NSString 來截取子串
        let NStr = (str as NSString).substring(with: NSRange(location: 1, length: 2))
        print(NStr)

集合

數(shù)組

  • 數(shù)組使用 [] 定義夺饲,這一點(diǎn)與 OC 相同
//: [Int]
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • 遍歷
for num in numbers {
    print(num)
}
  • 通過下標(biāo)獲取指定項(xiàng)內(nèi)容
let num1 = numbers[0]
let num2 = numbers[1]
  • 可變&不可變
    • let 定義不可變數(shù)組
    • var 定義可變數(shù)組
let array = ["zhangsan", "lisi"]
//: 不能向不可變數(shù)組中追加內(nèi)容
//array.append("wangwu")
var array1 = ["zhangsan", "lisi"]

//: 向可變數(shù)組中追加內(nèi)容
array1.append("wangwu")
  • 數(shù)組的類型
    • Swift中數(shù)組建議存放相同類型的元素
    • 如果初始化時(shí)奸汇,所有內(nèi)容類型不一致,需要手動(dòng)將數(shù)組的類型轉(zhuǎn)換為[Any]
//: array1 僅允許追加 String 類型的值
//array1.append(18)

var array2: [Any] = ["zhangsan", 18]
//: 在 Swift 中往声,數(shù)字可以直接添加到集合擂找,不需要再轉(zhuǎn)換成 `NSNumber`
array2.append(100)

  • 數(shù)組的定義和實(shí)例化
    • 使用 : 可以只定義數(shù)組的類型
    • 實(shí)例化之前不允許添加值
    • 使用 [類型]() 可以實(shí)例化一個(gè)空的數(shù)組
var array3: [String]
//: 實(shí)例化之前不允許添加值
//array3.append("laowang")
//: 實(shí)例化一個(gè)空的數(shù)組
array3 = [String]()
array3.append("laowang")
  • 數(shù)組的合并
    • 必須是相同類型的數(shù)組才能夠合并
    • 開發(fā)中,通常數(shù)組中保存的對(duì)象類型都是一樣的浩销!
array3 += array1

//: 必須是相同類型的數(shù)組才能夠合并贯涎,以下兩句代碼都是不允許的
//array3 += array2
//array2 += array3
  • 數(shù)組的刪除
//: 刪除指定位置的元素
array3.removeAtIndex(3)
//: 清空數(shù)組
array3.removeAll()

字典

  • 定義
    • 同樣使用 [] 定義字典
    • let 不可變字典
    • var 可變字典
    • [String : Any] 是最常用的字典類型
//: [String : Any] 是最常用的字典類型
var dict = ["name": "zhangsan", "age": 18]
  • 賦值
    • 賦值直接使用 dict[key] = value 格式
    • 如果 key 不存在,會(huì)設(shè)置新值
    • 如果 key 存在慢洋,會(huì)覆蓋現(xiàn)有值
//: * 如果 key 不存在塘雳,會(huì)設(shè)置新值
dict["title"] = "boss"
//: * 如果 key 存在,會(huì)覆蓋現(xiàn)有值
dict["name"] = "lisi"
dict
  • 遍歷
    • k普筹,v 可以隨便寫
    • 前面的是 key
    • 后面的是 value
//: 遍歷
for (k, v) in dict {
    print("\(k) ~~~ \(v)")
}
  • 合并字典
    • 如果 key 不存在败明,會(huì)建立新值,否則會(huì)覆蓋現(xiàn)有值
//: 合并字典
var dict1 = [String: NSObject]()
dict1["nickname"] = "大老虎"
dict1["age"] = 100

//: 如果 key 不存在太防,會(huì)建立新值妻顶,否則會(huì)覆蓋現(xiàn)有值
for (k, v) in dict1 {
    dict[k] = v
}
print(dict)

錯(cuò)誤處理

需要了解

  1. 知道錯(cuò)誤處理機(jī)制的三種方式
  2. 開發(fā)中通常使用第二種 try?
  3. 在 Swfit 3.0 中 Foundation 的部分類去除了 NS 前綴

代碼演練,網(wǎng)絡(luò)訪問反序列化數(shù)據(jù)

  • 網(wǎng)絡(luò)訪問代碼
let url = URL(string: "http://www.weather.com.cn/data/sk/101010100.html")!
URLSession.shared.dataTask(with: url, completionHandler: { (data, _, error) in
    if error != nil {
        print(error)
        return
    }
}).resume()
  • JSON反序列化
// 方式1:強(qiáng)try.加載失敗直接崩潰
let dict = try! JSONSerialization.jsonObject(with: data!, options: [])
print(dict)

// 方式2:可選try.反序列化失敗返回nil
let dict = try? JSONSerialization.jsonObject(with: data!, options: [])
print(dict)

// 方式3:默認(rèn)try.返回序列化失敗可以捕捉詳細(xì)信息
do {
    let dict = try JSONSerialization.jsonObject(with: data!, options: [])
    print(dict)
}catch {
    print(error)
}

函數(shù)

目標(biāo)

  • 函數(shù)
    • 定義格式
    • 外部參數(shù)
    • 無返回值的三種情況
  • 閉包
    • 閉包的定義
    • 尾隨閉包
    • 循環(huán)引用
    • OC Block復(fù)習(xí)

函數(shù)

目標(biāo)

  • 掌握函數(shù)的定義
  • 掌握外部參數(shù)的用處
  • 掌握無返回類型的三種函數(shù)定義方式

代碼實(shí)現(xiàn)

  • 函數(shù)的定義
    • 格式 func 函數(shù)名(行參列表) -> 返回值 {代碼實(shí)現(xiàn)}
    • 調(diào)用 let result = 函數(shù)名(值1, 參數(shù)2: 值2...)
//Swfit 3.0
func sum(a: Int, b: Int) -> Int {
    return a + b
}

let result = sum(a:10, b: 20)


//Swfit 2.0
func sum(a: Int, b: Int) -> Int {
    return a + b
}

let result = sum(10, b: 20)
  • 沒有返回值的函數(shù)蜒车,一共有三種寫法
    • 省略
    • ()
    • Void
func demo(str: String) -> Void {
    print(str)
}
func demo1(str: String) -> () {
    print(str)
}
func demo2(str: String) {
    print(str)
}

demo("hello")
demo1("hello world")
demo2("olleh")
  • 外部參數(shù)
    • 在形參名前再增加一個(gè)外部參數(shù)名讳嘱,能夠方便調(diào)用人員更好地理解函數(shù)的語(yǔ)義
    • 格式:func 函數(shù)名(外部參數(shù)名 形式參數(shù)名: 形式參數(shù)類型) -> 返回值類型 { // 代碼實(shí)現(xiàn) }
    • Swift 2.0 中,默認(rèn)第一個(gè)參數(shù)名省略
func sum1(num1 a: Int, num2 b: Int) -> Int {
    return a + b
}

sum1(num1: 10, num2: 20)
  • 函數(shù)的內(nèi)部函數(shù)
func demo() {

        //該內(nèi)部函數(shù)的范圍只能在 demo函數(shù)中可以被訪問
        func sum() {
            print("哈哈哈")
        }

        //必須先聲明內(nèi)部函數(shù)才能調(diào)用調(diào)用
        sum()

    }

函數(shù)格式小結(jié)

// 格式:func 函數(shù)名(形參1: 類型 = 默認(rèn)值, _ 形參2: 類型 = 默認(rèn)值...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:包含默認(rèn)值的函數(shù)可以不用傳遞醇王,并且可以任意組合
//
// 格式:func 函數(shù)名(形參1: 類型, _ 形參2: 類型...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:_ 可以忽略外部參數(shù)呢燥,與其他語(yǔ)言的函數(shù)風(fēng)格更加類似
//
// 格式:func 函數(shù)名(外部參數(shù)1 形參1: 類型, 外部參數(shù)2 形參2: 類型...) -> 返回值 { // 代碼實(shí)現(xiàn) }
// 說明:外部參數(shù)名供外部調(diào)用使用,形參 在函數(shù)內(nèi)部使用
//
// 格式:func 函數(shù)名(形參列表) -> 返回值 { // 代碼實(shí)現(xiàn) }

閉包

與 OC 中的 Block 類似寓娩,閉包主要用于異步操作執(zhí)行完成后的代碼回調(diào)叛氨,網(wǎng)絡(luò)訪問結(jié)果以參數(shù)的形式傳遞給調(diào)用方

目標(biāo)

  • 掌握閉包的定義
  • 掌握閉包的概念和用法
  • 了解尾隨閉包的寫法
  • 掌握解除循環(huán)引用的方法

OC 中 Block 概念回顧

  • 閉包類似于 OC 中的 Block
    • 預(yù)先定義好的代碼
    • 在需要時(shí)執(zhí)行
    • 可以當(dāng)作參數(shù)傳遞
    • 可以有返回值
    • 包含 self 時(shí)需要注意循環(huán)引用

閉包的定義

  • 函數(shù)實(shí)際上是一個(gè)特殊的閉包, 函數(shù)知識(shí)閉包的一種新式
  • 可以結(jié)合函數(shù)的使用 來學(xué)習(xí)閉包
  • 定義一個(gè)函數(shù)在函數(shù)內(nèi)部定義一個(gè)沒有參數(shù)也沒有返回值的內(nèi)部函數(shù)
    func demo1A() {
        func sum() {
            print("哈哈哈")
        }
        sum()

    }

  • 定義一個(gè)閉包 實(shí)現(xiàn)函數(shù)的內(nèi)部函數(shù)一樣的效果
    • 閉包 = { (行參) -> 返回值 in // 代碼實(shí)現(xiàn) }
    • in 用于區(qū)分函數(shù)定義和代碼實(shí)現(xiàn)
//使用閉包實(shí)現(xiàn)
    func demo1B() {
        //定義代碼塊   () -> ()
        //沒有參數(shù)沒有返回值的閉包
        let closure = { () -> Void in
            print("OK")
        }

        //執(zhí)行代碼塊
        closure()
    }
  • 定義一個(gè)函數(shù),內(nèi)部函數(shù)有參數(shù)也有返回值
    func demo3A() {
        func sum(num1 a: Int, num2 b: Int) -> Int{
            return a + b
        }

        let result = sum(num1: 10, num2: 20)
        print(result)
    }
  • 定義一個(gè)函數(shù),實(shí)現(xiàn)上述函數(shù)的內(nèi)部函數(shù)
    func demo3B() {
        let closure = { (num1 a: Int, num2 b: Int ) -> Int in

            return a + b
        }


        let result = closure(num1: 100, num2: 200)
        print(result)
    }
  • 定義一個(gè)閉包 實(shí)現(xiàn)函數(shù)的內(nèi)部函數(shù)一樣的效果
    • 閉包 = { (行參) -> 返回值 in // 代碼實(shí)現(xiàn) }
    • in 用于區(qū)分函數(shù)定義和代碼實(shí)現(xiàn)
  • 最簡(jiǎn)單的閉包,如果沒有參數(shù)/返回值棘伴,則 參數(shù)/返回值/in 統(tǒng)統(tǒng)都可以省略
    • { 代碼實(shí)現(xiàn) }
let closure = {
    print("hello")
}

尾隨閉包

  • 當(dāng)閉包是函數(shù)的最后一個(gè)參數(shù)時(shí),函數(shù)的參數(shù)的小括號(hào)可以提前關(guān)閉,閉包可以寫在小括號(hào)后面當(dāng)做尾隨閉包

閉包的三種簡(jiǎn)寫形式只需要了解即可,在實(shí)際開發(fā)中做到看到不陌生即可,直接使用系統(tǒng)提示的類型

基本使用

GCD 異步

  • 模擬在后臺(tái)線程加載數(shù)據(jù)
func loadData() {
     DispatchQueue.global().async {
            //假裝耗時(shí)
            Thread.sleep(forTimeInterval: 2)
    }
}
  • 尾隨閉包寞埠,如果閉包是最后一個(gè)參數(shù),可以用以下寫法

自定義閉包參數(shù)焊夸,實(shí)現(xiàn)主線程回調(diào)

  • 添加沒有參數(shù)仁连,沒有返回值的閉包
override func viewDidLoad() {
    super.viewDidLoad()

    loadData {
        print("完成回調(diào)")
    }
}

//異步請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)沒在主隊(duì)列中回調(diào) 更新UI

    //TODO: 待會(huì)解釋該關(guān)鍵詞
    //@escaping
    func loadData(a: Int,finished: @escaping (String) -> () ) {
        DispatchQueue.global().async {
            //假裝耗時(shí)
            Thread.sleep(forTimeInterval: 2)
            //獲取結(jié)果
            let res = "老王"
            //在主隊(duì)列中回調(diào)數(shù)據(jù)
            DispatchQueue.main.async {
                //回調(diào)數(shù)據(jù)  只負(fù)責(zé)請(qǐng)求數(shù)據(jù) 不負(fù)責(zé)更新UI
                //從外界傳遞閉包
                finished(res)
            }
        }
    }
  • 添加回調(diào)參數(shù)
override func viewDidLoad() {
    super.viewDidLoad()

    //定義一個(gè)有參數(shù)沒有返回值的閉包當(dāng)做參數(shù)傳遞
        let closure = { (res: String) -> () in
            print("我是請(qǐng)求結(jié)果: \(res)")
        }
        loadData(a:20,finished: closure)

    //使用尾隨閉包的方式滴啊用
        loadData(a: 20) { (res) in
            print(res)
        }
}

循環(huán)引用

  • 建立 NetworkTools 對(duì)象
class NetworkTools: NSObject {

    /// 加載數(shù)據(jù)
    ///
    /// - parameter finished: 完成回調(diào)
    func loadData(finished: () -> ()) {
        print("開始加載數(shù)據(jù)...")

        // ...
        finished()
    }

    deinit {
        print("網(wǎng)絡(luò)工具 88")
    }
}
  • 實(shí)例化 NetworkTools 并且加載數(shù)據(jù)
class ViewController: UIViewController {

    var tools: NetworkTools?

    override func viewDidLoad() {
        super.viewDidLoad()

        tools = NetworkTools()
        tools?.loadData() {
            print("come here \(self.view)")
        }
    }

    /// 與 OC 中的 dealloc 類似,注意此函數(shù)沒有()
    deinit {
        print("控制器 88")
    }
}

運(yùn)行不會(huì)形成循環(huán)引用,因?yàn)?loadData 執(zhí)行完畢后饭冬,就會(huì)釋放對(duì) self 的引用

  • 修改 NetworkTools使鹅,定義回調(diào)閉包屬性
/// 完成回調(diào)屬性
var finishedCallBack: (()->())?

/// 加載數(shù)據(jù)
///
/// - parameter finished: 完成回調(diào)
func loadData(finished: () -> ()) {

    self.finishedCallBack = finished

    print("開始加載數(shù)據(jù)...")

    // ...
    working()
}

func working() {
    finishedCallBack?()
}

deinit {
    print("網(wǎng)絡(luò)工具 88")
}

運(yùn)行測(cè)試,會(huì)出現(xiàn)循環(huán)引用

解除循環(huán)引用

  • 與 OC 類似的方法
/// 類似于 OC 的解除引用
func demo() {
    weak var weakSelf = self
    tools?.loadData() {
        print("\(weakSelf?.view)")
    }
}
  • Swift 推薦的方法
loadData { [weak self] in
    print("\(self?.view)")
}
  • 還可以
loadData { [unowned self] in
    print("\(self.view)")
}

閉包(Block) 的循環(huán)引用小結(jié)

  • Swift

    • [weak self]
      • self是可選項(xiàng)昌抠,如果self已經(jīng)被釋放患朱,則為nil
    • [unowned self]
      • self不是可選項(xiàng),如果self已經(jīng)被釋放炊苫,則出現(xiàn)野指針訪問
  • Objc

    • __weak typeof(self) weakSelf;
      • 如果self已經(jīng)被釋放裁厅,則為nil
    • __unsafe_unretained typeof(self) weakSelf;
      • 如果self已經(jīng)被釋放,則出現(xiàn)野指針訪問

面相對(duì)象

目標(biāo)

  • 構(gòu)造函數(shù)
    • 構(gòu)造函數(shù)的基本概念
    • 構(gòu)造函數(shù)的執(zhí)行順序
    • KVC 在構(gòu)造函數(shù)中的使用及原理
    • 便利構(gòu)造函數(shù)
    • 析構(gòu)函數(shù)
    • 區(qū)分 重載重寫
  • 懶加載
  • 只讀屬性(計(jì)算型屬性)
  • 設(shè)置模型數(shù)據(jù)(didSet

構(gòu)造函數(shù)基礎(chǔ)

構(gòu)造函數(shù)是一種特殊的函數(shù)侨艾,主要用來在創(chuàng)建對(duì)象時(shí)初始化對(duì)象执虹,為對(duì)象成員變量設(shè)置初始值,在 OC 中的構(gòu)造函數(shù)是 initWithXXX唠梨,在 Swift 中由于支持函數(shù)重載袋励,所有的構(gòu)造函數(shù)都是 init

構(gòu)造函數(shù)的作用

  • 分配空間 alloc
  • 設(shè)置初始值 init

必選屬性

  • 自定義 Person 對(duì)象
class Person: NSObject {

    /// 姓名
    var name: String
    /// 年齡
    var age: Int
}

提示錯(cuò)誤 Class 'Person' has no initializers -> 'Person' 類沒有實(shí)例化器s

原因:如果一個(gè)類中定義了必選屬性,必須通過構(gòu)造函數(shù)為這些必選屬性分配空間并且設(shè)置初始值

  • 重寫 父類的構(gòu)造函數(shù)
/// `重寫`父類的構(gòu)造函數(shù)
override init() {

}

提示錯(cuò)誤 Property 'self.name' not initialized at implicitly generated super.init call -> 屬性 'self.name' 沒有在隱式生成的 super.init 調(diào)用前被初始化

  • 手動(dòng)添加 super.init() 調(diào)用
/// `重寫`父類的構(gòu)造函數(shù)
override init() {
    super.init()
}

提示錯(cuò)誤 Property 'self.name' not initialized at super.init call -> 屬性 'self.name' 沒有在 super.init 調(diào)用前被初始化

  • 為比選屬性設(shè)置初始值
/// `重寫`父類的構(gòu)造函數(shù)
override init() {
    name = "張三"
    age = 18

    super.init()
}

小結(jié)

  • 非 Optional 屬性姻成,都必須在構(gòu)造函數(shù)中設(shè)置初始值插龄,從而保證對(duì)象在被實(shí)例化的時(shí)候,屬性都被正確初始化
  • 在調(diào)用父類構(gòu)造函數(shù)之前科展,必須保證本類的屬性都已經(jīng)完成初始化
  • Swift 中的構(gòu)造函數(shù)不用寫 func

子類的構(gòu)造函數(shù)

  • 自定義子類時(shí),需要在構(gòu)造函數(shù)中糠雨,首先為本類定義的屬性設(shè)置初始值
  • 然后再調(diào)用父類的構(gòu)造函數(shù)才睹,初始化父類中定義的屬性
/// 學(xué)生類
class Student: Person {

    /// 學(xué)號(hào)
    var no: String

    override init() {
        no = "001"

        super.init()
    }
}

小結(jié)

  • 先調(diào)用本類的構(gòu)造函數(shù)初始化本類的屬性
  • 然后調(diào)用父類的構(gòu)造函數(shù)初始化父類的屬性
  • Xcode 7 beta 5之后,父類的構(gòu)造函數(shù)會(huì)被自動(dòng)調(diào)用甘邀,強(qiáng)烈建議寫 super.init()琅攘,保持代碼執(zhí)行線索的可讀性
  • super.init() 必須放在本類屬性初始化的后面,保證本類屬性全部初始化完成

Optional 屬性

  • 將對(duì)象屬性類型設(shè)置為 Optional
class Person: NSObject {
    /// 姓名
    var name: String?
    /// 年齡
    var age: Int?
}
  • 可選屬性不需要設(shè)置初始值松邪,默認(rèn)初始值都是 nil
  • 可選屬性是在設(shè)置數(shù)值的時(shí)候才分配空間的坞琴,是延遲分配空間的,更加符合移動(dòng)開發(fā)中延遲創(chuàng)建的原則

重載構(gòu)造函數(shù)

  • Swift 中支持函數(shù)重載逗抑,同樣的函數(shù)名剧辐,不一樣的參數(shù)類型
/// `重載`構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age:  年齡
///
/// - returns: Person 對(duì)象
init(name: String, age: Int) {
    self.name = name
    self.age = age

    super.init()
}

注意事項(xiàng)

  • 如果重載了構(gòu)造函數(shù),但是沒有實(shí)現(xiàn)默認(rèn)的構(gòu)造函數(shù) init()邮府,則系統(tǒng)不再提供默認(rèn)的構(gòu)造函數(shù)
  • 原因荧关,在實(shí)例化對(duì)象時(shí),必須通過構(gòu)造函數(shù)為對(duì)象屬性分配空間和設(shè)置初始值褂傀,對(duì)于存在必選參數(shù)的類而言忍啤,默認(rèn)的 init() 無法完成分配空間和設(shè)置初始值的工作

調(diào)整子類的構(gòu)造函數(shù)

  • 重寫父類的構(gòu)造函數(shù)
/// `重寫`父類構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age:  年齡
///
/// - returns: Student 對(duì)象
override init(name: String, age: Int) {
    no = "002"

    super.init(name: name, age: age)
}
  • 重載構(gòu)造函數(shù)
/// `重載`構(gòu)造函數(shù)
///
/// - parameter name: 姓名
/// - parameter age:  年齡
/// - parameter no:   學(xué)號(hào)
///
/// - returns: Student 對(duì)象
init(name: String, age: Int, no: String) {
    self.no = no

    super.init(name: name, age: age)
}

注意:如果是重載的構(gòu)造函數(shù),必須 super 以完成父類屬性的初始化工作

重載重寫

  • 重載仙辟,函數(shù)名相同同波,參數(shù)名/參數(shù)類型/參數(shù)個(gè)數(shù)不同
    • 重載函數(shù)并不僅僅局限于構(gòu)造函數(shù)
    • 函數(shù)重載是面相對(duì)象程序設(shè)計(jì)語(yǔ)言的重要標(biāo)志
    • 函數(shù)重載能夠簡(jiǎn)化程序員的記憶
    • OC 不支持函數(shù)重載鳄梅,OC 的替代方式是 withXXX...
  • 重寫,子類需要在父類擁有方法的基礎(chǔ)上進(jìn)行擴(kuò)展未檩,需要 override 關(guān)鍵字

KVC 字典轉(zhuǎn)模型構(gòu)造函數(shù)

/// `重寫`構(gòu)造函數(shù)
///
/// - parameter dict: 字典
///
/// - returns: Person 對(duì)象
init(dict: [String: Any]) {
    setValuesForKeysWithDictionary(dict)
}
  • 以上代碼編譯就會(huì)報(bào)錯(cuò)卫枝!

  • 原因:

    • KVC 是 OC 特有的,KVC 本質(zhì)上是在運(yùn)行時(shí)讹挎,動(dòng)態(tài)向?qū)ο蟀l(fā)送 setValue:ForKey: 方法校赤,為對(duì)象的屬性設(shè)置數(shù)值
    • 因此,在使用 KVC 方法之前筒溃,需要確保對(duì)象已經(jīng)被正確實(shí)例化
  • 添加 super.init() 同樣會(huì)報(bào)錯(cuò)

  • 原因:

    • 必選屬性必須在調(diào)用父類構(gòu)造函數(shù)之前完成初始化分配工作
  • 講必選參數(shù)修改為可選參數(shù)马篮,調(diào)整后的代碼如下:

/// 個(gè)人模型
class Person: NSObject {

    /// 姓名
    var name: String?
    /// 年齡
    var age: Int?

    /// `重寫`構(gòu)造函數(shù)
    ///
    /// - parameter dict: 字典
    ///
    /// - returns: Person 對(duì)象
    init(dict: [String: AnyObject]) {
        super.init()

        setValuesForKeysWithDictionary(dict)
    }
}

運(yùn)行測(cè)試,仍然會(huì)報(bào)錯(cuò)

錯(cuò)誤信息:this class is not key value coding-compliant for the key age. -> 這個(gè)類的鍵值 age 與 鍵值編碼不兼容

  • 原因:
    • 在 Swift 中怜奖,如果屬性是可選的浑测,在初始化時(shí),不會(huì)為該屬性分配空間
    • 而 OC 中基本數(shù)據(jù)類型就是保存一個(gè)數(shù)值歪玲,不存在可選的概念
  • 解決辦法:給基本數(shù)據(jù)類型設(shè)置初始值
  • 修改后的代碼如下:
/// 姓名
var name: String?
/// 年齡
var age: Int? = 0

/// `重寫`構(gòu)造函數(shù)
///
/// - parameter dict: 字典
///
/// - returns: Person 對(duì)象
init(dict: [String: Any]) {
    super.init()

    setValuesForKeysWithDictionary(dict)
}

提示:在定義類時(shí)迁央,基本數(shù)據(jù)類型屬性一定要設(shè)置初始值,否則無法正常使用 KVC 設(shè)置數(shù)值

KVC 函數(shù)調(diào)用順序

init(dict: [String: Any]) {
    super.init()

    setValuesForKeysWithDictionary(dict)
}

override func setValue(value: Any?, forKey key: String) {
    print("Key \(key) \(value)")

    super.setValue(value, forKey: key)
}

// `NSObject` 默認(rèn)在發(fā)現(xiàn)沒有定義的鍵值時(shí)滥崩,會(huì)拋出 `NSUndefinedKeyException` 異常
override func setValue(value: Any?, forUndefinedKey key: String) {
    print("UndefinedKey \(key) \(value)")
}
  • setValuesForKeysWithDictionary 會(huì)按照字典中的 key 重復(fù)調(diào)用 setValue:forKey 函數(shù)
  • 如果沒有實(shí)現(xiàn) forUndefinedKey 函數(shù)岖圈,程序會(huì)直接崩潰
    • NSObject 默認(rèn)在發(fā)現(xiàn)沒有定義的鍵值時(shí),會(huì)拋出 NSUndefinedKeyException 異常
  • 如果實(shí)現(xiàn)了 forUndefinedKey钙皮,會(huì)保證 setValuesForKeysWithDictionary 繼續(xù)遍歷后續(xù)的 key
  • 如果父類實(shí)現(xiàn)了 forUndefinedKey蜂科,子類可以不必再實(shí)現(xiàn)此函數(shù)

子類的 KVC 函數(shù)

/// 學(xué)生類
class Student: Person {

    /// 學(xué)號(hào)
    var no: String?
}
  • 如果父類中已經(jīng)實(shí)現(xiàn)了父類的相關(guān)方法,子類中不用再實(shí)現(xiàn)相關(guān)方法

convenience 便利構(gòu)造函數(shù)

  • 條件判斷短条,只有滿足條件导匣,才實(shí)例化對(duì)象,可以防治造成不必要的內(nèi)存開銷
  • 簡(jiǎn)化對(duì)象的創(chuàng)建
  • 本身不負(fù)責(zé)屬性的創(chuàng)建和初始化工作

特點(diǎn)

  • 默認(rèn)情況下茸时,所有的構(gòu)造方法都是指定構(gòu)造函數(shù) Designated
  • convenience 關(guān)鍵字修飾的構(gòu)造方法就是便利構(gòu)造函數(shù)
  • 便利構(gòu)造函數(shù)具有以下特點(diǎn):
    • 可以構(gòu)造失敗 返回 nil,
    • 只有便利構(gòu)造函數(shù)中可以調(diào)用 self.init()
    • 便利構(gòu)造函數(shù)不能被重寫或者 super
/// `便利構(gòu)造函數(shù)`
///
/// - parameter name: 姓名
/// - parameter age:  年齡
///
/// - returns: Person 對(duì)象贡定,如果年齡過小或者過大,返回 nil
convenience init?(name: String, age: Int) {
    if age < 20 || age > 100 {
        return nil
    }

    self.init(dict: ["name": name, "age": age])
}

注意:在 Xcode 中可都,輸入 self.init 時(shí)沒有智能提示

/// 學(xué)生類
class Student: Person {

    /// 學(xué)號(hào)
    var no: String?

    convenience init?(name: String, age: Int, no: String) {
        self.init(name: name, age: age)

        self.no = no
    }
}

便利構(gòu)造函數(shù)應(yīng)用場(chǎng)景

  • 根據(jù)給定參數(shù)判斷是否創(chuàng)建對(duì)象缓待,而不像指定構(gòu)造函數(shù)那樣必須要實(shí)例化一個(gè)對(duì)象出來
  • 在實(shí)際開發(fā)中,可以對(duì)已有類的構(gòu)造函數(shù)進(jìn)行擴(kuò)展汹粤,利用便利構(gòu)造函數(shù)命斧,簡(jiǎn)化對(duì)象的創(chuàng)建

構(gòu)造函數(shù)小結(jié)

  • 指定構(gòu)造函數(shù)必須調(diào)用其直接父類的的指定構(gòu)造函數(shù)(除非沒有父類)
  • 便利構(gòu)造函數(shù)必須調(diào)用同一類中定義的其他指定構(gòu)造函數(shù)或者用 self. 的方式調(diào)用父類的便利構(gòu)造函數(shù)
  • 便利構(gòu)造函數(shù)可以返回 nil
  • 便利構(gòu)造函數(shù)可以被繼承

懶加載

在 iOS 開發(fā)中,懶加載是無處不在的

  • 懶加載的格式如下:
lazy var person: Person = {
    print("懶加載")
    return Person()
}()
  • 懶加載本質(zhì)上是一個(gè)閉包
  • 懶加載的簡(jiǎn)單寫法
lazy var demoPerson: Person = Person()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘱兼,一起剝皮案震驚了整個(gè)濱河市国葬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖汇四,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件接奈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡通孽,警方通過查閱死者的電腦和手機(jī)序宦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來背苦,“玉大人互捌,你說我怎么就攤上這事⌒屑粒” “怎么了秕噪?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)厚宰。 經(jīng)常有香客問我腌巾,道長(zhǎng),這世上最難降的妖魔是什么铲觉? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任澈蝙,我火速辦了婚禮,結(jié)果婚禮上撵幽,老公的妹妹穿的比我還像新娘灯荧。我一直安慰自己,他們只是感情好并齐,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布漏麦。 她就那樣靜靜地躺著,像睡著了一般况褪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上更耻,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天测垛,我揣著相機(jī)與錄音,去河邊找鬼秧均。 笑死食侮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的目胡。 我是一名探鬼主播锯七,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钦椭,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤裆操,失蹤者是張志新(化名)和其女友劉穎副女,沒想到半個(gè)月后阻塑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脐湾,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犁珠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年袱蜡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丝蹭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坪蚁,死狀恐怖奔穿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敏晤,我是刑警寧澤贱田,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站茵典,受9級(jí)特大地震影響湘换,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜统阿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一彩倚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扶平,春花似錦帆离、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至麻献,卻和暖如春们妥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勉吻。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工监婶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人齿桃。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓惑惶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親短纵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子带污,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容