본문 바로가기
Swift

확장 (Extension)

by 밤새는 탐험가89 2024. 5. 24.
728x90
SMALL

확장 (Extension) 이란?

클래스, 구조체, 열거형 타입에 새로운 property, method, initializer 등을 추가하는 것을 말하는데,

원본 타입에 접근하지 못하는 타입에도 확장해서 사용할 수 있다. 

 

 

확장은 어떻게 사용하나?

 

⭐ 포인트는 "기존에 것에 추가한다" 이다. ⭐

 

아래와 같이 CGPoint 라는 구조체가 있다.

let point: CGPoint = .init(x: 10, y: 20)

 

여기서 point 라는 변수를 print 문을 이용하여 아래와 같이 출력하고 싶다면?

x: 10, y: 20

 

근데 CGPoint란 구조체에는 위에 처럼 출력해줄 수 있는 기능이 없다.

따라서 출력하려면 아래와 같이 직접 print 문을 사용하여 구현해야 한다. 

print("x: \(point.x), y: \(point.y)")

 

CGPoint에 직접 출력할 수 있는 print 기능을 넣을 때 사용하는게 바로

"extension" 이다.

extension CGPoint {
    func printPoint() {
        print("x: \(self.x), y: \(self.y)")
    }
}

 

point 변수를 출력할 떄는 아래와 같이 사용하면 된다.

 

 

 

HomeViewController 라는 클래스에 TweetTableViewCellDelegate 라는 프로토콜을 준수하고 싶다면?

아래와 같이 사용해도 되지만 이러면 가독성이 떨어진다.

class HomeViewController: TweetTableViewCellDelegate {
    ...
}

 

이 때 "extension" 이라는 키워드를 사용하여 클래스 입장에서 준수해야 하는 또는 추가해야 하는 것을 

분리하여 작성하면 가독성이 좋다. 

class HomeViewController: UIViewController {
    ...
}

extension HomeViewController: TweetTableViewCellDelegate {
    ...
}

 

확장에 연산 프로퍼티 추가

 

저장 프로퍼티는 추가할 수 없고, "연산 프로퍼티"만 추가 가능하다. 

 

저장 프로퍼티를 추가하면 에러가 발생한다.

 

연산 프로퍼티는 아래와 같이 사용된다. 

extension Int {
    var half: Int {
        return self / 2
    }
}

 

사용하려면?

let num = 100
print(num.half)           // 50

 

 

확장에 메서드 추가

인스턴스 메서드, 타입 메서드 모두 추가 가능하다. 

// 타입 메서드
extension Int {
    static func printZero() {
        print(0)
    }
}
 
Int.printZero()             // 0
// 인스턴스 메서드
extension Int {
    func printDouble() {
        print(self * 2)
    }
}
 
let num = 100
num.printDouble()           //200

 

 

확장에 생성자 (initializer) 추가

기존 타입에 새로운 이니셜라이저를 추가할 수 있다. 

 

 

Class에 생성자 추가

Designated initializer는 추가할 수 없고 Convenience initializer만 추가할 수있으며,

deinitializer를 추가할 수 없다.

 

 

deinit 안되고, 

 

init 안되고

 

convenience init 메서드만 구현할 수 있다. 

 

 

Struct에 생성자 추가

extension으로 생성자를 추가할 경우, Memberwise Initializer를 보존하며 새로운 생성자를 추가할 수 있다

 

✅ Memberwise Initializer는 "기본 생성자를 자동으로 제공하는 기능"을 갖고 있다. 

✅ Class는 생성자를 지정해야줘야하지만, Struct는 그렇지 않다는 것을 말한다. 

 

 

여기서 자동으로 제공해주는 Memberwise Initializer를 대신하여 struct에 직접 생성자를 구현한다면?

struct PointStruct {
    let x: Int
    let y: Int
 
    init(value: Int) {
        self.x = value
        self.y = value
    }
}

 

이 경우에는 더이상 "Memberwise Initializer" 기능은 제공되지 않는다.

 

여기서 "extension"을 사용한다면?

기존의 Memberwise Initializer를 보존하면서 새로운 생성자를 추가할 수 있다

 

struct PointStruct {
    let x: Int
    let y: Int
}
 
extension PointStruct {
    init(value: Int) {
        self.x = value
        self.y = value
    }
}

 

Memberwise Initializer를 보존하면서 생성자를 추가할 수 있다.

 

 

 

확장에 서브스크립트(Subscript) 추가

extension String {
    subscript(idx: Int) -> String? {
        guard (0..<count).contains(idx) else {
            return nil
        }
        let target = index(startIndex, offsetBy: idx)
        return String(self[target])
    }
}

 

위의 extension을 통해 서브스크립트를 구현하면

 

let sodeul = "Hello, Sodeul!"
sodeul[0]           // Optional("H")
sodeul[100]         // nil

 

[]를 통해 내가 원하는 index의 문자에 접근할 수 있다.

 

 

확장에 중첩 타입 추가

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
            }
    }
}

 

위의 코드처럼 Int 탸입의 extension 안에 Kind라는 enum을 중첩해서 선언할 수 있다. 

let num = 100
print(num.kind)      // positive
 
let num2 = -100
print(num2.kind)     // negative

 

 

 

728x90
LIST

'Swift' 카테고리의 다른 글

고차함수 (Map, Filter, Reduce)  (0) 2024.05.26
MVC 패턴 (Model - View - Controller)  (0) 2024.05.25
클로저 6편  (1) 2024.02.17
클로저 5편  (1) 2024.02.13
클로저 4편  (0) 2024.02.10