1. 程式人生 > >Swift 擴充套件(extension)

Swift 擴充套件(extension)

擴充套件就是向一個已有的類、結構體、列舉型別或者協議型別新增新功能(functionality)。Swift中的擴充套件就類似於ObjC中的分類(事實上在其他高階語言中更多的稱之為擴充套件而非分類),但是它要比分類強大的多. 
Swift 中的擴充套件可以:
新增計算型屬性和計算型靜態屬性
定義例項方法和型別方法
提供新的構造器
定義下標
定義和使用新的巢狀型別
使一個已有型別符合某個協議
注意:
擴充套件可以對一個型別新增新的功能,但是不能重寫已有的功能。

在 Swift 中,你甚至可以對一個協議(Protocol)進行擴充套件,提供協議需要的實現,或者新增額外的功能能夠對合適的型別帶來額外的好處。

擴充套件語法


宣告一個擴充套件使用關鍵字extension:
extension SomeType {
    // 加到SomeType的新功能寫到這裡
}
新增屬性和方法
//新增屬性和方法  擴充套件Double型別來解決辛巴威貨幣兌換問題.
extension Double{

    //1)擴充套件增加型別屬性
    static var name = "擴充套件Double"
    
    //通用匯率
    static var factor:Double{
           return 12_500_000_000_000
    }
    
    //轉換為人命幣
    var CNY:Double{
        return self / Double.factor
    }
    
    //轉換為美元
    var USD:Double{
        return (self / Double.factor) / 6.7
    }
    
    //轉換為歐元
    var EUR:Double{
        return (self / Double.factor) / 8.8
    }
    
    //統計當前貨幣中零的個數
    var Zeros:Int{
    
        let strValue = "\(self)"
        //這裡使用 UTF8字符集統計字串的長度
        return strValue.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) - 3
    }
    
    //增加例項方法
    func description() ->String{
        return Double.name
    }
    
    //2)擴充套件增加型別方法
    static func whoAmI() ->String{
        return "我是\(name) 我的型別是\(self)"
    }

}

func testOne(){
    
        var money:Double = 100_000_000_000_000
        print("辛巴威的貨幣: \(money)")
        print("辛巴威貨幣兌換為人命幣: \(money.CNY)")
        print("辛巴威的貨幣兌換為美元: \(money.USD)")
        print("辛巴威的貨幣兌換為歐元: \(money.EUR)")
        print("當前的貨幣是: \(money.description)")
        
        print(Double.whoAmI())
        print("當前的辛巴威貨幣含有 \(money.Zeros)")
        print("當前的辛巴威貨幣含有: \(100_000_000_000_000.Zeros)")
        
        /*
        
        辛巴威的貨幣: 100000000000000.0
        辛巴威貨幣兌換為人命幣: 8.0
        辛巴威的貨幣兌換為美元: 1.19402985074627
        辛巴威的貨幣兌換為歐元: 0.909090909090909
        當前的貨幣是: 100000000000000.0
        我是擴充套件Double 我的型別是Double
        當前的辛巴威貨幣含有 14
        當前的辛巴威貨幣含有 14
        
        程式碼分析:
        
        1:上述程式碼部分,我們使用了extension擴充套件了Swift內建型別 Double的功能.
        2:添加了靜態計算屬性,如factor屬性.添加了計算屬性CNY,EUR等.
        3:extension不能夠擴充套件例項的儲存屬性,比如我們的name屬性,如果去掉static,則會報錯,但是我們可以增加型別的儲存屬性.
        4:我們為Double增加了型別方法( whoAmI())也增加了例項方法(description()).
        5:擴展出來的屬性和方法不僅可以通過變數名呼叫,甚至可以直接使用字面量.如100_000_000_000_000.Zeros.
        6:擴充套件不能夠給已有屬性增加屬性觀察器.
        
        */
        
} 
新增構造器
其實構造器就是一個特殊的方法,所以擴充套件構造器方法就是新增方法,不過要注意兩點:
   1:如果擴充套件類型別,我們只能夠增加便捷構造器,而不能夠增加指定構造器和析構器.
   2:對於值型別,比如結構體,列舉,在擴充套件其構造器時,務必保證所有的屬性都被初始化.

//守夜人的佩劍
struct Sword{

    var length = 0.5
    var materials:String = "由普通鋼鑄造而成"
}

//守夜人類
class NightWatcher{

