TIL 23

오늘 한 일 / 배운점

  • iOS 앱개발 입문 팀 프로젝트 과제
  • 8/16 9:00 팀 회의 / 코드리뷰
  • 8/18 19:00 보충학습 (HG튜터님) : 확장

과제

  • iOS 앱개발 입문 팀 프로젝트 과제

느낀점

오늘은 보충수업때 확장(Extension)에 대한 개념을 배웠다

상속 - Person -> Developer, Doctor : 타입을 새로 만드는 것. 데이터를 추가하고 함수를 변형시켜 사용(수직적) 확장 - 현재 존재하는 타입에 함수를 추가하여 사용(수평적)

확장은 기존 타입에 새로운 기능을 추가하거나 확장하는 것을 의미한다. 상속과 달리 수직적인 확장이 아닌 수평적인 확장이라고 할 수 있으며

확장은 클래스, 구조체, 열거형, 프로토콜에 적용할 수 있으며, Int, String, Date 등 이미 구현된 타입에 추가 기능을 편리하게 구현할 수 있다. 아래는 기존 Dog 클래스에 growl이라는 기능을 추가하는 확장 예시다

class Dog {
	let age = 3
  let breed = "진돗개"
  func bark() {
    print("멍멍")
  }
}
extension Dog {
	func growl() {
    print("으르렁")
  }
}

let dog = Dog() 
dog.growl()

저장 속성은 반드시 클래스 선언부에 있어야 하며, deinit 등 일부 기능은 확장에 추가할 수 없다.

반면 계산 속성은 확장에서도 정의 가능!

또한, 확장을 활용하여 코드를 기능별로 더 깔끔하게 나눌 수도 있는데 아래는 Developer 클래스의 studyplay 기능을 기능별로 확장하여 코드를 조직화한 예시다

class Developer: Person {
  var language = "Swift"
  
  override func work() {
    print("개발한다")
  }
  
  func study() {
    print("\(language)를 공부한다")
  }
}
extension Developer {
  func play() {
    print("게임한다")
  }
}

이런식으로 기능별로 extension을 활용해서 코드를 가독성있게 나눌수도 있다

또한, 기존 타입을 extension으로 확장하여 유용한 기능을 추가할 수도 있는데 아래는 정수, 문자열, 날짜 타입에 대한 확장을 통해 각각의 기능을 추가한 예시다.

// 1
extension Int {
  var square: Int {
    self * self
  }
}

3.square // 결과 : 9

// 2
extension String {
  var starCovered: String {
    return "***" + self + "***" 
  }
}

"hello".starCovered // 결과 : "***hello***"

// 3
extension Date {
  var formatted: String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    formatter.timeZone = TimeZone(identifier: TimeZone.current.identifier)!
    return formatter.string(from: self)
  }
}
Date().formatted // 결과 : "2023-08-18 19:21:20"

이렇게 date를 String으로 변환한다던지
이런 자주쓰이는것도 확장을 활용하면 유용하게 사용가능하다

또 주의사항으로는

extension String {
  func makeStarString() {
    self = "***" + self + "***" // immutable에러가 뜸
  }
}

extension String {
  mutating func makeStarString() {
    self = "***" + self + "***"
  }
}

var str = "hello"
str.makeStarString() // ***hello***
str // ***hello***

구조체(struct)내부의 저장속성을 변경할때에는 반드시 mutating 키워드 붙여아 한다

subscript

extension String {
	subscript(idx: Int) -> String? {
    guard (0..<count).contains(idx) else {
      return nil
    }
    let target = index(startIndex, offsetBy: idx)
    return String(self[target])
  }
}

let helloWorld = "Hello world"
helloWorld[0] // "H"
helloWorld[5] // ""
helloWorld[100] // nil

이 부분은 subscript를 사용하여 문자열 내의 특정 인덱스에 접근하는 방법을 확장을 통해 추가하는 예시이다

※ subscript : array[1] 이런 것 같이 인덱스로 접근할 수 있는 것들
String은 subscript 지원 안하지만 이렇게 만들어서 사용 가능

중첩타입

extension Dog {
	enum Spacies{
		case maltese
		case shihTzu
		case pomeranian
	}
}

Dog.Species.maltese
Dog.Species.shihTzu

위의 코드에서 보여주는 것처럼, 확장을 통해 클래스 내부에 중첩 타입을 추가하는 방법도 있고

class ViewControllerExample: UIViewController {
}

extension ViewControllerExample: UITableViewDelegate {
}

extension ViewControllerExample: UITableViewDataSource {
}

이런식으로 코드 작성에서 클래스와 확장을 사용하여 코드를 더 구조적이고 가독성 있게 나누어서 사용할 수 있다