본캠프 3주차 월요일 TIL

호씨·2024년 11월 4일
0

오늘도 문법주간으로 주말에 못본 문법강의들을 다 몰아서 봤지만 시간적인 이슈로 따로 TIL을 작성을 못해서 한번에 정리할려고 한다.

1.Enum (열거형)

열거형은 Swift에서 특정 주제나 종류에 따라 묶을 수 있는 값들을 그룹화하고 정의하여 데이터타입으로 사용할 수 있다.

enum 특징

  • 관련된 값을 그룹화할 수 있다.
  • enum 키워드와 코드블록을 사용하여 정의할 수 있다.
    • 그룹화된 값은 enum의 코드블록 내에서 case를 사용하여 정의할 수 있다.
  • 타입 안정성을 보장하고, 의도를 명확하게 파악할 수 있다.
  • 정의한 enum은 데이터 타입 으로 사용할 수 있다.
  • enum을 사용하여 만들어진 인스턴스는 Value Type이다.

enum 기본 선언 방법 및 사용 방법

1. 기본선언방법

// enum 뒤에 열거형의 이름을 작성하고 코드블록 안에서 그룹화된 값들을을 case로 작성한다.

// 기본 선언 방법
enum Season {
    case spring
    case summer
    case autumn
    case winter
}

// 한국말로 풀어보기
// Season enum을 선언한다.
// Season이 그룹화하여 갖고있는 값은 spring, summer, autumn, winter 다.

2.사용방법

// 기본 사용 방법

// 기본적인 접근 방법
Season.spring // Enum의 이름뒤에 .을 찍고 데이터에 접근할 수 있다.
Season.summer 

// 데이터 타입(String, Int ...) 처럼 변수나 상수에 저장할 수 있다.

var season: Season = .spring // 데이터 타입을 Season이라고 명시한다면 
                             // Season.spring에서 Seaspn을 생략할 수 있다.

var summer = Season.summer // 데이터타입을 명시해서 값을 넣으면 
												   // 컴파일러가 데이터 타입을 추론해준다.
												   
summer = .spring // summer의 타입이 Season으로 판단되어 
                 // Season.spring에서 Season을 생략할 수 있다.

enum Associated Values(연관 값)

  • 단순히 여러 값을 나열하는 것 이상의 기능을 제공한다.
  • 추가적인 정보를 연관시켜 저장할 수 있는 기능이다.
  • case 와 함께 타입을 지정하여 선언하면 연관값을 가질 수 있다.
  • Assoicated Value 기본 선언 방법 및 사용 방법
// 선언 방법

// 택배 배송 상태를 관리하는 열거형
enum DeliveryStatus { 
	case ordered // 주문함
	case shipping(trackingNumber: String) // 배달중의 상태로 연관값으로 String형태의 trackingNumber를 저장한다.
	case delevereCompleted(date: String)	// 배달완료 상태로 연관값으로 String 타입의 date 정보를 저장한다.
}
let status = DeliveryStatus.shipping(trackingNumber: "123")

print(status) // shipping(trackingNumber: "123")

switch status { // status 값을 switch 코드블록에 넣는다.
case .ordered: // .ordered 상태일 때 아래 코드를 실행한다.
    print("주문완료!")
case .shipping(let trackingNumber): // .shipping 상태일 때 아래의 코드를 실행한다. 연관값은 trackingNumber 상수에 넣는다.
    print("배송중! 송장번호: \(trackingNumber)")
case .delevereCompleted(let date):  // .delevereCompleted 상태일 때 아래의 코드를 실행한다. 연관값은 date 상수에 넣는다.
    print("배송완료! 배송일: \(date)")
}

enum Raw Values (원시 값)

  • case 마다 기본으로 설정된 원시값을 저장할 수 있다.
  • enum 이름 뒤에 콜론: 을 작성한 후 타입을 명시해주어야 한다.
  • case 뒤에 = 을 사용하여 원시값을 정해줄 수 있다.
  • rawValue를 사용하여 enum의 값을 만들 수 있다.
    이때는 매칭되는 rawValue가 없을 수 있기 때문에 Optional 값으로 나온다.
  • Raw Values 사용법
// 문자열의 Raw Value
// 이름(Season)뒤에 : String을 붙여줘서 원시값을 String이라고 명시했다.  
enum Season: String {
    case spring = "값을 정해줄 수 있다"
    case summer // 값이 없다면 case 이름과 동일하게 된다.
    case autumn
    case winter
}

