Swift/Swift Language

Swift Language ) Extensions

iosswift 2021. 9. 14. 12:06

Swift 에서 Extension은, 

Enumeration, Struct, Class, 그리고 Protocol 에 새로운 기능을 부여해 줄 수 있어요

또한, 소스코드에 접근할 수 없는 것도 사용할 수 있게 도와줍니다. 

 

TableView 나 CollectionView 등 에서 Delegate, DataSource 를 이용해 기존 ViewController 에 
기능을 더해주는 것도 Extension 덕분입니다. 

 

Swift Extension이 할 수 있는 일들은 아래와 같아요! 

  • 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

 

또한, Protocol 에 Extension 을 사용함으로써 Protocol conform 에 필요한 requirements 를 Implementation 할 수 있고, 

conform 할 Type 들이 사용할 수 있도록 새로운 기능들을 더해줄 수도 있어요! ( override 는 안되요..!)

 

 

 

Extension Syntax

선언하는 방식은 class, struct 등을 하는 방식과 동일합니다. 

extension SomeType { 
	// new functionality to add to SomeType goes here
}

 

extension 을 이용해서 이미 존재하는 Type이 어떤 Protocols 를 conform 하도록 선언해줄 수 있어요. 쓰는 방법은 class, struct 에서 하는 방식과 동일합니다. (더 자세한 방식은 Protocol 에 써있어요 ! )

(2021.09.16 - [Swift/Swift Language] - Swift Language ) Protocol

 

extension SomeType: SomeProtocol, AnotherProtocol { 
	// implementation of protocol requirements goes here 
}

 

 

 

 

 

 

이제부터, 앞서 언급했던 Extension 이 할 수 있는 일에 대해 하나하나 알아보도록 하겠습니다. 

 

1.   Add computed instance properties and computed type 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			// return 25.4 / 1000 -> 0.0254 
print("One inch is \(oneInch) meters") 
// Prints "One inch is 0.0254 meters"

let threeFeet = 3.ft	// return 3 / 3.28084 -> 0.9143...
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

위 예시에서 1.m   ==>  1.0 

                   1.km ==> 1000.0 

 

m 를 기준으로 km, cm, mm, ft 를 변환해줬어요.  (m 은 SI Unit 이라 기준으로 설정한 듯 싶어요

( 또한, 해당 properties 는 read-only computed property이고 간결하게 표현하기 위해 get 은 생략했음을 알 수 있습니다. )

 

 

 

2.   Define instance methods and type methods

Extension 을 이용해서 Type에 Instance Method, Type Method 를 추가해줄 수 있습니다. 

extension Int { 
    func repetitions(task: () -> Void) { 
       for _ in 0 ..< self { 
 		  task()
       }
    }
}

5.repetitions { 
    print("hi")
}

// print "hi" 5 times

또한, InstanceMethod 를 이용해서 self 를 바꿀 수도 있어요 ! 

Struct, Enum 의 경우 value type 이므로 해당 instance 자체를 바꾸고 싶을 때는 'mutating' 을 func 앞에 붙여야 한다는걸 기억하세요!

 

extension Int { 
    mutating func square() {
        self = self * self 
    }
    
    func square2() -> Int { 
        return self * self 
    }
}

var someInt = 3
someInt.square()
print(someInt)  // print "9"

var someInt2 = 4
someInt2 = someInt2.square2()
print(someInt2) // print "16"

 

 

3.   Provide new initializers

Extension 을 이용해서 Type 에 새로운 Initializer 를 부여할 수 있습니다. 기존 타입에 본인의 Custom Type 을 Parameter 로 받는 Initializer 를 추가하거나, 또는 Type 에 본인이 원하는 Initializer Option 을 추가해보세요 ~!

 

Class 에는 Extension 을 이용해서 Convenience Initializer 는 추가할 수 있지만 Deinitializer, Designated Initializer 는 추가할 수 없어요 .. (해당 Initializers 는 반드시 처음에 Class 에서 정의되어야합니다. ) 

 

Value Type 에 대해 Extension 을 이용하여 Initializer 를 추가할 때는,  

1. 만약 해당 Type 내 Stored Properties 의 default value 가 전부 이미 주어져 있는 경우,

2. 그리고 Custom Initializer 가 존재하지 않는 경우 

 

추가할 Initializer 에서 Type 의 Default Initializer memberwise Initializer 를 내부에서 이용할 수 있어요. 

Struct Point { 
    var x = 0.0, y = 0.0
}

Struct Size { 
    var width = 0.0, height = 0.0
}

Struct Rect { 
    var origin = Point()
    var size = Size()
}

여기서 Rect Structure 는 모든 Properties 에 대해 Default Value 를 가지므로 Default Initializer 와 Memberwise Initialier 를 갖게됩니다. 

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), 
			  size: Size(width: 5.0, height: 5.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)
    }
}


let centerRect = Rect(center: Point(x: 6, y: 6), size: Size(width: 10, height: 10))
print("\(centerRect.origin)") // Point(x: 1.0, y: 1.0)

위에서, Default Initializer init(origin: size) 를 이용해 Rect Structure 의 Initializer init(center: size:)  를 만든 것을 볼 수 있어요.

 

잘 모르는 부분: Convenience Initializer, Designated Initializer. 

 

 

4.   Define subscripts

Extension 을 이용해서 새로운 subscripts 를 더해줄 수 있어요 ! 

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

 

5.   Define and use new nested types

Extension 을 이용해서 Enumeration, Structure, Class 에 Nested Type 을 만들어 줄 수 있어요. 

 

Extension Int { 
    enum Kind { 
        case positive, zero, negative 
    }
    
    var kind: Kind { 
        switch self { 
        case 0: return .zero
        case let x where x > 0 : return .positive
        default: return .negative
        }
    }
}

print(3.kind) // print positive

 

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 + "

 

 print(, terminator: "") : 보통 print 의 마지막 출력은  \n.  즉, 한줄 띄기 이지만,
 terminator 를 설정해주면 \n 대신 다른 값을 끝에 넣어줍니다. 

 

6.   Make an existing type conform to a protocol 

다음 Protocol Chapter 에서 함께 다루겠습니다~

 

 

출처: https://docs.swift.org/swift-book/LanguageGuide/Extensions.html