1. 程式人生 > >Swift和OC的區別(基於Swift3.0)

Swift和OC的區別(基於Swift3.0)

Swift無疑是iOS程式設計師的未來,但是目前蘋果公司還在不端更新和改變Swift,甚至在語法層面還在不斷探索,雖然xcode提供一些直接適配新版本Swift語法的快捷鍵,但給我們學習不免造成一些影響,這裡是基於Swift3.0和OC的一些基礎性知識的比較。

一. 基礎部分

1.Swift的型別是在C和Objective-C的基礎上提出的,Int是整型;Double和Float是浮點型;Bool是布林型;String是字串。Swift還有兩個有用的集合型別,Array和Dictionary,(型別第一個字母大寫)

2.Swift還增加了Objective-C中沒有的型別比如元組(Tuple)。元組可以讓你建立或者傳遞一組資料,比如作為函式的返回值時,你可以用一個元組可以返回多個值。

元組(tuples)把多個值組合成一個複合值。元組內的值可以使任意型別,並不要求是相同型別。

let http404Error =(404,"Not Found")

(404,”Not Found”)元組把一個Int值和一個String值組合起來表示HTTP狀態碼的兩個部分:一個數字和一個人類可讀的描述。這個元組可以被描述為“一個型別為(Int,String)的元組”。

你可以把任意順序的型別組合成一個元組,這個元組可以包含所有型別。只要你想,你可以建立一個型別為(Int,Int,Int)或者(String,Bool)或者其他任何你想要的組合的元組。

你還可以通過下標來訪問元組中的單個元素,下標從零開始:

println("The status code is \(http404Error.0)")

你可以在定義元組的時候給單個元素命名:

let http200Status =(statusCode: 200,description: "OK")

給元組中的元素命名後,你可以通過名字來獲取這些元素的值:

println("The status code is \(http200Status.statusCode)")

注意:元組在臨時組織值的時候很有用,但是並不適合建立複雜的資料結構。如果你的資料結構並不是臨時使用,請使用類或者結構體而不是元組。

3.Swift還增加了可選(Optional)型別,用於處理值缺失的情況。可選表示“那兒有一個值,並且它等於x”或者“那兒沒有值”。可選有點像在Objective-C中使用nil,但是它可以用在任何型別上,不僅僅是類。可選型別比Objective-C中的nil指標更加安全也更具表現力,它是Swift許多強大特性的重要組成部分。

Swift定義的常量和變數是沒有預設值的,所以引入了可選的概念,用?修飾變數,標示該變數的值可能為空也可能為某個值,然後獲取的時候用!強制解析,此時必須有值,否則報執行時錯誤。

比如你定義一個String型別的變數

var str:String?
str="ass"
print("str=\(str!)")

如果不使用!強制解析,可以使用可選繫結(optional binding)來判斷可選是否包含值,如果包含就把值賦給一個臨時常量或者變數。

if let realstr = str {
    print("realstr=\(realstr)")
} else {
    print("str為空")
}

有的函式的返回值就是可選型別,預設是使用?修飾的,也需要!強制解析或者可選繫結解析。

let convertedNumber = possibleNumber.toInt()

// convertedNumber被推測為型別"Int?",或者型別"optional Int"
ifconvertedNumber {
    println("\(convertedNumber!)")
} else {
    print("could not be converted")
}

隱式解析可選

有時候在程式架構中,第一次被賦值之後,可以確定一個可選總會有值。在這種情況下,每次都要判斷和解析可選值是非常低效的,因為可以確定它總會有值。

這種型別的可選被定義為隱式解析可選(implicitly unwrapped optionals)。把想要用作可選的型別的後面的問號(String?)改成感嘆號(String!)來宣告一個隱式解析可選。

let possibleString: String?= "An optional string."
print(possibleString!)   // 需要驚歎號來獲取值

//輸出"An optional string."
let assumedString: String! = "An implicitly unwrapped optional string."

print(assumedString)//不需要感嘆號
//輸出"An implicitly unwrapped optional string."

自判斷連結

自判斷連結(Optional Chaining)是一種可以請求和呼叫屬性、方法及子指令碼的過程,它的自判斷性體現於請求或呼叫的目標當前可能為空(nil)。如果自判斷的目標有值,那麼呼叫就會成功;相反,如果選擇的目標為空(nil),則這種呼叫將返回空(nil)。多次請求或呼叫可以被連結在一起形成一個鏈,如果任何一個節點為空(nil)將導致整個鏈失效。

如果用!解析一個可選型別為空時會報執行時異常