// 사용 방법
let season = Season.spring
print(season.rawValue) // "값을 정해줄 수 있다"

let season2 = Season.summer
print(season2.rawValue) // "summer"

// 반대로 rawValue를 가지고 Season 상수(변수)를 만들 수 있다.
let newSeason = Season(rawValue: "summer")
print(newSeason) // Optional(summer)
// 숫자형의 Raw Value

enum Month: Int {
    case january = 1 // 값을 정해주지 않는다면 0부터 1씩 올라간다.
    case february = 3 
    case march // 값을 정해주지 않아서 3 다음에 4가 된다.
    case april // 값을 정해주지 않아서 5가 된다.
    case may
    case june
    case july
    case august
    case september
    case october
    case november
    case december
}

print(Month.january.rawValue) // 1
print(Month.february.rawValue) // 3
print(Month.march.rawValue) // 4
print(Month.april.rawValue) // 5


let month = Month(rawValue: 3)
print(month) // Optional(february) 

enum에서 함수를 정의하고 사용하는 방법.

// enum 안에 함수를 만들고 호출해서 사용할 수 있다.

enum Season: String {
    case spring
    case summer
    case autumn
    case winter
    
    func weather() -> String{
        switch self {
        case .spring:
            return "따뜻해요"
        case .summer:
            return "더워요"
        case .autumn:
            return "시원해요"
        case .winter:
            return "추워요"
        }
    }
}

let season = Season.spring
print(season.weather()) // "따뜻해요"가 출력된다

enum, class, struct 이름 짓는 방법
enum, class, struct 공통으로 이름짓는 방법
enum, class, struct는 데이터타입으로 사용할 수 있으며, 이름은 대문자로 시작하는 Camel Case로 이름을 만들고 있다.
지금까지 사용했던 String, Int 도 사실은 struct으로 만들어진 데이터타입이라고한다.

enum Weather { // ✅ 대문자로 시작해야한다.
}
enum weather { // ❌ 소문자로 시작하면 안되다.
} 

enum SeasonWeather { } // ✅ 대문자로 시작하고 Camel Case 형식으로해야한다=
enum Seasonweather { } // ❌ 대문자로 시작했지만 중간에 대문자가 없어서 읽기 불편하다. 

열거형은 Swift에서 특정 주제나 종류에 따라 묶을 수 있는 값들을 그룹화하고 정의하여 데이터타입으로 사용할 수 있다.

2. 인스턴스와 init

  • 인스턴스는 class, struct, enum과 같은 설계도를 기반으로 실제 메모리에 생성되는 실체다.
  • class, struct, enum은 설계도의 역할을 하고 인스턴스는 설계도를 보고 직접 만들어져서 메모리에 저장되어 있는 값이다.

간단하게 설명하자면

  • 설계도는 클래스를 정의해놓은 것이고 설계도를 토대로 실제 만든것을 인스턴스라고한다.

인스턴스의 init

인스턴스를 생성할 때 상태를 초기화하여 생성해야 한다.
인스턴스를 만들때 사용되는 변수, 상수(프로퍼티)의 값을 정해주어야 한다.

  • 필요한 재료(프로퍼티)를 넣어서 초기화 하여 생성한다.
  • init 키워드를 사용하여 모든 변수,상수(프로퍼티)을 초기화 해야 한다.
  • 변수나 상수에 기본값을 정해주거나 값이 없음을 나타내는 Optional 타입으로 선언하면 초기화하지 않아도 된다.

enum 에서 init

enum Season: String {
    case spring
    case summer
    case autumn
    case winter
}

let season: Season = .spring 

3. class

class 는 연관된 상태는 변수, 상수에 저장하고 행동은 함수를 정의한 후 그룹화하여 데이터타입으로 사용할 수 있다.

