Property
, Method
, Initializer
등을 추가하는 것extension
이란 키워드를 사용하여 확장한다.소멸자
는 extension으로 구현할 수 없다. 원본 클래스에서만 구현해야한다.init
메서드도 extension으로 구현할 수 없다. extension SomeType{}
extension SomeType : SomeProtocol, AnotherProtocol{}
let point : CGPoint = .init( x: 10, y : 20 )
예를 들어서 이 x
, y
를 출력하고 싶은데 해당 클래스에 프린트 메서드가 없다고 쳐보자.
그렇다고 하면 내가 필요할 때마다 직접 프린트 하는 함수를 구현해야한다. 이렇게
print("x : \(point.x), y : \(point.y)")
근데, 귀찮은 일일수도 있고 형식을 매번마다 맞추지 못할 수도 있다.
때문에 해당 클래스에 내가 원하는 메서드가 있으면 좋을텐데 싶다.
이 경우에 extension
을 사용한다.
원본은 코드 그대로 두고, 내가 원하는 기능만 해당 타입에 확장한다.
extension CGPoint{
func printPoint(){
print("x : \(self.x), y : \(self.y)")
}
}
extension
확장할 타입의 이름 {
// 원하는 메서드 구현
}
이후에
point.printPoint()
이렇게 원래 printPoint()
가 있었던 것처럼 사용할 수 있다.
CGPoint
원본을 건드리지는 않았지만, 우리는 확장을 하였다.CGPoint
를 선언하는 모든 인스턴스는 CGPoint
+ Extension
으로 구현된다.where
절로 제한을 줄 수 있다.저장 프로퍼티
는 추가할 수 없다. 연산 프로퍼티
만 추가할 수 있다. // 저장 프로퍼티
extension Int{
var zero : Int = 0 // Extensions must not contain stored properties.
}
// 연산 프로퍼치
extension Int {
var half : Int{
return self / 2
}
}
인스턴스 메서드
, 타입메서드
모두 추가 가능// 타입메서드
extension Int{
static func printZero(){
print(0)
}
}
Int.printZero()
// 인스턴스메서드
extention Int{
func printDouble(){
print(self * 2)
}
}
let num = 100
num.printDouble()
인스턴스 변수
나 인스턴서 메서드
를 참조
혹은 초기화
할때 self
를 사용한다. 클래스 변수
나 클래스 메서드
를 참조
혹은 초기화
할 때 self
를 사용한다. 사용예시
// 인스턴스 변수나 인스턴스 메서드를 참조할 때
class Person{
var name : String
init(name : String){
self.name = name
}
func sayHello(){
print("Hello, \(self.name)")
}
}
class Person{
static var numberOfPeople : Int = 0
init(){
self.numberOfPeople += 1
}
}
struct ExampleStruct{
let x : Int
let y : Int
}
class ExampleClass{ // Error : ExampleClass는 초기화자(initializer)가 없습니다.
let x : Int
let y : Int
}
swift
에서 구조체
는 클래스
와 다르게 모든 프로퍼티를 초기화할 수 있게 하는 기본생성자를 자동으로 제공한다. 클래스
는 그렇지 않다. 기본 생성자
는 제공되지 않는다.extension
을 사용하면 기본생성자를 보존하면서 새로운 생성자를 추가할 수 있다.struct ExampleStruct{
var x : Int?
var y : Int?
}
extention ExampleStruct{
init(value : Int){
self.x = value
self.y = value
}
}
extention String{
subscript(idx : Int) -> String? {
guard(0..<count).contains(idx) else{
return nil
}
let target = index(startIndex, offsetBy, idx)
return String(self[target])
}
}
이후에 다음과 같이 사용할 수 있다.
let johnson = "Hello Johnson"
johnson[0] // H
johnson[100] // nil
Closed Range
start..<end
Half-Open Range
start...end
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
}
}
}
// 사용
let num = 100
print(num.kind) // positive
let num2 = -100
print(num * kind) // negative
struct Human{
let name : String
}
let johnson : Human = .init(name : "Johnson")
let johnsonjohnson = johnson
johnson == johnsonjohnson // 에러 : Human이 Equatable 프로토콜을 채택하고 있지 않아서
==
연산자를 사용하려면 Equatable
이란 프로토콜을 채택하고 있어야 한다.때문에 아래와 같이 이렇게 할수는 있으나, 이건 보다시피 좋은 방법은 아니다. (물론 해결책은 될 수 있다)
struct Human : Equatable{
let name : String
public static func == (lhs : Human, rhs : Human) -> Bool{
return lhs.name == rhs.name
}
}
그러나, 보통 우리가 구조체
를 만들때는 원본
에는 관련된 함수들만 놓는다. 클래스
도 마찬가지다. 이를 캡슐화
라고 불렀었다.
때문에 extension
을 사용해서 해당 프로토콜에 맞는 메서드만 구현한다.
struct Human{
let name : String
}
extention Human : Equatable{
public static func == (left : Human, right : Human) -> bool {
return left.name == right.name
}
}
Extension
를 짜면서 기능별로 분리를 한다면 파일 트리구조도 바뀌어서, 가독성 좋은 코드를 만들어낼 수 있다. extension Array where Element : Equatable{
func printAll(){
for item in self{
print(item)
}
}
}
Array
의 제네릭인 Element
가 Equatable
을 채택하고 있는 경우에 printAll
을 사용가능하다.
예시
let nums : [Int] = [1,2]
let points : [(Int, Int)] = [(0,0), (1,1)]
nums.printAll() // 1 2
points.printAll() // 아예 인텔리제이가 printAll 메서드를 보여주지 않는다.
// 왜냐하면 points의 element는 Equatable을 지원하고 있지 않기 때문이다.
특정타입
만 확장 가능하게 하기extension Array where Element == Int{
func printAll(){
for item in self{
print(item)
}
}
}