Swift Extensions
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
1. Extension vs. Inheritance π©βπ»
κΈ°μ‘΄μ Types λ₯Ό νμ₯νκΈ° μν λ°©λ² μ€ νλμΈ Inheritance λ
Class μμλ§ μ¬μ©ν μ μλ€.
Inheritance λ κΈ°μ‘΄ Class λ κ·Έλλ‘ λ μ± λ³λμ Class λ₯Ό μμ±νλ©°, μ΄λ€μ Superclass/Subclass λΌλ κ΄κ³λ‘ μ°κ²°λ
Hierarchy ꡬ쑰
λ₯Ό κ°λλ€. Subclass λ κΈ°μ‘΄μ Superclass μ κΈ°λ₯μ μΆκ°ν΄ νμ₯
νλ κ² λΏ μλλΌ
μ΄λ―Έ μ‘΄μ¬νλ κΈ°λ₯μ Overriding
νλ κ²λ κ°λ₯νλ€.
Extension
μ Class, Structure, Enumeration, Protocol νμ
μμ μ¬μ©μ΄ κ°λ₯νλ©° Extensions κ° ν μ μλ κ²μ λ€μκ³Ό κ°λ€.
- Add computed instance properties and computed type properties
- Define instance methods and type methods
- Provide new initializers
- Define subscripts
- Define and use new nested types
- Make an existing type conform to a protocol
Extension μ Inheritance μ λ§μ°¬κ°μ§λ‘ κΈ°μ‘΄μ μ‘΄μ¬νλ νμ μ κΈ°λ₯μ μΆκ°ν μ μλ€. κ·Έλ¦¬κ³ Extension μ΄ κ°λ νΉμ§μΌλ‘ Inheritance μ λ€λ₯Έμ μ λ€μκ³Ό κ°λ€.
- Original source code μ μ κ·Ό κΆνμ΄ μλ κ²½μ°μλ Extension μ΄ κ°λ₯νλ€.
μ΄λ₯Ό
Retroactive Modeling
(μκΈ λͺ¨λΈλ§) μ΄λΌ νλ€. - Extension μ Inheritance μ λ¬λ¦¬ Stored Properties, Property Observers λ νμ₯μ΄ λΆκ°λ₯νλ€.
μ€μ§ Computed Instance Properties μ Computed Type Properties λ§ νμ₯ κ°λ₯νλ€. - Extension μ κΈ°λ₯μ μΆκ°λ§ κ°λ₯ν λΏ Inheritance μ λ¬λ¦¬
Overriding μ΄ λΆκ°λ₯
νλ€.
Swift μ Extensions λ Objective-C μ Categories μ μ μ¬νλ€. λ¨, Extensions λ μ΄λ¦μ κ°μ§ μλλ€.
2. Extension Syntax π©βπ»
Syntax
extension SomeType {
// new functionality to add to SomeType goes here
}
Extension μ νλ μ΄μμ Protocol
μ μ±νν΄ κΈ°μ‘΄μ νμ
μ νμ₯ν μ μλ€.
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
μ΄λΏ μλλΌ Generic Type
μ νμ₯νλ κ² μμ κ°λ₯νλ€.
3. Computed Properties π©βπ»
Extensions λ₯Ό μ΄μ©ν΄ Computed Instance Properties
λλ Computed Type Properties
λ₯Ό νμ₯νλ κ²μ΄ κ°λ₯νλ€. μ΄κ²μ
μ¬μ©μκ° μ μν νμ
λΏ μλλΌ Built-in Types λ₯Ό νμ₯νλ κ²μ ν¬ν¨
νλ€.
λ€μ μμ λ TypeScript κ° Prototype μ μ΄μ©ν΄ Built-in Types μ κΈ°λ₯μ μΆκ°νλ― λ€μν κΈΈμ΄ λ¨μλ₯Ό βmeterβ λ¨μλ‘ λ³κ²½νκΈ° μν΄ Double μ 5κ°μ Computed Instance Properties λ₯Ό μΆκ°νλ€.
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters") // One inch is 0.0254 meters
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters") // Three feet is 0.914399970739201 meters
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long") // A marathon is 42195.0 meters long
Extensions λ Computed Instance Properties λ Computed Type Properties λ₯Ό μΆκ°νλ κ²λ§ κ°λ₯νλ€.
Stored Properties λ₯Ό μΆκ°νκ±°λ, μ΄λ―Έ μ‘΄μ¬νλ Properties μ Property Observers λ₯Ό μΆκ°νλ κ²μ λΆκ°λ₯νλ€.
4. Initializers π©βπ»
κΈ°μ‘΄μ Value Types
κ° λͺ¨λ Stored Properties μ default values λ₯Ό μ 곡νκ³ , Initializers λ₯Ό ꡬννμ§ μμ
Default Initializers & Memberwise Initializers μ
쑰건μ λͺ¨λ λ§μ‘±νλ©΄, Extension μ Initializer μμ Default Initializers
μ Memberwise Initializers
λ₯Ό μ¬μ©νλ κ²μ΄ κ°λ₯νλ€.
μ¦, Default Initializers μ Memberwise Initializers λ₯Ό μ μ§νλ©΄μ μ¬μ©μ μ μ Initializers λ₯Ό μΆκ°ν μ μλ€. λμ λ€λ₯Έ Instances λ€μ΄ Initializer Extensions μΌλ‘ μΈν΄ μν₯μ λ°μ§ μκ³ μ μμ μΌλ‘ μ΄κΈ°ν λμ΄ instance κ° μμ±λλμ§ νμΈν΄μΌν μ± μμ΄ λ°λ₯Έλ€.
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()
}
Rect
λ Default Initializers μ Memberwise Initializers 쑰건μ λ§μ‘±νλ―λ‘ Default Initializers μ
Memberwise Initializers κ° μλμΌλ‘ μμ±λλ€.
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
Extensions λ Convenience Initializers λ₯Ό μΆκ°νλ κ²λ§ κ°λ₯νλ€.
Designated Initializers λ Deinitializers λ₯Ό μΆκ°νλ κ²μ λΆκ°λ₯νλ€.
1 ) Without Initializer Extensions
μ Rect
μ μ€μ¬μ κ³Ό ν¬κΈ°λ₯Ό λ°μ Rect instance λ₯Ό μμ±νλ μ Initializers λ₯Ό μΆκ°ν΄λ³΄μ. κ·Έ Initializers λ λ€μκ³Ό κ°μ κ²μ΄λ€.
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)
}
νμ§λ§ Initializer Delegation for Value Types
μμ μ΄ν΄λ³Έ κ²μ²λΌ init(center:size:)
λ₯Ό μΆκ°νλ μκ° Default Initializers μ Memberwise Initializers λ₯Ό μλ μμ±νλ
쑰건μ λ§μ‘±νμ§ μμΌλ―λ‘ μ΄μ νμν Initializers λ₯Ό λͺ
μμ μΌλ‘ ν¨κ» μμ±ν΄μΌνλ€.
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
μ Initializers λ₯Ό μΆκ°ν΄ μ½λλ₯Ό μμ±μμΌλ³΄μ.
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()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
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)
}
}
let basicRect = Rect()
let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
printRect(basicRect) // The origin is (0.0, 0.0) and its size is (0.0, 0.0)
printRect(originRect) // The origin is (2.0, 2.0) and its size is (5.0, 5.0)
printRect(centerRect) // The origin is (2.5, 2.5) and its size is (3.0, 3.0)
func printRect(_ rect: Rect) {
print("The origin is (\(rect.origin.x), \(rect.origin.y)) and its size is (\(rect.size.width), \(rect.size.height))")
}
2 ) With Initializer Extensions
μ΄μ Rect
μ Extensions λ₯Ό μ΄μ©ν΄ Initializers λ₯Ό μΆκ°ν΄λ³΄μ.
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)
}
}
Extensions μ λ΄λΆμμ Initializer Delegation
μ νκΈ° μν΄ Memberwise Initializers λ₯Ό μ¬μ©νλ€. νμ§λ§ κΈ°μ‘΄μ Rect
λ
μ΄λ₯Ό μν΄ Default Initializers μ Memberwise Initializers λ₯Ό λͺ
μμ μΌλ‘ μμ±ν νμκ° μλ€.
struct Rect {
var origin = Point()
var size = Size()
}
Original Structures Rect
λ μ¬μ ν μλ¬΄λ° Initializers μ ꡬνμ νμλ‘ νμ§ μλλ€.
Extensions λ₯Ό μ΄μ©ν Initializer Extensions μ μ½λλ₯Ό μμ±μμΌλ³΄μ.
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))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
printRect(defaultRect) // The origin is (0.0, 0.0) and its size is (0.0, 0.0)
printRect(memberwiseRect) // The origin is (2.0, 2.0) and its size is (5.0, 5.0)
printRect(centerRect) // The origin is (2.5, 2.5) and its size is (3.0, 3.0)
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)
}
}
func printRect(_ rect: Rect) {
print("The origin is (\(rect.origin.x), \(rect.origin.y)) and its size is (\(rect.size.width), \(rect.size.height))")
}
- Without Extensions : μ¬μ©μ μ μ Initializers λ₯Ό μΆκ°νλ μκ° Default Initializers μ Memberwise Initializers λ μλ μμ±λλ 쑰건μ λ§μ‘±νμ§ μκ² λλ€. λ°λΌμ νμν λ§νΌ Default Initializers μ Memberwise Initializers λ₯Ό λͺ μμ μΌλ‘ μμ±ν΄μΌνλ€.
- With Extensions :
Original implementation
μ Default Initializers μ Memberwise Initializers μ 쑰건μ λ§μ‘±νλ―λ‘ μλμΌλ‘ ν΄λΉ Initializers λ₯Ό μμ±νλ€. λ°λΌμDefault Initializers μ Memberwise Initializers μ μμ± μ‘°κ±΄μ μ μ§ν μ± Custom Initializers λ₯Ό μΆκ°νλ κ²μ κ°λ₯
νκ² νλ€.
5. Methods π©βπ»
1. With Method Extensions
Extensions λ₯Ό μ΄μ©ν΄ Instance Methods
μ Type Methods
λ₯Ό νμ₯νλ κ²μ΄ κ°λ₯νλ€. μ΄κ²μ
Computed Property Extensions μ λ§μ°¬κ°μ§λ‘ μ¬μ©μκ° μ μν νμ
λΏ μλλΌ
Built-in Types λ₯Ό νμ₯νλ κ²μ ν¬ν¨
νλ€.
λ€μ μμ λ Built-in Types μ λ³λμ Iterator λ₯Ό μ¬μ©νμ§ μκ³ λ΄μ₯λ λ©μλλ§μ μ¬μ©ν΄ λ°λ³΅μ ν μ μλλ‘ Int μ λ°λ³΅μ μ€ννλ Instance Methods λ₯Ό μΆκ°νλ€.
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
λ€μ μΈ μ½λλ λͺ¨λ λμΌν μλμ νλ€.
for _ in 1...3 {
print("Hello!")
}
Array(1...3).forEach { _ in print("Hello!") }
3.repetitions { print("Hello!") }
Hello!
Hello!
Hello!
2. Mutating Method of Value Types
Swift μμ Structures μ Enumerations λ Value Types λ‘ instance μκΈ° μμ μ Properties μμ νκΈ°
μν΄μλ λ°λμ λ©μλ μμ mutating
keyword λ₯Ό μ μ΄μΌνλ€.
Swift μμ Double
λλ Int
μ κ°μ μλ£νμ Structure λ‘ κ΅¬νλμλ€. λ°λΌμ Extensions λ₯Ό μ¬μ©ν λ μμ μμ μ
Properties λ₯Ό μμ νλ €λ©΄ mutating
μ΄ νμνλ€.
var someDouble: Double = 3.342
let rounded = someDouble.rounded()
print(rounded) // 3
print(someDouble) // 3.342
someDouble.round()
print(someDouble) // 3
rounded()
λ©μλλ func rounded() -> Self
λ‘ μμ μ νμ
μ λ°ννλ λ©μλλ€. λ°λ©΄ round()
λ©μλλ
mutating func round()
λ‘ μμ μμ μ Properties λ₯Ό λ³κ²½νλ, μ¦, mutating λ©μλλ€.
Int Structure μ μκΈ° μμ μ μ κ³±ν΄ κ°μ λ³κ²½νλ(mutating) λ©μλλ₯Ό Extensions λ₯Ό μ΄μ©ν΄ μΆκ°ν΄λ³΄μ.
extension Int {
func squared() -> Self {
self * self
}
mutating func square() {
self = self * self
}
}
var someInt: Int = 3
let squared = someInt.squared()
print(squared) // 9
print(someInt) // 3
someInt.square()
print(someInt) // 9
6. Subscripts π©βπ»
Subscripts
μμ Built-in Types λ₯Ό νμ₯νλ κ²μ ν¬ν¨
νλ€.
λ€μμ 10μ§λ²μμ ν΄λΉ μλ¦Ώμμ μ«μλ₯Ό ꡬνλ μκ³ λ¦¬μ¦μ΄λ€.
(3782 / 1) % 10 // 2
(3782 / 10) % 10 // 8
(3782 / 100) % 10 // 7
(3782 / 1000) % 10 // 3
3782
λ₯Ό 10μΌλ‘ λλλλ¨Έμ§λ 2
κ° λλ―λ‘1μ μ리
λ 2λ€.3782
λ₯Ό 10μΌλ‘ λλλ©΄Int / Int
μ΄λ―λ‘ κ²°κ³Ό μμ Int κ° λμ΄μΌνλ€. λ°λΌμ κ²°κ³Όλ378
μ΄ λκ³ , μ΄μ 378μ 10μΌλ‘ λλλλ¨Έμ§λ 8
μ΄ λλ―λ‘10μ μ리λ 8
μ΄λ€.
μ΄ λ‘μ§μ Built-in Types Int
μ Subscripts λ₯Ό μ΄μ©ν΄ νμ₯ν΄λ³΄μ.
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
3782[0] // 2, 10^0 μ μλ¦Ώμλ 2λ€.
3782[1] // 8, 10^1 μ μλ¦Ώμλ 8μ΄λ€.
3782[2] // 7, 10^2 μ μλ¦Ώμλ 7μ΄λ€.
3782[3] // 3, 10^3 μ μλ¦Ώμλ 3μ΄λ€.
3782[4] // 0, 10^4 μ μλ¦Ώμλ μ‘΄μ¬νμ§ μμΌλ―λ‘ 0μ΄λ€.
7. Nested Types π©βπ»
Extensions λ₯Ό μ΄μ©ν΄ μ΄λ―Έ μ‘΄μ¬νλ Classes, Structures, Enumerations μ Nested Types
λ₯Ό μΆκ°ν μ μμΌλ©°,
μ΄κ² μμ Built-in Types λ₯Ό νμ₯νλ κ²μ ν¬ν¨
νλ€.
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
}
}
}
0.kind // zero
1.kind // positive
(-2).kind // negative
Extensions λ₯Ό μ΄μ©ν΄ Built-in Types
λ₯Ό νμ₯νλ©΄ λ€μκ³Ό κ°μ λ‘μ§μ μ’ λ μ°μνκ² κ΅¬νν μ μλ€.
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(1, 3, 0, -7, 9, 2, 0, -3) // + + 0 - + + 0 -
Reference
- βExtensions.β The Swift Programming Language Swift 5.7. accessed Jan. 17, 2023, Swift Docs Chapter 20 - Nested Types.