[Swift] 확장

임승섭·2023년 6월 24일
0

Swift

목록 보기
11/35

확장

  • 상속 : 수직 확장
    • 성격이 비슷한 타입을 새로 만듦
    • 데이터(저장속성) 추가
    • 기능(메서드) 변형
  • 확장 : 수평 확장
    • 현재 존재하는 타입에 기능(메서드) 추가
    • 저장 속성 확장은 불가능
    • 클래스, 구조체, 열거형 타입에 확장 가능
/*Ex 1*/
class SomeType {
}

// extension 키워드 붙여서 확장
extension SomeType {
	// 새로운 기능(메서드) 추가
    func doSomething() {
    	print("hi")
    }
}
/*Ex 2*/
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("학생이 공부한다")
    }
}

// Student 확장
extension Student {
	func play() {
    	print("학생이 논다")
    }
}

class Undergraduate: Student {
    var major = "전공"
    
    override func walk() {
        print("대학생이 걷는다.")
    }
    
    override func study() {
        print("대학생이 공부한다.")
    }
    
    func party() {
        print("대학생이 파티한다.")
    }
    
    /*	swift에서는 상위 클래스의 확장에서 정의한 메서드는 재정의가 불가능하다!! (@objc 붙이면 가능)
    override func play() {		// 에러
    	print("대학생이 논다")
    }*/
}
var a = Undergraduate()
a.play()				// 학생이 논다. -> 확장한 것도 자동으로 상속



/*확장의 장점 - 소급 모델링 (retroactive modeling)*/
Int -> 하나의 struct
-> 확장해서 새로운 메서드를 추가할 수 있다!!

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

5.squared	// 25
  • 재정의는 상속에서!! 확장에서는 새로운 기능을 추가할 뿐

  • 확장 가능한 멤버 종류

    • 메서드 형태만 정의 가능
    • (타입) 계산 속성, (인스턴스) 계산 속성
    • (타입) 메서드, (인스턴스) 메서드
    • ⭐️ 새로운 생성자 (클래스의 경우, 편의생성자만 추가 가능,
      지정생성자 및 소멸자는 반드시 original에 구현)
    • 서브스크립트
    • 새로운 중첩 타입 정의 및 사용
    • 프로토콜 채택 및 프로토콜 관련 메서드

계산 속성의 확장

  • 타입 계산 속성

    extension Double {
        static var zero: Double { return 0.0 }
    }
    Double.zero	// 0.0
  • 인스턴스 계산 속성

    extension Double {
        var km: Double { return self * 1_000.0 } 
        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) 미터의 길이임") 

메서드의 확장

  • 타입 메서드

    Int.random(in: 1...100)
    
    extension Int {
        static func printNumbersFrom1to5() {
            for i in 1...5 {
                print(i)
            }
        }
    }
    
    Int.printNumbersFrom1to5()
  • 인스턴스 메서드

    extension String {
        func printHelloRepetitions(of times : Int) {
            for _ in 0..<times {
                print("Hello \(self)!")
            }
        }
    }
    
    "Steve".printHelloRepetitions(of: 4)	// Hello Steve! x4
  • 구조체, 열거형에서, 자신의 속성을 변경하는 메서드는 mutating 키워드

    extension Int {
        mutating func square() {
            self = self * self	// 값을 직접적으로 변경
        }
    }
    var someInt = 3
    someInt.square()	// 9

새로운 생성자

클래스

  • 편의 생성자만 추가 가능
    즉, original의 지정생성자를 호출하는 방법으로만 구현이 가능하다
  • 지정 생성자 & 소멸자 추가 불가
    항상 original에서만 정의해야 한다
// 기존 생성자
var color = UIColor(red: 0.3, green: 0.5, blue: 0.4, alpha: 1)

// 편의 생성자 추가
extension UIColor {
	convenience init(color: CGFloat) {
        self.init(red: color/255, green: color/255, blue: color/255, alpha: 1)	// original의 지정 생성자를 호출해야 한다
    }
}
UIColor(color: 1)

구조체

  • 애초에 편의 생성자가 존재하지 않음

  • 비슷하게 original의 지정생성자 호출하는 방식은 존재

    struct Point {
        var x = 0.0, y = 0.0
    
        init() {
            self.init(x: 0.0, y: 0.0)
        }
    
        init(x: Double, y: Double) {
            self.x = x
            self.y = y
        }
    }
  • (편의 생성자와 비슷하게) 생성자를 추가하여 original의 지정 생성자 호출하는 방식으로도 구현 가능하고

  • 새롭게 지정생성자 형태로 구현하는 것도 가능하다

  • (주의) original에서 직접 생성자를 구현하면, 기본생성자 init()과 멤버와이즈 생성자는 제공되지 않는다. 즉, 확장의 생서앚에서 반드시 original의 직접 구현한 지정생성자를 호출해야 한다
    • (예외) original 모든 저장속성에 기본값을 제공하고 직접 생성자를 구현하지 않았다면, 확장에서는 괜찮다
      즉, 확장에서 생성자를 구현해도, original에는 여전히 기본생성자 init()과 멤버와이즈 생성자가 자동 제공되고,
      original의 기본생성자 init()과 멤버와이즈 생성자 호출하는 방식으로의 구현도 가능하
struct Point {
	var x = 0.0, y = 0.0
}
struct Size {
	var width = 0.0, height = 0.0
}
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 {
	// center값으로 직사각형을 만드는 생성자
	init(center: Point, size: Size) {
    	let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        
        self.init(origin: Point(x: originX, y: originY), size: size)
        
        // 예외 케이스
        // 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))

서브스크립트

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

중첩 타입

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


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("")
}

0개의 댓글