티스토리 뷰

Swift

Swift [Extension]

이도형 2023. 2. 26. 09:47

INDEX

1.  확장(Extension)이란?

2.  확장 가능 멤버 (6가지)

3. UIKit에서의 확장(Extension)

4.  마무리

 

 

1.  확장(Extension)이란?

 

상속(Inheritence) VS 확장(Extension)

(상속 - 수직적 개념) 성격이 비슷한 "타입을 새로"만들어, 데이터를 추가하고, 기능(메서드)을 변형시켜 사용하려는 것

(확장 - 수평적 개념): "현재 존재하는 타입"에 기능(메서드)을 추가하여 사용하려는 것

 

확장이란 기존타입에 기능을 추가 하는 것으로

1)클래스, 2)구조체, 3)열거형 (그리고 프로토콜) 타입에 확장(새로운 기능 추가) 가능

단, 새 기능을 추가 할 수는 있지만 기존 기능(메서드)의 재정의(overriding) 불가.

기존 타입의 소스코드에 접근 권한이 없는 타입( == 원래는 접근 불가햇던 타입)을 확장하는 기능이

extension에는 포함되어 있다

 

확장(extension)의 사용 방법

//기존 타입
class SomeType {

}

// 확장
extension SomeType {      // 기존타입에 extension 키워드를 사용해서 확장, 새로운 기능을 정의
    // 새로운 기능을 추가 가능(메서드 형태만 가능) ⭐️
}

기존 유형에 새 기능을 추가하기 위해 확장을 정의하면

확장이 정의되기 전에 생성된 경우에도 기존 인스턴스에서 새 기능을 사용 가능

(당연하다...extension선언 위치의 제약은 받지 않는다는 말)

 

확장 기본 예제

class Person {
    var id = 0
    var name = "이름"
    var email = "1234@gmail.com"
    
    func walk() {
        print("사람이 걷는다.")
    }
}

class Student: Person {
    var studentId = 1
    
    override func walk() {
        print("학생이 걷는다.")
    }
    func study() {
        print("학생이 공부한다.")
    }
}

extension Student {  // 스위프트에서는 확장에서 구현한 메서드에 대한 재정의가 불가❗️ (@objc 붙이면 가능)
    func play() {
        print("학생이 논다.")
    }
}

class Undergraduate: Student {
    var major = "전공"
    
    override func walk() {
        print("대학생이 걷는다.")
    }
    override func study() {
        print("대학생이 공부한다.")
    }
    func party() {
        print("대학생이 파티한다.")
    }
    
    // func play()
//    override func play() {     // 스위프트에서는 확장에서 구현한 메서드에 대한 재정의가 불가❗️
//        print("대학생이 논다.")
//    }
}


//확장에서 재정의는 불가능❗️
//extension Undergraduate {
//    override func play() {
//        print("대학생이 논다.")
//    }
//}

 

확장의 장점? NEW 기능 추가

extension Int {
    var squared: Int {
        return self * self
    }
}


5.squared
3.squared
7.squared

 

 

2.  확장 가능 멤버 (6가지)

 

(확장은 메서드만 가능하다. 이 관점으로 보면 더 쉽게 이해됨)

1. 계산 속성(Computed Property) - 타입 계산 속성, 인스턴스 계산 속성            

2. 메서드(Methods) - 타입 메서드(Type Method), 인스턴스 메서드(Instance Method)                                                                             3. 새로운 생성자(New Initializer) - (다만, 클래스의 경우 편의생성자만 추가가능 / 지정생성자 및 소멸자는 불가능)      

4. 서브스크립트(subscript)                         

5. 새로운 중첩 타입 정의 및 사용(Nested Type)

6. 프로토콜(protocol) 채택 및 프로토콜 관련 메서드(이 부분은 프로토콜에서 정리)

 

1.  계산 속성의 확장

새로운 계산 속성 추가가능!

새로운 저장속성(Stored Proerty), 속성감시자(Property Observer)추가는 불가능

 

1-1 ) 타입 계산 속성의 확장

extension Double {
    static var zero: Double { return 0.0 }
}

Double.zero //0.0

 

1-2) 인스턴스 계산 속성의 확장

인스턴스의 (읽기전용) 계산 속성을 🍎이 만든 Double 타입에 직접 추가 하는 예제

