iOS/Swift

확장 (Extension)

밤새는 탐험가89 2024. 5. 24. 22:35

확장 (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

 

 

 

'iOS > 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