2022. 6. 10. 23:24ㆍSwift/기술 면접
확장(Extensions)

여기서 잠깐 ⚠️
위 애플 가이드에서 "익스텐션은 타입에 새 기능을 추가할 수 있지만 Override는 할 수 없다." 라고 명시되어 있다. 하지만 개발을 하면서 Extension을 통해 Override는 사용 가능하다.

위 소스코드를 보면 Extension에서 shouldAutorotate라는 변수와 viewDidLoad라는 메소드를 Override하고 있고, 에러가 발생하지 않는 것을 볼 수 있다. 이러한 일이 가능한 이유는 컴파일러가 Objective-C와의 호환성을 위해 Extension에서 Override를 허용하기 때문이다. 즉, 메소드가 Objective-C와 호환되는 경우에만 Extension에서 메소드를 Override 할 수 있다는 것이다.
또한 Extension을 통한 Method Swizzling도 가능하다. load() 및 initialize()를 override하여 원하는 메소드를 실행시킬 수 있다.
하지만 애플에서 Extension은 기능의 추가를 위해 사용하라고 하니 Extension에서 Override는 지양하자!
클래스의 상속과 확장(Extensions) 비교
| 상속 | 확장 | |
| 타입 | 클래스 타입에서만 가능하다. | 구조체, 클래스, 프로토콜 등에 적용이 가능하다. |
| 특징 | 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가기능을 구현하는 수직 확장이다. | 기존 타입에 기능을 추가하는 수평 확장이다. |
| 재정의(override) | 기존 기능을 재정의 할 수 있다. | 재정의가 불가능 하다. |
- Extensions를 쓰는 이유
- 외부 라이브러리나 프레임워크를 사용한다면 원본 소스를 수정하지 못한다. 이처럼 외부에서 가져온 타입에 내가 원하는 기능을 추가하고자 할 때 확장(Extensions)을 사용한다.
확장(Extensions) 문법
- extension 키워드로 확장 선언하기
extension 확장할 타입이름 {
//타입에 추가될 새로운 기능 구현
}
- 확장(extension)은 기존 타입을 하나 이상의 프로토콜을 채택하도록 확장할 수 있다.
- 프로토콜을 추가적으로 준수(conformance)하기위해, 클래스나 구조체를 작성하는 것과 같은 방법으로 프로토콜 이름을 작성한다.
- 프로토콜을 채택할 경우의 문법
extension 확장할 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3 {
// 프로토콜 요구사항 구현
}
- 기존 타입에 새로운 기능을 추가하기 위해 확장을 정의하는 경우, 새로운 기능은 타입의 모든 기존 인스턴스에서 사용 가능하다.
- 심지어는 확장이 정의되기 전에 생성된 인스턴스에서도 사용할 수 있다.
확장(Extensions)으로 추가할 수 있는 기능
1. 연산 프로퍼티
- 익스텐션을 통해 타입에 연산 프로퍼티를 추가할 수 있다.
ex)
extension Int {
var isEven: Bool { // 짝수일때 true를 반환
return self % 2 == 0
}
var isOdd: Bool { // 홀수일때 true를 반환
return self % 2 == 1
}
}
- 위 코드의 익스텐션은 Int 타입에 두 개의 연산 프로퍼티를 추가한 것이다. 한 개는 짝수인지를 판별하는 Bool 타입 연산 프로퍼티고 다른 하나는 홀수인지를 판별하는 Bool 타입 연산 프로퍼티이다.
- 위 익스텐션으로 Int 타입에 추가해준 연산 프로퍼티는 Int타입의 어떤 인스턴스에서도 사용 가능하다.
- Static 키워드를 사용하면 타입 연산 프로퍼티도 추가 할 수 있다.
- 여기서 타입 연산 프로퍼티는 모든 타입이 사용할 수 있는 상수 프로퍼티라고 한다.
확장은 새로운 연산 프로퍼티를 추가할 수 있지만, 저장 프로퍼티를 추가하거나 기존 프로퍼티를 감시(observers)하는 프로퍼티를 추가 할 수 없다.
2. 메서드
- 익스텐션을 통해 타입 메서드를 추가할 수 있다.
ex)
extension Int {
func multiply(by n: Int) -> Int {
return self * n
}
mutating func multyplySelf(by n: Int) {
self = self.multiply(by: n)
}
static func isIntTypeInstance(_ instance: Any) -> Bool { // 타입이 int인지 확인하는 Bool 타입, 타입메서드이다.
return instance is Int
}
}
- 위 메서드는 Int 타입의 여러 종류 메서드를 추가해 주었다.
3. 이니셜라이저
- 인스턴스를 초기화 할 때는 여러 종류의 이니셜라이저를 만들 수 있다.
- 타입의 정의 부분에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가할 수 있다.
- 단, 익스텐션으로 클래스 타입에 편의 이니셜라이저를 추가할 수는 있지만, 지정 이니셜라이저는 추가해 줄 수 없다.
- 익스텐션에 지정 이니셜라이저를 추가해 줄 수 없는 이유는 지정 이니셜라이저와 디 이니셜라이저는 반드시 클래스 타입의 구현부에 위치해야 하기 때문이다.
- 익스텐션으로 값 타입(열거형, 구조체 등)에 이니셜라이저를 추가했을 때 해당 값 타입이 다음 조건을 모두 성립한다면 익스텐션으로 사용자 정의 이니셜라이저를 추가한 이후에도 해당 타입의 기본 이니셜라이저와 멤버와이즈 이니셜라이저를 호출할 수 있다.
1. 모든 저장 프로퍼티에 기본값이 있다.
2. 타입에 기본 이니셜라이저와 멤버와이즈 이니셜라이저 외에 추가 사용자 정의 이니셜라이저가 없다.
* 멤버와이즈 이니셜라이저?
- 이니셜라이저를 선언하지 않았는데 자동으로 기본 프로퍼티가 포함되어있는 이니셜라이저가 생성되는 것을 말한다.
3. 익스텐션을 통해 추가하는 이니셜라이저는 타입의 기존 이니셜라이저가 갖는 책무를 동일하게 수행해야 한다.
즉, 이니셜라이저 호출이 종료되는 시점까지 인스턴스가 정상적으로 완벽하게 초기화하는 것을 책임져야 한다.
- 위 조건을 만족한다면 익스텐션으로 추가한 이니셜라이저를 해당 타입의 기본 이니셜라이저와 멤버와이즈 이니셜라이저를 호출할 수 있다.
struct Size {
var width: Double = 0.0
var height: Double = 0.0
}
struct Point {
var x: Double = 0.0
var y: Double = 0.0
}
struct Rect {
var orgin: Point = Point()
var size: Size = Size()
}
let defaultRect: Rect = Rect()
let memberwiseRect: Rect = Rect(orgin: Point(x:2.0, y:2.0), size: Size(width: 5.0, height:5.0))
// 이렇게 이니셜라이저를 따로 설정은 해주지 않았지만 자동으로 Size, Point, Rect구조체의 이니셜라이저가 되는 것을 멤버와이즈 이니셜라이저라고 한다.
extension Rect {
init(center: Point, size: Size) {
let orginX: Double = center.x - (size.width / 2)
let orginY: Double = center.y - (size.height / 2)
self.init(orgin: Point(x: orginX, y: orginY), size: Size)
}
}
let centerRect: Rect = Rect(center: Point(x:4.0, y:4.0), size: Size(width: 3.0, height: 3.0))
- 위 예시를 통해 Size 구조체와 Point 구조체의 모든 저장 프로퍼티는 기본 값을 가지며, 추가로 사용자 정의 이니셜라이저를 구현하지 않았기 때문에 기본 이니셜라이저와 멤버와이즈 이니셜라이저를 사용할 수 있는 것을 볼 수 있다.
- 때문에 익스텐션에서 추가해주는 새로운 이니셜라이저는 멤버와이즈 이니셜라이저에게 초기화를 위임해 줄 수도 있다.
4. 중첩 데이터 타입
- 익스텐션을 통해 타입에 중첩 데이터 타입을 추가해 줄 수 있다.
ex)
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
}
}
}
print(1.kind) //positive
print(0.kind) //zero
print((-1).kind) //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(numbers: [3,19,-27,0,-6,0,7]) //+,+,-,0,-,0,+
- 위의 예시는 익스텐션을 통해 Int 타입에 Kind라는 열거형 타입과 Kind 타입의 연산 프로퍼티를 추가한 것을 볼 수 있다.
- Kind 프로퍼티는 인스턴스가 양수인지 음수인지 0인지 판별하여 Kind를 반환하는 연산 프로퍼티이다.
- printIntegerKinds(numbers:) 함수는 Int 타입 값의 배열을 전달받아 각 값의 부호를 kind연산 프로퍼티를 통해 해당 값을 print()해주는 함수이다.
참고
익스텐션 (Extensions) - The Swift Language Guide (한국어)
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
jusung.gitbook.io
[swift] 확장(extensions)이란?
안녕하세요 HoonIOS입니다 :) 이번은 swift의 extensions에 대해 공부를 해봤는데요, 제가 생각한 것보다 훨씬 포괄적이고 제가 모르는 부분이 많아서 포스팅을 했습니다. 그럼 한번 알아보겠습니다. ex
boidevelop.tistory.com
Extension 에서 Override 하기
안녕하세요. 피더입니다. 오늘은 Extension과 Override에 대해 알아보도록 하겠습니다. Extension 이란 Extension은 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있는 기능입니다. 클
feather.tistory.com
Extensions
[최종 수정일 : 2018.09.05] 원문 : 애플 개발 문서 Swift 4.2 Language Guide - Extensions 확장(Extensions) 확장(Extensions)은 기존에 있는 클래스, 구조체, 열거형, 프로토콜 타입에 새로운 기능을 추가합니..
kka7.tistory.com
'Swift > 기술 면접' 카테고리의 다른 글
| Any와 AnyObject의 차이점을 설명하세요. (0) | 2022.07.04 |
|---|---|
| 오토레이아웃을 코드로 작성하는 방법은 무엇인가? (3가지) (0) | 2022.06.22 |
| 앱이 foreground에 있을 때와 background에 있을 때 어떤 제약사항이 있나요? (0) | 2022.05.22 |
| 실제 디바이스가 없을 경우 개발 환경에서 할 수 있는 것과 없는 것을 설명하시오. (0) | 2022.05.19 |
| Bounds와 Frame의 차이점을 설명하시오. (0) | 2022.05.11 |