extension Double {
//self를 리턴한다는 점
    var km: Double { return self * 1_000.0 }   // 인스턴스 자신에 1000 곱하기
    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
print("1인치는 \(oneInch) 미터")       // 1인치는 0.0254 미터"


let threeFeet = 3.ft
print("3피트는 \(threeFeet) 미터")     // 3피트는 0.914399970739201 미터"

계산속성의 확장을 어떤 식으로 활용할 수 있을까?

let aMarathon = 42.km + 195.m
print("마라톤은 \(aMarathon) 미터의 길이임")        // 마라톤은 42195.0 미터의 길이임"

extension Int {
    var squared: Int {
        return self * self
    }
}

// 이런 함수를 만드는 것보다 훨씬 간단하고, 더 간결하게 만들 수 있음
func squared(num: Int) -> Int {     
    return num * num
}

Int(123).squared //15129
123.squared //15129

squared(num: 123) //15129

 

2. 메서드의 확장

인스턴스 메서드, 타입 메서드 모두 확장가능함

 

2-1. (타입)메서드의 확장

🍎이 만든 타입메서드 예시

Int.random(in: 1...100)

extension Int {
    static func printNumbersFrom1to5() {
        for i in 1...5 {
            print(i)
        }
    }
}

// 항상 1부터 5까지를 출력
Int.printNumbersFrom1to5() //1,2,3,4,5

 

2-2. (인스턴스)메서드의 확장

// Int타입에 특정 프린트 구문 확장해보기

extension String {
    func printHelloRepetitions(of times: Int) {
        for _ in 0..<times {
            print("Hello \(self)!")
        }
    }
}



"Steve".printHelloRepetitions(of: 4)

//Hello Steve!
//Hello Steve!
//Hello Steve!
//Hello Steve!

 

mutating 인스턴스 메서드의 확장 - 구조체(열거형)에서, 자신의 속성을 변경하는 메서드는 mutating키워드 필요

// 제곱하는 메서드 만들어 보기

extension Int {
    mutating func square() {    // 기존 메서드와 동일하게, mutating 키워드 필요
        self = self * self
    }
}

extension Double{
    mutating func half() {
        self = self / 2
        //내부 속성을 변경하므로 mutating키워드 필수!
    }
}


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

var a = 4.2
a.half() //2.1

 

3. ⭐️생성자의 확장⭐️  

[클래스] -> 까다로움

⭐️편의 생성자만 추가 가능 (즉, 본체의 지정생성자를 호출하는 방법으로만 구현 가능)

❗️지정생성자 추가 불가 / 소멸자 추가 불가 (항상 본래의 클래스에서 정의해야 함)

 

  [구조체] ->자유롭다

 - 구조체는 (원래) 편의 생성자가 존재하지 않고, 상속과 관련이 없기 때문에

   지정생성자의 형태로도 (자유롭게 ⭐️) 생성자 구현 가능 

   (1) (편의 생성자와 비슷한) 생성자를 추가하여 본체의 지정 생성자를 호출하는 방법으로도 구현 가능하고

   (2) 새롭게 지정생성자 형태로 구현하는 것도 가능

 

  구조체 참고 사항

 (본체) 직접 생성자 구현하면, 기본 생성자 init() /멤버와이즈 생성자 제공 안되는 것 인지하기

예외사항

 - (본체) 모든 저장속성에 기본값제공 + (본체에 직접) 생성자를 구현하지 않았다면, 확장에서는 괜찮음

(즉, 확장에서 생성자를 구현해도, 본체에는 여전히 기본 생성자/멤버와이즈 생성자가 자동 제공되고

본체의 기본 생성자/멤버와이즈 생성자 호출하는 방식으로의 구현도 가능)

 

구조체 생성자 확장 예제

// 포인트 구조체와 사이즈 구조체

struct Point {
    var x = 0.0, y = 0.0

    //init(x: Double, y: Double)
    //init()
}

struct Size {
    var width = 0.0, height = 0.0
    
    //init(width: Double, height: Double)
    //init()
}

// Rect구조체

struct Rect {     // 기본값 제공/생성자 구현안함  ===> 기본 생성자 / 멤버와이즈 생성자가 자동 제공 중
    var origin = Point()
    var size = Size()
}

//기본생성자와 멤버와이즈 생성자로 각각 인스턴스 생성하기
let defaultRect = Rect()    // 기본생성자
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) // 멤버와이즈 생성자

extension Rect {
    // 센터값으로 Rect 생성하는 생성자 만들기
    // Rect는 예외적인 경우에 해당 (저장속성에 기본값 셋팅 완료 + 본체에 생성자 구현X이기에 여전히 기본생성자/멤버와이즈 생성자 제공)
    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
    }
}

// 새로 추가한 생성자로 인스턴스 생성해보기
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))

클래스 생성자 확장 예제

// UIColor 기본 생성자
var color = UIColor(red: 0.3, green: 0.5, blue: 0.4, alpha: 1)
    
extension UIColor {      // 익스텐션 + 편의생성자 조합
    convenience init(color: CGFloat) {   // Float   / Double
        self.init(red: color/255, green: color/255, blue: color/255, alpha: 1)
    }
}

//객체 생성의 편리함
UIColor(color: 1)

 

4. 서브스크립트의 확장

확장으로 서브스크립트 추가 가능함

