本文首發地址
請在閱讀本文章時,順手將文中的示例程式碼在playground中敲一遍,這樣能加深理解!!!
閱讀該文章大約需要:15分鐘
讀完之後你能獲得:
1、Extension是什麼
2、它能做什麼
本文全部內容基於Swift版本:3.0.1
Extension的基本語法
extension SomeType {
// new functionality to add to SomeType goes here
}複製程式碼
Tip:擴充套件可以為一個型別新增新的功能,但是不能重寫已有的功能。
struct Student {
var name = ""
var age = 1
func print() {
}
}
extension Student {
//改行會報錯`invalid redeclaration print()`重複宣告print(),重寫變數也是不行的。
func print() {
}
}複製程式碼
Swift中的Extension可以做什麼
我們想知道Extension在Swift中能做些什麼,最直接的方法就是檢視Swift的官方文件了。下面是文件中指出Extension能做的六個方面。
- 新增計算型例項屬性和計算型型別屬性
- 定義例項方法和型別方法
- 提供新的構造器
- 定義下標
- 定義和使用新的巢狀型別
- 使已存在的型別遵守某個協議
看完上面Extension能做的六個方面,我們來逐條解釋說明一下:
新增計算型例項屬性和計算型型別屬性
首先我們要了解什麼是計算型屬性,計算型屬性(computed property)不直接儲存值,而是提供一個getter
和一個可選的setter
,來間接獲取和設定其他屬性或變數的值。關於更多的計算型屬性的內容請自行檢視官方文件,在此不再贅述。那麼我們什麼場景可以用到這個功能呢?舉一個最常見的例子,當你想訪問某個view的width的時候,通常情況下你會這麼寫:
view.frame.size.width複製程式碼
但是這樣寫很長很不方便,作為一個懶惰的程式設計師,這時候你就要想我能不能縮短訪問該屬性的程式碼。不要猶豫了騷年,這時候你只需要寫一個UIView的Extension就可以達到你的目的。
extension UIView {
var x: CGFloat {
set {
self.frame.origin.x = newValue
}
get {
return self.frame.origin.x
}
}
var y: CGFloat {
set {
self.frame.origin.y = newValue
}
get {
return self.frame.origin.y
}
}
var width: CGFloat {
set {
self.frame.size.width = newValue
}
get {
return self.frame.size.width
}
}
var height: CGFloat {
set {
self.frame.size.height = newValue
}
get {
return self.frame.size.height
}
}
}複製程式碼
這樣你就可以通過來訪問該屬性。怎麼樣,有沒有感受到Extension的便利之處。
view.width複製程式碼
定義例項方法和型別方法
在你辛辛苦苦寫完一個Student類後,萬惡的產品過來告訴你需求改了,這時雖然你心中有一萬隻草泥馬在奔騰,但是為了心中那份神聖的程式設計師的責任感(當然還有餬口的工資),你還是要修改程式碼。如果你想在不改變原始類的基礎上新增功能,那你可以給Student類新增Extension來解決問題。
Tip:這裡值得注意的一點是在Swift中,Extension可以給類和型別新增,比如你也可以給一個struct新增Extension,而在Objective-C中,你只能給類新增Extension。
class Student {
var name = ""
var age = 1
}
extension Student {
func printCurrentStudentName() {
print(self.name)
}
}
var jack = Student()
jack.name = "jack"
jack.printCurrentStudentName()複製程式碼
提供新的構造器(Initializers)
最常見的Rect通常由origin
和size
來構造初始化,但是如果在你寫完Rect的定義後,你偏偏想要通過center和size來確定Rect(作為一個程式設計師就要有一種作死的精神),那你就要用Extension來給Rect提供一個新的構造器。關於更多關於構造器的資訊,請參考官方文件
本例子來源官方文件
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))複製程式碼
通過Extension來給Rect新增一個新的構造器。
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}複製程式碼
這樣你就可以通過新的構造器來初始化Rect。
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)複製程式碼
定義下標
通過Swift中的Extension,你可以給已知型別新增下標。例如下面的例子就是給Int
型別新增一個下標,該下標表示十進位制數從右向左的第n個數字。
本例子來源官方文件
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
746381295[0]
// 5
746381295[1]
// 9複製程式碼
定義和使用新的巢狀型別(nest type)
Extensions可以給已知的類、結構體、列舉新增巢狀型別。下面的例子是給Int型別新增一個判斷正負數的Extension,該Extension巢狀一個列舉。
本例子來源官方文件
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + "複製程式碼
使已存在的型別遵守某個協議
編寫使該型別遵守某個協議的Extension的語法如下:
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}複製程式碼
示例程式碼:
protocol StudentProtocol {
var address: String { get }
}
struct Student {
var name = ""
var age = 1
}
extension Student: StudentProtocol {
var address: String {
return "address"
}
}
var jack = Student()
jack.address
//輸出 address複製程式碼
若新增Extension的型別已經實現協議中的內容,你可以寫一個空的Extension來遵守協議:
protocol StudentProtocol {
var address: String { get }
}
struct Student {
var address: String {
return "address"
}
var name = ""
var age = 1
}
extension Student: StudentProtocol {}
var jack = Student()
jack.address
//輸出 address複製程式碼
總結
- Extension可以為一個已有的類、結構體、列舉型別或者協議型別新增新功能。
- 可以在沒有許可權獲取原始原始碼的情況下擴充套件型別的內容
- Extendion和Objective-C中的Category類似。(OC中的Category有名字,Swift中的擴充套件沒有名字)
下篇預告:Swift-Protocol
若本文有何錯誤或者不當之處,還望不吝賜教。謝謝!