확장(Extension)
- 상속과 확장의 비교
- 상속 : 클래스만 가능
- [수직 확장]
- 본질적으로 성격이 비슷한 타입을 새로 만들어
- 데이터를(저장속성)를 추가하거나
- 기능(메서드)를 변형시켜서 사용하려는 것
- [수직 확장]
- 확장 : 클래스/구조체/열거형 모두 가능
- 추가적으로 메서드를 끼워넣기
- [수평 확장]
- 현재 존재하는 타입에 기능(메서드)을 추가하여 사용
- 메서드 종류만 추가 가능. 추가 메모리 공간이 필요한 저장 속성을 확장하는 것은 불가 → 붕어빵 틀에 별개의 영역에 추가로 붙여넣는다고 생각하기!(Direct Dispatch)
- 상속 : 클래스만 가능
Student 클래스 | |
저장 속성 | + play () ( 기존 메서드 재정의 불가 ) |
id name student id |
|
메서드 | |
walk() -1 study() |
- 장점
- 원본 소스 코드에 대한 액세스 권한이없는 유형을 확장하는 기능이 포함
- [소급-모델링 retroactive modeling] : Int, String 등 애플이 미리 만들어 놓은 타입에 확장도 가능
class SomeType {
}
// 확장
extension SomeType {
// 기존의 타입에 extension이라는 키워드를 사용해서 확장 후, 새로운 기능을 정의
// 새로운 기능을 추가 가능(메서드 형태만 가능!)
}
// 기존 유형에 새 기능을 추가하기 위해 확장을 정의하면
// 확장이 정의되기 전에 생성된 경우에도 기존 인스턴스에서 새 기능을 사용 가능함
class Student {
var id = 0
var name = "이름"
var studentId = 1
func walk() {
print("학생이 걷는다.")
}
}
extension Student {
// 스위프트에서는 확장에서 구현한 메서드에 대한 재정의가 불가
// @objc(Objective C) 키워드를 붙이면 가능
func play() {
print("학생 또 놀아?!")
}
}
class Undergraduate: Student {
// override func play -> Error!
// 스위프트에서는 확장에서 구현한 메서드에 대한 재정의가 불가!
// -> 사용은 가능하지만 재정의는 불가
...
}
확장 문법
- 확장 가능한 멤버의 종류 (메서드 형태만 가능)
- 원본 형식이 붕어빵틀 역할을 하기 때문에, 저장속성은 정의할 수 없음
- (타입) 계산 속성, (인스턴스) 계산 속성
- (타입) 메서드, (인스턴스) 메서드
- 새로운 생성자 (➡️ 다만, 클래스의 경우 편의생성자만 추가 가능 / 지정생성자 및 소멸자는 반드시 본체에 구현)
- [예외] : 값타입(구조체, 열거형)의 경우, 지정 생성자 형태로도 (자유롭게) 생성자 구현 가능
- 서브스크립트
- 새로운 중첩 타입 정의 및 사용
- 프로토콜 채택 및 프로토콜 관련 메서드
- 지정생성자는 본체에서 인스턴스를 찍어내는 중요역할을 하므로, 지정생성자는 확장에서 구현하지 못함
멤버의 확장(계산 속성)
- (타입, 인스턴스) 계산 속성의 확장
- 붕어빵 틀에서의 확장, (타입-인스턴스) 계산 속성 확장 가능
- 저장된 속성을 추가하거나 기존 속성에 속성 관찰자를 추가 할 순 없음
- 함수를 만드는 것보다 훨씬 간단하고, 더 간결하게 만들 수 있음
extension Double {
static var zero: Double { return 0.0 } // (타입)
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 }
}
멤버의 확장(메서드)
- (타입) 메서드의 확장
extension Int {
static func printNumbersFrom1to10() {
for i in 1...10 {
print(i)
}
}
}
- (인스턴스) 메서드의 확장
extension Int {
func printHello(of times: Int) {
for _ in 0..<times {
print("Hello \(self)!")
}
}
}
"Swift".printHello(of: 10) // Hello Swift! 10번 출력
- mutating 인스턴스 메서드의 확장
- 구조체(열거형)에서, 자신의 속성을 변경하는 메서드는 mutating 키워드 필요
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square() // 9
멤버의 확장(생성자)
- 생성자의 확장
- 클래스 - 편의 생성자만 구현 가능
- 즉, 본체의 지정생성자를 호출하는 방법으로만 구현 가능
- 지정생성자 추가 불가 / 소멸자 추가 불가 (항상 본래의 클래스에서 정의해야 함)
- 구조체(값타입) - 지정생성자 형태로도 (자유롭게) 생성자 구현 가능
- (원래) 편의 생성자가 존재하지 않고, 상속과 관련이 없기 떄문에 지정생성자의 형태로도 (자유롭게) 생성자 구현 가능 ☝🏻
- (편의 생성자와 비슷한) 생성자를 추가하여 본체의 지정 생성자를 호출하는 방법으로도 구현 가능하고
- 새롭게 지정생성자 형태로 구현하는 것도 가능
- [구조체 참고 사항]
- (본체) 직접 생성자 구현하면, 기본 생성자 init() / 멤버와이즈 생성자 제공 안되는 것이 원칙
- (예외) (본체) 모든 저장속성에 기본값제공 + (본체에 직접) 생성자를 구현하지 않았다면, 확장에서는 괜찮음
- (즉, 확장에서 생성자를 구현해도, 본체에는 여전히 기본 생성자 / 멤버와이즈 생성자가 자동 제공 되고, 본체의 기본 생성자/멤버와이즈 생성자 호출하는 방 식으로의 구현도 가능
- (원래) 편의 생성자가 존재하지 않고, 상속과 관련이 없기 떄문에 지정생성자의 형태로도 (자유롭게) 생성자 구현 가능 ☝🏻
- 클래스 - 편의 생성자만 구현 가능
- 예시
struct Point {
var x = 0.0 , y = 0.0
// init(x: Double, y: Double)
// init() -> 자동 제공, 기본값이 할당되어 있으므로
}
extension Point {
init() {
self.init(x: 0.0, y: 0.0)
}
}
struct Size {
var width = 0.0, height = 0.0
}
// Rect구조체
struct Rect {
// 기본값 제공/생성자 구현안함
// ===> 기본 생성자 / 멤버와이즈 생성자가 자동 제공 중
var origin = Point()
var size = Size()
}
// 기본생성자
let defaultRect = Rect()
// 멤버와이즈 생성자
let memberwiseRect = Rect(origin: Point(x: 20.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
}
}
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 인스턴스 생성 가능
UIColor(color: 1)
//UIColor(red: <#T##CGFloat#>, green: <#T##CGFloat#>, blue: <#T##CGFloat#>, alpha: <#T##CGFloat#>)
멤버의 확장(서브스크립트)
- 확장으로 서브스크립트 추가 가능
extension String {
subscript(num: Int) -> Character {
get {
// String.Index를 사용하여 특정 인덱스에 해당하는 문자에 접근
let index = self.index(self.startIndex, offsetBy: num)
return self[index]
}
set {
// String.Index를 사용하여 특정 인덱스에 해당하는 위치를 찾음
let index = self.index(self.startIndex, offsetBy: num)
// 새로운 문자로 문자열을 업데이트
var modifiedString = self
modifiedString.replaceSubrange(index...index, with: [newValue])
self = modifiedString
}
}
}
var str1: String = "aaaaaa"
print(str1[3]) // 출력: a
str1[3] = "b"
print(str1) // 출력: aaabaa
멤버의 확장(중첩타입)
- 클래스, 구조체 및 열거형에 새 중첩 유형을 추가 가능
class Person {
class SomeType {
...
}
}
// E.g : Int 타입에 열거형 추가
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 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의 인스턴스 계산속성을 호출 -> 해당되는 열거형을 리턴
let b = 0
b.kind
let c = -1
c.kind
Int.Kind.positive // .positive
Int.Kind.zero // .zero
Int.Kind.negative // .negative
let d: Int.Kind = Int.Kind.negative // .negative
반응형
'iOS > Swift' 카테고리의 다른 글
Swift No.21 (1) | 2024.01.28 |
---|---|
Swift No.20 (1) | 2024.01.27 |
Swift No.18 (1) | 2024.01.23 |
Swift No.17 (1) | 2024.01.23 |
Swift No.16 (0) | 2024.01.17 |