Int(정수형 타입)에 서브스크립트 추가해보기 (기본자리수의 n자리의 십진수 반환하도록 만들기)

extension Int {
    subscript(num: Int) -> Int {
        
        var decimalBase = 1 //분모
        
        for _ in 0..<num {
            decimalBase *= 10
        }
        
        return (self / decimalBase) % 10
        
    }
}

123456789[0]      // (123456789 / 1) ==> 123456789 % 10 ==> 나머지 9
123456789[1]      // (123456789 / 10) ==> 12345678 % 10 ==> 나머지 8
123456789[2]      // (123456789 / 100) ==> 1234567 % 10 ==> 나머지 7
123456789[3]      // (123456789 / 1000) ==> 123456 % 10 ==> 나머지 6

// Int값에 요청된 자릿수가 넘어간 경우 0 반환

746381295[9] //0


// 앞에 0 이 존재 하는 것과 같음
746381295[12] //0

 

 

⭐️5. 중첩 타입(Nested Types)⭐️

클래스, 구조체 및 열거형에 새 중첩 유형을 추가 가능함

 

Int(정수형 타입)에 종류(Kind) ====> 중첩 열거형 추가해 보기

extension Int {
    
    enum Kind {       // 음수인지, 0인지, 양수인지
        case negative, zero, positive
    }
    
    var kind: Kind {    // 계산 속성으로 구현
        switch self {
        case 0:                   // 0인 경우
            return Kind.zero
        case let x where x > 0:   // 0보다 큰경우
            return Kind.positive
        default:                  // 나머지 (0보다 작은 경우)
            return Kind.negative
        }
    }
}

let a = 1
a.kind // 숫자 1의 (인스턴스) 계산속성을 호출 ====> 해당하는 열거형(Int.Kind타입)을 리턴

let b = 0
b.kind //zero

let c = -1
c.kind //negative


Int.Kind.positive //positive
Int.Kind.zero //zero
Int.Kind.negative //negative


let d: Int.Kind = Int.Kind.negative //negative

// 위의 확장을 통해서, 어떤 Int값에서도 중첩 열거형이 쓰일 수 있음
// 위의 확장을 활용한 함수 만들어보기⭐️

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])      // + + - 0 - 0 +

 

3. UIKit에서의 확장(Extension)

Swift의 UIkit에서의 확장은 큰 강점을 지닌다.

 

1. UIKit 클래스에 직접 정의한 메서드 추가하기

extension UIButton {
    func highlight() {
        backgroundColor = UIColor.lightGray
    }
}

myButton.highlight()

2. UIKit 클래스에 프로토콜 메서드 구현하기 => 코드의 가독성을 높임

UIViewController클래스 확장하여 UITableViewDelegate 프로토콜 구현하는 예제

extension UIViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     
    }
}

3.UIKit클래스에서 계산속성 추가가능

무작위 색상 반환 예제

extension UIColor {
    static var random: UIColor {
        return UIColor(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1), alpha: 1.0)
    }
}

let randomColor = UIColor.rando

 

4.  마무리

확장(Extension)은 개념자체는 어렵지 않았고 new기능을 메서드를 통해 추가한다는 걸 의미했다.

  • 타입 계산 속성(Type Computed Property), 인스턴스 계산 속성(Instance Computed Property)
  • 타입 메서드(Type Method), 인스턴스 메서드(Instance Method)
  • 새로운 생성자(New Initializer)  (다만, 클래스의 경우 편의생성자만 추가가능 / 지정생성자❗️및 소멸자는 extension에서 불가능)
  • 서브스크립트(subscript)
  •  새로운 중첩 타입 정의 및 사용(Nested Type)
  • 프로토콜(protocol) 채택 및 프로토콜 관련 메서드(이 부분은 프로토콜에서 정리)

속성은 계산속성은 계산속성만 건드려 주면 되었고(계산속성은 메서드(함수)형태를 띤 속성이기 때문)

생성자는 편의 생성자만 추가가 가능했고 중첩타입이 처음보면 까다로울 수 있는 멤버였고, 

나머지는 쉽게 이해되었을 것이다.

extension은 뒤에 프로토콜을 배울때 한번 더 등장할 개념이며 프로토콜과 함께 자주 등장한다.

UIKit을 통해 실제 앱을 만들때도 많이 등장하는 만큼 어렵진 않지만, 제대로 알아둘 필요가 있는 개념이었다.

 

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

야곰님의 스위프트 프로그래밍 3판

 

반응형

'Swift' 카테고리의 다른 글

Swift [Protocol 2부]  (0) 2023.02.27
Swift [Protocol 1부]  (0) 2023.02.26
Swift [Any, AnyObject]  (0) 2023.02.25
Swift [Type Casting(is, as)]  (0) 2023.02.23
Swift [초기화와 생성자 3부(끝) - Required/Failable/Deinitializers]  (0) 2023.02.23
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함