class 특징

  • 프로퍼티메소드 로 구성되어 있다.
    • 구조체, class 안에 있는 변수,상수를 프로퍼티 라고 한다.
    • 구조체, class 안에 있는 함수를 메소드 라고 한다.
  • class의 인스턴스를 생성하여 사용할 수 있다.
  • class 안에서 사용되는 변수와 상수인 프로퍼티에는 default 값을 정해줄 수 있다.
  • class의 인스턴스를 생성할 때 반드시 초기화를 해주어야 한다.
  • init 초기화를 제공해야 한다.
    • 모든 프로퍼티에 default 값을 제공한다면 init 초기화를 생략할 수 있다.
    • 모든 프로퍼티가 Optional 인 경우 init 초기화를 생략할 수 있습.
  • 초기화를 도와주는 컨비니언스 이니셜라이저(convenience initializer)를 제공한다.
  • class를 사용해서 만들어진 인스턴스는 Reference Type다.
    • 인스턴스를 let 으로 만들어도 프로퍼티 변경된다.
    • 함수에 class의 인스턴스를 전달하고 프로퍼티를 변경하면 원본도 변경이 된다.
  • 상속 가능
    • 하위클래스가 상위클래스의 속성(프로퍼티)와 행동(메소드)를 물려받아서 사용할 수 있다.
    • 상속은 객체지향프로그래밍에서 뒤에서 나올 예정이다.

class 구현방법, 초기화방법, 사용방법

1. 기본정의방법

// 기본 정의(구현) 방법
class 클래스이름 {
	// 파라미터 선언
	// 메소드 선언
	
	// 파라미터가 있다면 init을 사용하여 초기화
}

// 사용 방법 (인스턴스로 만드는 방법)
// 클래스이름() 을 사용하면 된다.
// ()안에 클래스에서 사용하는 init에 들어가는 파라미터를 채워주면 된다.

2.init 방법(초기화방법)

인스턴스를 생성할 때 모든 프로퍼티를 초기화해야 하며, 이를 위해 init 키워드를 사용한다.

class 에서 init

  • 모든 프로퍼티의 값을 할당해주어야 한다.
    • 값이 없을 수 있는 옵셔널 타입 , 초기값을 준 프로퍼티 는 예외다.
  • init 을 여러개 만들 수 있다.
  • convenience init (보조 초기화) 기능을 제공한다.
    struct 에서는 제공하지 않는다(매우중요)

초기화방법

  1. 지정초기화
// 일반적인 init

class Person {
    var name: String // 값을 저장하는 저장 프로퍼티
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let person = Person(name: "Ryu", age: 25)
  1. 기본값 초기화
  • 프로퍼티에 기본값을 넣으면 초기화를 진행하지 않아도 된다.
class Person {
    var name: String = "이름없음"
    var age: Int = 0
}

let person = Person()

// 혹은 파라미터를 받지 않는 init에서 모든 프로퍼티 값을 정해줄 수 있다.

class Person {
    var name: String
    var age: Int
    
    init() {
        self.name = "이름없음"
        self.age = 15
    }
}
  1. 여러개의 init 사용
class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    init(name: String) {
        self.name = name
        self.age = 0
    }
    
    init() {
        self.name = "이름없음"
        self.age = 15
    }
}
  1. convenience init : 보조 초기화
  • 클래스에서만 사용할 수 있는 기능이다.
  • self.init을 사용하여 초기화를 도와준다.
// 컨비니언스 이니셜라이저(convenience initializer) 사용해보기
// 파라미터에 값을 할당하지 않고 컨비니언스 이니셜라이즈를 사용해도 비슷한 효과를 낼 수 있다.

class Person {
    var name: String
    var age: Int // 기본값을 주지 않았다.
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    convenience init(name: String) { // convenience init에서 name만 받은 후 아래 init에서 기본값을 정해줄 수 있다.
        self.init(name: name, age: 0)
    }
}

let person = Person(name: "이름만 있다.")
print(person.age) // 0 

deinit : 소멸자

  • class 에서만 사용 가능하다.
  • 사용이 종료된 인스턴스가 메모리에서 해제될 때 자동 호출된다.
  • 직접 호출은 사용 할 수 없다.
deinit {
	// 구현부 
}

3. class 예제코드

// 반복되는 코드

let myName = "Ryu"
let myAge = 20

let myFriendName = "감자"
let myFriendAge = 5

func introduce(name: String, age: Int) {
    print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
}

// 소개를 함수로 묶었는데 이름과 나이도 계속 중복되고 있다.
// struct이나 class로 변수,상수,함수를 관련된 내용을 모아놓을 수 있다.


class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name // self는 해당 클래스의 자기 자신을 의미한다.
                         // Person이 갖고있는 name에 파라미터로 받은 name을 할당한니다.
        self.age = age   // 자신의 age 변수에 파라미터로 받은 age값을 할당한다.
    }
    
    func introduce() {
        print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
    }

}