    var sword:Sword
    var name:String
    
    //使用指定構造器初始化屬性
    init(name: String,sword:Sword){
        self.name = name
        self.sword = sword
    }
    
    //便捷構造器
    convenience init(){
        self.init(name:"守夜人",sword:Sword())
    }
}

//擴充套件劍的質量 凡是"異鬼剋星"都使用龍晶打造,只有龍晶寶劍可以殺死異鬼
extension Sword{

    init(brand:String){
    
        if brand == "異鬼剋星"{
            //呼叫結構體Sword的成員構造器
            self.init(length:1 , materials: "由龍晶鑄造")
        }else{
            //呼叫結構體Sword的預設構造器,注意:只有結構體的所有屬性都有預設值時才可以使用其預設構造器
            self.init()
        }
    }
    
    var description:String{
        return "長\(length)米 \(materials)"
    }
}

//擴充套件NightWatcher類
extension NightWatcher{

    //擴充套件類的構造器,只能夠擴充套件便捷構造器,所以如果刪除前面的convenience將報錯
    convenience init(name:String){
    
        if name == "Tarly"{
            //呼叫該類固有的指定構造器
            self.init(name: name, sword:Sword(brand: "異鬼剋星"))
        }else{
            //呼叫便捷構造器
            self.init()
        }
    }
    
    func say(){
        print(" 我是\(name) 我的劍\(sword.description)")
    }
    
}
func testTwo(){
    
        //路人甲出場
        var somebody = NightWatcher()
        somebody.say()
        
        //tarly出場
        var tarly = NightWatcher(name: "Tarly")
        tarly.say()
        
        /*
        我是守夜人 我的劍長0.5米 由普通鋼鑄造而成
        我是Tarly 我的劍長1.0米 由龍晶鑄造

        注意:
        在擴充套件的便捷構造器內可以自由使用其原有的指定構造器和便捷構造器.
        擴充套件的結構體Sword的構造器.此處沒有什麼指定構造器和便捷構造器的說法,並且依然可以呼叫其成員構造器和預設構造器.
        */
} 

下標(subscript)

extension Int {
    subscript(var digitIndex: Int) -> Int {
        var decimalBase = 1
        while digitIndex > 0 {
            decimalBase *= 10
            --digitIndex
        }
        return (self / decimalBase) % 10
    }
}
//擴充套件可以向一個已有型別新增新下標。這個例子向Swift內建型別Int添加了一個整型下標。該下標[n]返回十進位制數字從右向左數的第n個數字.
func testThree(){
        
        print(746381295[0])// returns 5
        print(746381295[1])// returns 9
        print(746381295[2])// returns 2
        print(746381295[8])// returns 7
        
        //如果該Int值沒有足夠的位數,即下標越界,那麼上述實現的下標會返回0,因為它會在數字左邊自動補0:
  
         print(746381295[9])//returns 0, 即等同於:
         print(0746381295[9])//returns 0
        
    
 }

看一個小例子
import UIKit

//建立一個Person
class Person{
    
    var firstName:String,lastName:String
    var age = 0
    var fullName:String{
        get{
            return firstName + lastName
        }
    }
    
    init(firstName:String,lastName:String){
        self.firstName = firstName
        self.lastName = lastName
        
    }
    
    func showMessage(){
        print("name:\(fullName),age:\(age)")
    }
}

//對Person類進行擴充套件
extension Person{
    
    //只能擴充套件便利構造方法,不能擴充套件指定構造方法.
    convenience init(){
        self.init(firstName:"",lastName:"")
    }
    
    //只能擴充套件計算屬性,無法擴充套件儲存屬性.對於屬性不理解可以參考http://swiftcn.io/doc/chapter2/10_Properties.html
    var personInfo:String{
        return "firstName=\(firstName),lastName=\(lastName),age=\(age)"
    }
    
    //擴充套件例項方法
    func showHello(){
        print("Nice to meet you!")
    }
    
    //巢狀型別
    enum SkinColor{
         case Yellow,White,Black
    }
    
    //擴充套件型別方法
    static func skins() -> [SkinColor]{
        return [.Yellow,.White,.Black]
    }
}

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let p = Person()
        p.firstName = "ShiHua"
        p.lastName = "Long"
        p.age = 24
        
        print(p.personInfo)
        p.showHello()
        print(Person.skins())
        
    }
    
}