🐦 Swift

[Swift] 확장 (익스텐션, Extension)

dev_zoe 2023. 6. 11. 21:43
반응형

본 포스팅은 '스위프트 프로그래밍 (3판)' 도서 앨런 Swift 문법 마스터스쿨 강의를 참고하여

Swift 프로그래밍에 대해 정리하는 글입니다.

혹시 틀린 부분이 있거나 질문이 있으시다면 언제든지 댓글 달아주시면 정말 감사하겠습니다 :)


익스텐션

구조체, 클래스, 열거형, 프로토콜 타입 등 모든 타입에 새로운 기능(메서드)를 추가하여 사용하는 문법

애플이 만들고 개발자가 접근/변경할 수 있는 타입에도 추가가 가능하여 편리하게 사용 가능한 문법임

 

❓ 상속 vs 익스텐션

상속도 결국엔 기존 클래스를 물려받고 메서드를 추가할 수 있는거 아닌가? 했는데

상속은 반드시 상위 클래스/하위 클래스라는 개념이 있어서 하위 클래스가 상위 클래스를 물려받아 메소드를 추가하는 것이고 (수직 확장)

익스텐션은 기존의 클래스에 메소드를 추가하여 사용하는 방식(수평 확장)이므로 완전 다른 개념임

 

또한, 상속은 클래스만 가능하나 익스텐션은 모든 타입이 가능하다.

 

❓ 하위 클래스의 익스텐션에 추가된 메소드끼리 재정의가 가능한가?

 

상속은 되지만 재정의가 불가하다. 왜냐하면 익스텐션 메소드는 상속이 아니라 기존 클래스에 메서드를 추가한 개념이기 떄문

 

💡 익스텐션을 왜쓰는가?

 

주로 외부에서 가져온 타입은 내가 건들 수 없으니까 (애플 프레임워크 내의 데이터타입 포함) 여기에 프로젝트에 맞게 기능을 추가하여 커스텀할 수 있기 때문에 매우 편리함.

그래서 어떤 데이터타입에 추가적으로 필요한 기능 들을 공통적으로 묶을 수 있다면 코드의 재사용성을 높여주기도 함

extension String {
    subscript(_ index: Int) -> String {      // String은 서브스크립트로 접근이 안돼서 커스텀한 예시
        let character = self[self.index(startIndex, offsetBy: index)]
        return String(character)
    }
    
   func match(with pattern: String) -> Bool {  // 정규식이 매칭되는가를 판단
        let predicate = NSPredicate(format: "SELF MATCHES %@", pattern)
        return predicate.evaluate(with: self)
    }
}

 

익스텐션 규칙

1) 속성

- (타입, 인스턴스) 계산 속성 추가 가능

- 속성 감시자 추가 불가 -> 어차피 계산 속성에서 값의 변화를 관찰할 수 있기 때문

extension Int {
	var isEven: Bool {
    	return self % 2 == 0
    }
    
    var isOdd: Bool {
    	return self % 2 == 1
    }
}

print(1.isEven) // fale
print(2.isEven) // true

 

2) 메소드

- 어떠한 메소드든 추가 가능 (타입 메서드, 인스턴스 메서드, 서브스크립트 등)

 

3) 생성자

- 클래스의 경우, 편의생성자만 추가할 수 있기 때문에 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 본체에 구현되어있어야함

- 구조체, 열거형 등 기타 타입의 경우 어떠한 생성자도 구현 가능

class Person {
    var name: String
    
    init(name: String) {
    	self.name = name
    }
}

extension Person {
	convenience init() {
    	self.init(name: "Unknown")
     }
}

var person = Person()
print(person.name)   // Unknown


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

let basicrect = Rect()    // 기본생성자
let memberWiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
                          size: Size(width: 5.0, height: 5.0))    // 멤버와이즈 생성자

extension Rect {
    // 센터값으로 Rect 생성하는 생성자 만들기
    // 예외적인 경우 (저장속성에 기본값 + 본체에 생성자 구현 안한 경우, 여전히 기본생성자/멤버와이즈 생성자 제공)
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        
        // (1) 본체의 멤버와이즈 생성자 호출 방식으로 구현 가능
        self.init(origin: Point(x: originX, y: originY), size: size)
        
        // (2) 직접 값을 설정하는 방식으로도 구현 가능
        //self.origin = Point(x: originX, y: originY)
        //self.size = size
    }
}

 

4) 중첩 타입 정의 및 사용

- 기존 클래스 안에 중첩으로 타입을 생성하여 사용할 수 있다.

extension Int {
    
    enum Kind {       // 홀수인지 짝수인지
        case odd, even, unknown
    }
    
    var kind: Kind {    // 계산 속성으로 구현
        switch self {
        case let x where x%2 == 0:  // 짝수
            return Kind.even
        case let x where x%2 != 0:  // 홀수
            return Kind.odd
        default:                  // 나머지
            return Kind.unknown
        }
    }
}

let a = 1
a.kind      // Kind.odd

let b = 2   // Kind.even
b.kind

 

5) 프로토콜 채택 및 프로토콜 메소드 (프로토콜 공부 후에 수정할 예정)

 

반응형