如果改用?解析就可以避免

print(stu.room!.person.name)

room==nil所以報執行時錯誤

print(stu.room?.person.name)

就列印nil

更正規的寫法可以使用if let
如果numberOfRooms為Int型別,john.residence?.numberOfRooms就是Int?型別

if let roomCount = john.residence?.numberOfRooms {

} else {

}

如果printNumberOfRooms()返回值為Void型別, john.residence?.printNumberOfRooms()就是Void?型別

if john.residence?.printNumberOfRooms(){

} else {

}

4.常量的值一旦設定就不能改變,而變數的值可以隨意更改。常量和變數必須在使用前宣告,用let來宣告常量,用var來宣告變數。所謂改變,對於基本資料型別而言,值改變就是改變,而對於引用資料型別而言,指標指向改變才是改變。

Swift中整數(Integer)、浮點數(floating-point)、布林值(Booleans)、字串(string)、陣列(array)和字典(dictionaries),都是值型別,並且都是以結構體的形式在後臺所實現。

eg: 
var arr=[“a”,”b”,”c”]
var arrcopy =arr
//此時arr-“a,b,c”arrcopy-“abc”
arrcopy[0]=“fuck”
//此時arr-“a,b,c”arrcopy-“fuck,b,c”
class Person {
    varname:String=“a”
}

var arr=[person1,person2,person3]
var arrcopy =arr
//此時arr,arrcopy指向一塊區域,一同改變

5.類型別名(type aliases)就是給現有型別定義另一個名字。你可以使用typealias關鍵字來定義類型別名。

當你想要給現有型別起一個更有意義的名字時,類型別名非常有用。假設你正在處理特定長度的外部資源的資料:

typealias AudioSample = UInt16

定義了一個類型別名之後,你可以在任何使用原始名的地方使用別名:

varmaxAmplitudeFound = AudioSample.min

6.控制流

兩種形式的for迴圈

// 傳統獲取index的語法

for i in 010 {

}
// 快速訪問物件的語法
for obj in arr {

}

二.函式

普通函式定義和使用

func add(param1:String,param2:String)->String {
    return param1+param2
}

print(add(param1: "w",param2: "q"))

可以給引數新增預設值,使用時也可以不傳此引數

func add2(param1:String,param2:String="q")->String {
        return param1+param2
}

print(add2(param1: "w"))

inout修飾函式引數

變數形參只能在函式本身內改變。如果你想讓函式改變形參值,並想要在函式呼叫結束後保持形參值的改變,那你可以把形參定義為in-out形參。

func change(param:inout String) {
        param="wowo"
}

var kaka:String="kaka"

change(param: &kaka)

print(kaka)  // 此時kaka=“wowo”

複雜函式定義和使用

函式可以作為形參和另一個函式的返回值

三.類

class Person: NSObject {

    //普通屬性方法定義
    var name:String?

    func sayHi(mes:String)->String{

    return "person\(mes)"
}
// 靜態屬性方法定義(屬性用static修飾,方法用static或class修飾)
static var age:String?

class func sayMessage(){
    print("i am person\(age!)")
}

屬性

lazy修飾的屬性必須初始化它會在使用該屬性的時候才去執行初始化的程式碼一般用在消耗不少時間的變數上比如檔案開啟

lazy特性;區域性範圍的常量或變數不會延遲計算。

set get方法

// 既有set又有get方法
var firstName:String {
    get{
        return "first"+self.name!
    }

    //newValue代表set引數值
    set{
        self.name="setName:"+newValue
    }
}

// 只有get方法
var lastName:String {
    return "last"+self.name!
}

屬性監視器

willSet didSet方法

willSet在設定新的值之前呼叫

didSet在新的值被設定之後立即呼叫
// 需要設定預設值
var idcard:Double = 0.0 {
    willSet{
            print("willSet")
    }

    didSet{
        print("didSet")
    }
}

繼承

如果子類實現和父類相同的方法或者屬性就是重寫。必須用override關鍵字表明。

重寫override方法

class Student: Person {
    override func sayHello(mes: String)-> String {
        //可以使用super關鍵字呼叫父類方法
        return super.sayHello(mes:mes)
    }
}

重寫override屬性

override var name: String?{
    set {

    }

    get{
        return super.name
    }
}
原則:你可以將一個繼承來的只讀屬性重寫為一個讀寫屬性,只需要你在重寫版本的屬性裡提供getter和setter即可。但是,你不可以將一個繼承來的讀寫屬性重寫為一個只讀屬性。

防止重寫

你可以通過把方法,屬性或附屬指令碼標記為final來防止它們被重寫

構造方法

與Objective-C中的構造器不同,Swift的構造器無需返回值,它們的主要任務是保證新例項在第一次使用前完成正確的初始化。

構造器分位指定構造器和便利構造器

原則:指定構造器是向上呼叫的,便利構造器是橫向呼叫的.

規則1:指定構造器必須呼叫其父類的指定構造器

規則2:便利構造器必須呼叫同一類中呼叫的其他構造器

規則3:便利構造器必須以呼叫一個指定構造器結束
// 指定構造器(預設構造方法),因為父類也有該方法,所以用override
override init() {
    super.init()
    print("Student init")
}
// 指定構造器(自定義構造方法,父類沒有該方法,不需要override)

init(size:Double){
    super.init(size: size)
    print("Student init with size")
}
// 便利構造器(使用關鍵詞convenience,需要呼叫自己的構造方法)
convenience init(size:Double,friendNums:Double){
    self.init(size:size)
    self.friendNums=friendNums
}

類擴充套件

相當於OC分類

Swift中的擴充套件可以:

1.新增計算型屬性和計算靜態屬性

注意:擴充套件可以新增新的計算屬性,但是不可以新增儲存屬性,也不可以向已有屬性新增屬性觀測器(property observers)
2.定義例項方法和型別方法
3.提供新的構造器
4.定義下標
5.定義和使用新的巢狀型別
6.使一個已有型別符合某個介面
extension Room {

