Extension

냐옹·2024년 7월 15일
0

IOS

목록 보기
28/32

확장 extension

  • 기존 클래스, 구조체, 열거형 타입에 새로운 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
  1. 인스턴스 자신을 참조하는데에 사용한다.
  • 인스턴스 변수인스턴서 메서드참조 혹은 초기화할때 self를 사용한다.
  1. 클래스 자신을 참조하는데 사용된다.
  • 클래스 변수클래스 메서드참조 혹은 초기화할 때 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
    }
}

기본생성자 Memberwise Initializer

  • 원래는 구조체나, 클래스나 인스턴스 생성자 호출이 끝나는 시점에는 모든 프로퍼티가 초기화되어 있어야 한다.
struct ExampleStruct{
	let x : Int
    let y : Int
}

class ExampleClass{ // Error : ExampleClass는 초기화자(initializer)가 없습니다.
	let x : Int
    let y : Int
}
  • swift에서 구조체클래스와 다르게 모든 프로퍼티를 초기화할 수 있게 하는 기본생성자를 자동으로 제공한다. 클래스는 그렇지 않다.
  1. 내가 만약에 struct 안에 생성자를 구현한다면 기본 생성자는 제공되지 않는다.
  2. 그러나, 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
Swift - Range
    1. Closed Range
      ㄴ 시작과 끝을 포함하는 범위를 나타낸다.
      ㄴ 형식 : start..<end
    1. 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, #pragma mark

  • Extension를 짜면서 기능별로 분리를 한다면 파일 트리구조도 바뀌어서, 가독성 좋은 코드를 만들어낼 수 있다.

범용 타입에서의 where로 확장에 조건두기

    1. 특정 프로토콜을 채택한 타입만 확장 가능하게하기
extension Array where Element : Equatable{
	func printAll(){
    	for item in self{
        	print(item)
        }
    }
}
  • Array의 제네릭인 ElementEquatable을 채택하고 있는 경우에 printAll을 사용가능하다.

  • 예시

let nums : [Int] = [1,2]
let points : [(Int, Int)] = [(0,0), (1,1)]

nums.printAll() // 1 2
points.printAll() // 아예 인텔리제이가 printAll 메서드를 보여주지 않는다. 
// 왜냐하면 points의 element는 Equatable을 지원하고 있지 않기 때문이다. 
    1. 특정타입만 확장 가능하게 하기
extension Array where Element == Int{
	func printAll(){
    	for item in self{
        	print(item)
        }
    }
}

출처 https://babbab2.tistory.com/124

0개의 댓글