1. Extension vs. Inheritance πŸ‘©β€πŸ’»

기쑴의 Types λ₯Ό ν™•μž₯ν•˜κΈ° μœ„ν•œ 방법 쀑 ν•˜λ‚˜μΈ Inheritance λŠ” Class μ—μ„œλ§Œ μ‚¬μš©ν•  수 μžˆλ‹€.
Inheritance λŠ” κΈ°μ‘΄ Class λŠ” κ·ΈλŒ€λ‘œ λ‘” 채 λ³„λ„μ˜ Class λ₯Ό μƒμ„±ν•˜λ©°, 이듀은 Superclass/Subclass λΌλŠ” κ΄€κ³„λ‘œ μ—°κ²°λœ Hierarchy ꡬ쑰λ₯Ό κ°–λŠ”λ‹€. Subclass λŠ” 기쑴의 Superclass 에 κΈ°λŠ₯을 μΆ”κ°€ν•΄ ν™•μž₯ν•˜λŠ” 것 뿐 μ•„λ‹ˆλΌ 이미 μ‘΄μž¬ν•˜λŠ” κΈ°λŠ₯을 Overriding ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€.

Extension은 Class, Structure, Enumeration, Protocol νƒ€μž…μ—μ„œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ©° Extensions κ°€ ν•  수 μžˆλŠ” 것은 λ‹€μŒκ³Ό κ°™λ‹€.


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

  1. β€œExtensions.” The Swift Programming Language Swift 5.7. accessed Jan. 17, 2023, Swift Docs Chapter 20 - Nested Types.