let me = Person(name: "Ryu", age: 20)
let friend = Person(name: "감자", age: 5)

me.age // me의 age 프로퍼티
me.name // me 의 name 프로퍼티
me.introduce() // me 의 introduce 함수를 호출한다. 
			   // 출력 값 : 안녕하세요 Ryu입니다. 저는 20살 입니다

4. Struct (구조체)

struct 는 연관된 상태는 변수, 상수에 저장하고 행동은 함수를 정의한 후 그룹화하여 데이터타입으로 사용할 수 있다.

struct 주요 특징

  • 프로퍼티메소드 로 구성되어 있다.
    • 구조체, 클래스 안에 있는 변수,상수를 프로퍼티 라고 한다.
    • 구조체, 클래스 안에 있는 함수를 메소드 라고 한다.
  • 구조체의 인스턴스를 생성하여 사용할 수 있다.
  • 구조체 안에서 사용되는 변수와 상수인 프로퍼티에는 default 값을 정해줄 수 있다.
  • Memberwise Initializer(멤버와이즈 이니셜라이저)를 제공합니다.
    • 직접 init 을 정의하지 않아도 모든 프로퍼티의 초기화를 자동으로 생성해주는 기능이다.
  • 구조체를 사용하여 만들어진 인스턴스는 Value Type 다.
    • 구조체의 인스턴스는 값이 복사 되므로, 서로 다른 값으로 처리된다.
      (이부분은 나중에 자세하게 알아볼게요!)
  • 메소드에서 프로퍼티를 변경하려면 mutating 키워드를 사용해야 된다.
  • 상속 불가능
    • 클래스와 달리 구조체는 다른 구조체로부터 상속받거나 상속할 수 없다.
  • 인스턴스를 let 으로 만들면 내부 프로퍼티 변경할 수 없다.
    프로퍼티를 변경하려면 var 로 만들어야 한다.

struct 정의, 초기화, 사용하는 방법

1. 기본 정의 및 사용 방법

// 기본 정의 방법
struct 구조체이름 {
	// 프로퍼티 선언
	// 함수 구현
	
	// ⭐️ class와 다르게 init을 만들지 않아도 자동으로 만들어주어서 생략해도 된다.
}

// 사용 방법
// 구조체이름() 를 사용하면 된다.
// ()안에 구조체에서 사용하는 프로퍼티들의 내용을 채워주어야 한다.
let 상수이름 = 구조체이름() 

2. 초기화방법

인스턴스를 생성할 때 모든 프로퍼티를 초기화해야 하며, 이를 위해 init 키워드를 사용한다.
struct 에서 init

  • 모든 프로퍼티의 값을 할당해주어야 한다.
    • 값이 없을 수 있는 옵셔널 타입 , 초기값을 준 프로퍼티 는 예외다.
  • init 을 여러개 만들 수 있다.
  • memberwise init 멤버와이즈 init을 제공한다.
    class에서는 제공하지 않는다(헷갈리지않게 조심)

초기화방법

  1. memberwise init 사용
  • struct 에서만 제공하는 init 으로 자동으로 init을 생성해준다.

    struct Person {
    var name: String
    var age: Int
    
    	// class와 다르게 init을 생성하지 않아도 자동으로 해준다
    }
    
    var person = Person(name: "Ryu", age: 10)
  • 기본값을 할당한 프로퍼티가 있을 때 멤버와이즈 init는 생략하고 만들어준다.

  1. 기본값 초기화
struct Person {
    var name: String = "이름없음"
    var age: Int = 1
}

Person() // 기본값만 사용 가능
Person(name: "Ryu") // 이름만 변경하고 age는 기본값 사용 가능
Person(age: 15)
Person(name: "Ryu", age: 15)
  1. 지정초기화
  • init을 직접 생성하면 memberwise init이 사용되지 않는다.
struct Person {
    var name: String = "이름없음"
    var age: Int = 1
    
    init() {
        print("init")
    }
}

Person() // 정상동작 : Person에서 init()을 구현했기 때문에 정상동작
Person(name: "Brody") // Error : name만을 파라미터로 받는 init이 없다.
Person(age: 15) // Error : age만 파라미터로 받는 init이 없다.
Person(name: "Brody", age: 15) // Error : name, age를 파라미터로 받는 init이 없다.
  1. 여러개의 init 사용