    var newSize:Double {
        return self.size+100
    }

    func sayExtensionRoom(){
        print("sayExtensionRoom\(self.newSize)")
    }
}

協議

四.自動引用計數

總體基本和OC一致,在解決迴圈引用的問題有點區別。

可以使用弱引用weak和無主引用unowned修飾一方變數。

1.兩個屬性的值都可能是nil,並有可能產生強引用環。這種場景下適合使用弱引用。

var a:A?

weak b:B?
2.一個屬性可以是nil,另外一個屬性不允許是nil,並有可能產生強引用環。這種場景下適合使用無主引用。

var a:A?
unowned b:B

override init(){
    self.b=B()
}
3.兩個屬性都必須有值,且初始化完成後不能為nil。這種場景下,則要一個類用無主引用屬性,另一個類用隱式展開的可選屬性。

var a:A!

override init(){
    self.a=A()
}


unowned b:B

override init(){
    self.b=B()
}

五.型別轉換

檢查型別

用型別檢查操作符(is)來檢查一個例項是否屬於特定子型別。型別檢查操作符返回true若例項屬於那個子型別,若不屬於返回false。

library中新增Movie和Song

for item in library {
    if item is Movie {
        ++movieCount
    } else if item is Song {
        ++songCount
    }
}

向下轉型(簡稱下轉)

某型別的一個常量或變數可能在幕後實際上屬於一個子類。你可以相信,上面就是這種情況。你可以嘗試向下轉到它的子型別,用型別轉換操作符(as)

如果強制轉型肯定成功,可以使用as!,如果使用as?,那麼返回結果成功則為該轉的型別,失敗則為空,此時可以使用if let來接收判斷

Any和AnyObject的轉換

Swift為不確定型別提供了兩種特殊類型別名:

  1. AnyObject可以代表任何class型別的例項。

  2. Any可以表示任何型別(基礎型別和class型別),除了方法型別(function types)。

六.閉包

Swift中的閉包與C和Objective-C中的blocks有點類似。

閉包表示式語法

{(parameters)-> returnType in
    statements
}

捕獲

閉包可以在其定義的上下文中捕獲常量或變數。即使定義這些常量和變數的原作用域已經不存在,閉包仍然可以在閉包函式體內引用和修改這些值。

// makeIncrementor函式返回值是一個()->Int型別的函式
func makeIncrementor(amount: Int)->()-> Int {

    var runningTotal = 0

    func incrementor()-> Int {
        runningTotal += amount
        return runningTotal
    }

    return incrementor
}
// 閉包捕獲amount引數的值,然後每次都可以使用

let incrementByTen = makeIncrementor(amount: 10)

print(incrementByTen())//10

print(incrementByTen())//20

print(incrementByTen())//30

七.應用

Swift讀取檔案為String型別

let path:String = Bundle.main.path(forResource: "demo2.js",ofType:nil)!

if let spath = try?String.init(contentsOf: URL.init(fileURLWithPath: path),encoding:String.Encoding.utf8) {

} else {

}