struct Person {
    var name: String
    var age: Int
    
    init() {
        self.name = "이름 없음"
        self.age = 0
    }
    
    init(name: String) {
        self.name = name
        self.age = 0
    }
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

Person()
Person(name: "Brody")
Person(age: 15) // Error : 파라미터로 age만 전달하는 init이 없기떄문에 컴파일 오류 발생
Person(name: "Ryu", age: 15)

3. 사용방법

struct Person {
    var name: String
    var age: Int
    
    func introduce() {
        print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
    }
    
    mutating func happyNewYear() {
	    age += 1
    }
}

let me = Person(name: "Ryu", age: 20)
let friend = Person(name: "감자", age: 5)

me.age // me의 age 프로퍼티
me.name // me 의 name 프로퍼티
me.introduce() // me 의 introduce 함수를 호출합니다.
			   // 출력 값 : 안녕하세요 Ryu입니다. 저는 20살 입니다
// struct도 프로퍼티에 값을 준다면 초기화할 때 생략해도 된다.

struct Person {
    var name: String
    var age: Int = 0 // 기본값을 0을 주었다.
    
    func introduce() {
        print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
    }
}

Person(name: "홍길동", age: 20) // 초기화할 때 전달하는 age를 사용한다.
Person(name: "Ryu") // age의 기본값 0을 사용한다.

5.## 헷갈리는 내용 정리

classstruct가 헷갈리기 시작

  • 지금까지 배운 class, struct는 프로퍼티와 메소드를 모아놓고 정의하고 사용방법도 비슷해서 헷갈린다.
  • 개념은 비슷하지만 메모리 측에서는 동작하는 방법이 다르기 때문에 헷갈리는것이 당연하다.
  • 심화학습에서 메모리에 대해서 배우면서 차이점을 공부할 예정이므로 아직은 몰라도된다
    • 지금은 init을 제공해주는 것들이 다르다.
    • print로 인스턴스를 출력하면 다르게 출력된다.
    • 지금은 인스턴스가 reference type, value type 으로 다르다. 정도만 이해해야할것같다.

Method vs Function

  • Function 과 Method는 비슷하지만 차이가 있다.
  • Method는 class, struct, enum 내부에 정의된 함수를 메소드라고 부른다.
  • Function은 반대로 class, struct, enum 안에 없는 전역 함수를 함수라고 부른다.
  • 일상 대화에서는 Function 과 Method를 혼용해서 이야기 한다.
// class, struct, enum등 안에 없는 전역함수 -> Function
func printName() {
    print("Hi I'm Ryu")
}

struct Person {
    var name: String
    var age: Int = 0
    // class, struct, enum 등 안에 있는 함수 -> Method
    func introduce() {
        print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
    }
}

let person = Person(name: "Ryu", age: 20)

Property vs 변수,상수

  • 프로퍼티와 변수,상수는 비슷한 역할을 하지만, 정의된 위치에 따라 달르게 불린다.
  • class, struct, enum 등 안에 있는 변수와 상수를 프로퍼티라고 부른다.
    • 저장 프로퍼티, 연산 프로퍼티, 타입 프로퍼티 등 이 있다.
  • class, struct, enum 안에 없을 때는 변수와 상수라고 부른다.
struct Person {
    var name: String // struct 안에 있으므로 Property
    var age: Int = 0

    func introduce() {
        print("안녕하세요 \(name)입니다. 저는 \(age)살 입니다")
    }
}

let person = Person(name: "Ryu", age: 20)

// person은 struct, class 안에 없어서 상수라고 부른다.
// person안에있는 age는 Property라고 부른다.

Xcode에서 자동완성으로 나오는 경우에 확인을 할 수 있다
f : function의 약자로 함수임을 확인 할 수 있다.
M : Method의 약자로 메소드임을 확인 할 수 있다.
V : Variable의 약자로 변수/상수임을 확인 할 수 있다.
P : Property의 약자로 프로퍼티임을 확인 할 수 있다.
이런식으로 확인 할 수 있다.

6. Property(프로퍼티)

class, struct, enum 등에서 사용되는 변수나 상수를 프로퍼티(Property)라고 한다..

프로퍼티 사용하는 방법에 따라 몇가지 유형으로 나눌 수 있다.

  • 저장 프로퍼티
  • 연산 프로퍼티
  • 타입 프로퍼티
  • 프로퍼티 옵저버
  • 지연저장 프로퍼티

저장 프로퍼티(Stored Property)

  • 데이터를 저장하는 프로퍼티 다.
  • varlet 을 모두 사용할 수 있다.
  • class, struct 등에서 데이터를 저장하기 위한 변수 와 상수를 의미한다.
  • enum 에서는 저장프로퍼티를 사용할 수 없다. (빌드 오류 발생)
  • 초기값을 가질 수 있다.
struct Person {
    var name: String // 값을 저장하는 저장 프로퍼티
    var age: Int = 0 // 값을 저장하는 초기값이 있는 저장 프로퍼티
}

연산 프로퍼티(Computed Property)

  • 값을 직접 저장하지 않고 계산된 값을 제공하는 프로퍼티다.
  • class, struct, enum 등 에서 사용 가능하다.
  • var 만 사용 가능하다.
    • 동적으로 계산하여 값을 반환하기 때문에 상수인 let은 사용할 수 없다.
  • gettersetter를 사용하여 동적으로 값을 계산한다.
    • getterget 키워드를 사용하여 코드블록을 구현하여 사용한다.
    • setterset 키워드를 사용하여 코드블록을 구현하여 사용한다.
  • getter 은 필수이고 setter 는 구현하지 않아도 된다.
  • setter 을 구현하지 않았다면 get 키워드를 생략할 수 있다.
// Getter와 Setter 모두 있는 연산 프로퍼티
struct Person {
    var name: String // 값을 저장하는 저장 프로퍼티
    var age: Int = 0 // 값을 저장하는 초기값이 있는 저장 프로퍼티

    var isAdult: Bool { // getter와 setter를 제공하는 연산 프로퍼티
        get {
            return age >= 20
        }

        set {
            if newValue == true { // newValue는 할당한 새로운 값을 의미한다.
                age = 20
            } else {
                age = 19
            }
        }
    }
}

var person = Person(name: "Ryu", age: 100) // person 변수에 Person 구조체를 생성하여 할당한다.
                                           // name에는 "Ryu"를 age에는 100을 할당한다.
print(person.isAdult)    // age가 20보다 크므로 isAdult는 true가 된다.
person.isAdult = false   // isAdult를 false로 변경하면 isAdult 연산프로퍼티의 Set
                         // 코드블록이 실행되어 age를 19로 변경한다.
print(person.age)        // age가 19이므로 19가 출력된다.
// Setter 가 구현하지 않은 연산프로퍼티
struct Person {
    var name: String // 값을 저장하는 저장 프로퍼티
    var age: Int = 0 // 값을 저장하는 초기값이 있는 저장 프로퍼티

    var isAdult: Bool { // getter만 제공하는 연산 프로퍼티
        get {
            return age >= 20
        }
    }
}

var person = Person(name: "Ryu", age: 100) // person 변수에 Person 구조체를 생성하여 할당한다.
                                             // name에는 "Ryu"를 age에는 100을 할당한다.
print(person.isAdult)    // age가 20보다 크므로 isAdult는 true가 된다.
person.isAdult = false   // isAdult에는 Getter만 있기때문에 새로운 값을 할당할 수 없다.
                         // Compile Error 발생
// Setter가 구현되지 않았을 때는 get 키워드를 생략 할 수 있다.

struct Person {
    var name: String // 값을 저장하는 저장 프로퍼티
    var age: Int = 0 // 값을 저장하는 초기값이 있는 저장 프로퍼티
    
    var isAdult: Bool { // get 키워드가 생략된 연산 프로퍼티 Only Get
        return age >= 20
    }
}

타입 프로퍼티(Type Property)

  • 모든 인스턴스가 공유하는 프로퍼티로, 인스턴스를 만들지 않고 타입 자체에서 접근이 가능하다.
  • static 키워드를 사용한다.
    • class, struct, enum 등 에서 사용 가능하다.
  • 타입이름.타입프로퍼티_이름 형태로 접근 가능하다.
struct Person {
    static var structName = "Person" // static 키워드를 사용한 타입 프로퍼티다.
                                     // 타입 프로퍼티의 이름은 structName이고 "Person"을 할당한다.

    func logStructName() {
        print(Person.structName) // 내부에서는 Self로 사용 가능
    }
}

var person = Person()
person.logStructName() // "Person" 출력
Person.structName = "Changed!" // Person의 타입프로퍼티 structName의 값을 "Changed!"로 변경한다.
person.logStructName() // "Changed!" 출력
print(Person.structName) // "Changed!" 출력
// enum 에서 사용하기
enum Season: String {
    case spring
    case summer
    case autumn
    case winter
    
    static var enumName = "Season"
}

Season.enumName // "Season"

프로퍼티 옵저버(Property Observers)

  • 저장 프로퍼티의 값이 변경되는 것을 감시하고 있다가 코드블록을 실행할 수 있는 기능이다.
class Person {
    var name: String
    var age: Int
    
    var isAdult: Bool {
        get {
            return age >= 20
        }
        set {
            print("Person isAdult setter 코드블록 호출됨")
            if newValue == true {
                age = 20
            } else {
                age = 19
            }
        }
    }
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

class Child: Person { // Child class를 정의합니다. Person class를 상속받는다.
    override var name: String {
        willSet {
            print("Child name 변경 예정 : \(newValue)")
        }
        
        didSet {
            print("Child name 변경 완료 : \(oldValue)")
        }
    }
    
    override var isAdult: Bool {
        willSet {
            print("Child isAdult 변경될 예정")
        }
    }
}

let child = Child(name: "Child", age: 1)
child.isAdult = false
child.name = "변경되는 이름"
  • 저장 프로퍼티 타입뒤에 코드블록을 작성하여 만들 수 있다.
  • 저장 프로퍼티가 사용 가능한 struct, class에서 사용 가능하다.
  • willSet, didSet 을 제공합니다. 둘 중 하나만 작성해도 된다.
  • willSet
    • 값이 변경되기 전에 호출된다.
    • 변경 될 값은 newValue 를 통해 접근하며, 이름을 정할 수 있다.
    • 이름을 정하려면 willSet(이름) 형식으로 작성하면 된다.
  • didSet
    • 값이 변경된 후 호출된다.
    • 변경되기 전의 값은 oldValue 를 통해 접근하며, 이름을 정할 수 있다.
    • 이름을 정하려면 didSet(이름) 형식으로 작성하면 된다.
// 기본 사용 방법

struct Person {
    var name: String {
        willSet {
            print("Person의 name이 변경될 예정입니다. 변경될 값은 \"\(newValue)\" 입니다.")
        }
        
        didSet {
            print("Person의 name이 변경되어 didSet 코드블록이 호출되었습니다.")
            print("\(oldValue) -> \(name)")
        }
    }
    var age: Int
}

var person = Person(name: "Ryu", age: 10)

person.name = "Apple"

/* 출력 값
Person의 name이 변경될 예정입니다. 변경될 값은 "Apple" 입니다.
Person의 name이 변경되어 didSet 코드블록이 호출되었습니다.
Brody -> Apple
*/
// newValue, oldValue 다른 이름으로 변경해서 사용하기
struct Person {
    var name: String {
        willSet(changeNewName) { 
            print("Person의 name이 변경될 예정입니다. 변경될 값은 \"\(changeNewName)\" 입니다.")
        }
        
        didSet(beforeName) {
            print("Person의 name이 변경되어 didSet 코드블록이 호출되었습니다.")
            print("\(name) -> \(beforeName)")
        }
    }
    var age: Int
}

지연 저장 프로퍼티(Lazy Stored Property)

  • 처음 사용하기 전까지 실제로 메모리에 값을 저장하지 않는 지연된 저장 프로퍼티다.
  • 사용하기 전까지 값을 저장하지 않기 때문에 메모리 효율을 높일수 있다.
  • lazy 키워드를 사용합니다.
    • lazy var 프로퍼티이름 형태로 사용하면 된다.
  • var 에서만 사용 가능하다.
  • 저장 프로퍼티를 사용할 수 있는 class, struct 등에서 사용할 수 있다.
class Person {
    var name: String = "A"
    var age: Int = 15
    lazy var height = 175.7
}

let person = Person()
// Person의 height값에 접근하지 않았기 때문에 아직 메모리에 할당되지 않은 상태
print(person.height) // height값을 사용하기 때문에 이제 메모리에 할당하여 사용함.


struct Animal {
    var name: String
    var age: Int
    lazy var color: String = "Black"
}

var animal = Animal(name: "Dog", age: 10) // color값에 접근하지 않았기 때문에 아직 메모리에 할당되지 않은 상태
print(animal.color) // color값을 사용하기 때문에 이제 메모리에 할당하여 사용함.

7. 인스턴스 메소드 (Instance Method)

  • 인스턴스를 통해서 호출되는 메소드다.
  • 지금까지 계속 인스턴스를 통해서 호출했던 메소드가 인스턴스 메소드다.
class Person {
    var name: String = "Default Name"
    var age: Int = 15
    
    func introduce() -> String {
        return "안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다."
    }
    
    func printIndtroduce() {
        print(introduce())
    }
}

let person = Person()
print(person.introduce())
person.printIndtroduce()

인스턴스 메소드에서 프로퍼티 변경하기

인스턴스가 Reference Type, Value Type에 따라서 프로퍼티를 변경하는 방법이 나누어진다.

Reference Type

  • class 으로 만들어진 인스턴스Reference Type이므로, 인스턴스 메소드에서 프로퍼티 값을 직접 변경할 수 있다.
class Person {
    var name: String = "Default Name"
    var age: Int = 15
    
    func introduce() -> String {
        return "안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다."
    }
    
    func timeToNextYear() {
        age += 1 // age 프로퍼티 값에 1더한 값을 할당한다.
    }
}

let person = Person()
print(person.age) // 출력 값 : 15
person.timeToNextYear() // age += 1 해서 16으로 변경
print(person.age) // 출력 값 : 16

Value Type

  • struct , enumvalue type 이므로, 인스턴스 메소드에서 프로퍼티를 직접 변경할 수 없으며 수정하려면 mutating 키워드를 사용해야 한다.
// struct 메소드에서 프로퍼티 값 변경 하기

struct Person {
    var name: String = "Default Name"
    var age: Int = 15
    
    func introduce() -> String {
        return "안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다."
    }
    
    mutating func timeToNextYear() { // mutating 키워드를 붙여야 한다.
        age += 1                     // 붙이지 않으면 컴파일 오류 발생
    }
}

var person = Person()
print(person.age) // 15
person.timeToNextYear() // 16으로 변경
print(person.age) // 16
// enum 메소드에서 값 변경하기
// enum은 프로퍼티가 아닌 self(자기자신)의 현재 값을 변경 할 수 있다.


enum VideoPlayerState {
    case off
    case playing
    case complete
    
    mutating func moveNext() { // mutating 키워드를 붙여서 값을 수정할 수 있게한다.
        switch self {          // mutating 키워드가 없는데 값을 변경하면 컴파일 오류 발생!
        case .off:
            self = .playing
        case .playing:
            self = .complete
        case .complete:
            self = .off
        }
    }
}

var playerState = VideoPlayerState.off
print(playerState) // 출력 값 : off

playerState.moveNext()
print(playerState) // 출력 값 : playing

playerState.moveNext()
print(playerState) // 출력 값 : completed

타입 메소드 (Type Method)

타입 프로퍼티 와 마찬가지로 인스턴스를 만들지 않고 호출할 수 있는 함수다.

  • static 키워드를 사용한다.
  • class, struct, enum 등 에서 사용 가능하다.
  • 함수 내부에서 있는 타입 프로퍼티에만 접근할 수 있다.
    인스턴스를 만들지 않고 사용하기 때문에 다른 프로퍼티에는 접근 할 수 없다.
// struct, class 에서 타입 메소드 사용 방법
struct Person {
    static var structName = "Person"
    var name: String = "Default Name"
    var age: Int = 15
    
    func introduce() -> String {
        return "안녕하세요. 제 이름은 \(name)이고, 나이는 \(age)살 입니다."
    }

    static func introduceType() -> String {
//        name = "A" // 오류 발생! 저장 프로퍼티에는 접근할 수 없어요!
        return "안녕하세요. 저는 \(structName) 타입입니다."
    }
}

print(Person.introduceType()) // 출력 값 : 안녕하세요. 저는 Person 타입입니다.
// enum 에서 타입 메소드 사용 방법
enum Season {
    static var enumName = "Season"
    case spring
    
    static func introduceType() -> String {
        return "저는 \(enumName) 타입입니다."
    }
}

print(Season.introduceType())
profile
이것저것 많이 해보고싶은 사람

0개의 댓글