오늘의 공부(19)

주방·2022년 7월 28일
0

Swift

목록 보기
11/17
post-thumbnail

#1. Study

1) Property

  1. 저장프로퍼티(Stored Property)

    가. 저장프로퍼티

    • 클래스와 구조체에서만 쓸 수 있고, 값을 저장하기 위해 선언되는 변수/상수
    class Human {
      let name: String = "joo"
      var age: Int = 30
    }
    
    struct Person {
      let name: String = "joo"
      var age: Int = 40
    }
    
    let human = Human()
    human.name = "another name" // wrong
    human.age = 40 // 출력 40
    
    Q. 상수 human으로 인스턴스 생성했는데, 왜 age 프로퍼티가 변환이 가능할까?
    a. human은 스택에 할당되고, Human은 힙에 할당이 된다. 스택에 있는 human은 힙의 Human 클래스를 참조하고 있다. 그래서 human 안에 힙에 할당된 클래스의 주소값이 들어가 있다. Human을 상수로 인스턴스 생성하게 되면, 실제 저장된 힙 영역에 저장된 name, age와 상관없이 스택에 저장된 human안의 주소값이 상수가 된다. 그래서 인스턴스 생성당시 변수나 상수로 선언하던지 저장프로퍼티에 접근에 영향을 주지 않는다.

    나. 지연 저장 프로퍼티

    • 프로퍼티가 호출되기 전까지 선언될 뿐 초기화되지 않고, 호출되는 순간 초기화 되는 저장프로퍼티의 한 종류이다.
    • 특징
      • 클래스의 인스턴스 생성과 상관없이 처음 사용될 때, 개별적으로 초기화된다.

      • 항상 변수로 선언되어야 한다.

        class Contacts {
          var email: [String] = .init(repeating: "", count: 100000)
          var address: String = ""
        
          init(){
            print("Contacts Init")
          }
        }
        
        class User{
          lazy name: String = "unknown"
          lazy var contacts: Contacts = .init()
        }
        
        // 만약 지연저장프로퍼티를 사용하지 않으면, 실제 유저 정보가 5,000개였다면 필요없이 100,000개의 정보를 생성하는 것이다.
        // 이로써 메모리 낭비를 줄일 수 있게 되는 것이다.
  2. 연산프로퍼티(Computed Property)

    • 클래스, 구조체, 열거형에서 쓰이는 프로퍼티이며, 특정한 연산을 통해 값을 리턴해준다.
    struct BMI {
        var nickname: String {
            willSet {
                print("유저 닉네임이 \(nickname)에서 \(newValue)로 변경될 예정이다.")
            }
            didSet {
                print("유저 닉네임 변경 완료 \(oldValue) -> \(nickname)으로 바뀜")
    
            }
        }// 타입프로퍼티는 처음부터 무조건 초기값을 가지고 있어야 한다.
        var weight: Double
        var height: Double
    
        // 저장 프로퍼티는 메모리를 차지하고 있다. 연산프로퍼티는 메모리를 가지고 있지 않다. 저장 프로퍼티를 활용해 원하는 값을 변화하는 용도로 주로 사용
        //읽기전용(read only) 프로퍼티지만 계산 하는 값에 따라 결과가 다달라질 수 있기 대문이다.
        var BMIResult: String{
            get { // 인스턴스를 통해서도 해결할 수 있다. 계산을 해줄 때 비로소 저장된다. 결국 호출해야 올라온다.
                let bmiValue = (weight * height) / height
                let bmiStatus = bmiValue < 18.5 ? "저체중" : "정상 이상"
                return "\(nickname) - \(bmiValue) - \(bmiStatus)"
            }
    //        set {
    //            nickname = newValue
    //        }
        }
    
        var sample: String {
            return "\(Int.random(in: 1...100))"
        }
    }
    
    //
    var bmi = BMI(nickname: "고래밥", weight: 50, height: 100)
    
    let result = bmi.BMIResult
    print(result) //미리 안에서 계산을 처리하고 보여주자
    
    bmi.BMIResult = "울라프"  // 오 이렇게 값을 바꿔주면 값이 변환시킬 수 있다. 그러나 상단 set이 생략이 되면 값을 바꿔 줄수 없게 된다.
    print(bmi.BMIResult)
  3. 타입프로퍼티

    • 프로퍼티에 대해 type을 정의하고, 타입의 인스턴스가 생성되었을 때 인스턴스 프로퍼티로 사용할 수 있었다.(저장, 연산 프로퍼티)
    • 그러나 타입 프로퍼티는 타입 자체에 속한다. 그래서 특정타입의 모든 인스턴스에 공통적인 값을 갖게 된다.
    • static 키워드를 활용해 사용할 수 있다. 단, 오버라이딩을 할 경우 class를 사용한다.
    • 인스턴스를 따로 생성하지 않고 사용이 가능하기 때문에 타입의 이름만으로도 프로퍼티를 사용할 수 있다.
  4. 프로퍼티 옵저버

    • 값이 변경되면 작업을 취할 수 있다.
    • willSet(값이 변경되기 직전에 호출되는 메서드), didSet(메서드와 프로퍼티 값이 변경된 직후에 호출하는 메서드)
    class StepCounter {    //totalSteps는 "저장 프로퍼티"입니다!!
        var totalSteps: Int = 0 {
            willSet {
                print("totalSteps을 \(newValue)로 설정하려고 합니다")
            }
            didSet {
                if totalSteps > oldValue  {
                    print("\(totalSteps - oldValue)걸음이 추가되었습니다.")
                }
            }
        }
    }
    
    let stepCounter = StepCounter()
    stepCounter.totalSteps = 200
    /*
     totalSteps을 200로 설정하려고 합니다
     200걸음이 추가되었습니다.
     */
    stepCounter.totalSteps = 360
    /*
     totalSteps을 360로 설정하려고 합니다
     160걸음이 추가되었습니다.
    */
    

2) Protocol

프로토콜은 메소드, 프로퍼티에 대해 정의만 하고, 프로토콜을 채택한 곳에서 구현은 한다.

  1. 프로토콜 요구사항
    • 프로토콜을 준수하는 타입에게 특정 이름과 타입인 인스턴스 프로퍼티 또는 타입 프로퍼티를 요구할 수 있다. 다만, 저장프로퍼티인지, 연산 프로퍼티인지 명시하지 않는다.
      @objc
      protocol ViewPresentableProtocol{
      
          var navigationTitleString: String { get set }
          var backgroundColor: UIColor { get }
          static var identifier: String { get }
      
          func configureView()
          @objc optional func configureLabel()
          @objc optional func configureTextField()
      
      }


#2. Assignment

  • 환율계산
    struct ExchangeRate {
        var  currencyRate: Double {
            willSet {
                print("CurrenvyRate willSet - 환율 변동 예정: \(currencyRate) -> \(newValue)")
            }
            didSet {
                print("CurrenvyRate didSet - 환율 변동 완료: \(oldValue) -> \(currencyRate)")
            }
        }
    
        var USD: Double {
            get{
                return 1.0
            }
        }
    
        var KRW: Double {
            willSet {
                print("USD willSet - 환전금액: USD: \(newValue / currencyRate)달러로 환전될 예정") // 50만원 / 1100을 나눈 것이다.
            }
            didSet {
                print("USD didSet - KRW: \(KRW) -> \(KRW/currencyRate)달러로 환전되었음. ")
            }
        }
    }
    
    var rate = ExchangeRate(currencyRate: 1100, KRW: 1)
    rate.KRW = 500000.0
    //USD willSet - 환전금액: USD: 454.54545454545456달러로 환전될 예정
    //USD didSet - KRW: 500000.0 -> 454.54545454545456달러로 환전되었음.
    rate.currencyRate = 1350.0
    //CurrenvyRate willSet - 환율 변동 예정: 1100.0 -> 1350.0
    //CurrenvyRate didSet - 환율 변동 완료: 1100.0 -> 1350.0
    rate.KRW = 500000.0
    //USD willSet - 환전금액: USD: 370.3703703703704달러로 환전될 예정
    //USD didSet - KRW: 500000.0 -> 370.3703703703704달러로 환전되었음.


#3. 회고

  • 과제를 마무리하고 다시 학습에 들어간다. 과제 중 cell에 대한 identifier와 뷰의 레이아웃에 대한 메소드 형식을 어떻게 통합할지 고민했었다. 그러나 protocol 채택을 통해 해결할 수 있음을 알 수 있었음.
  • 어렵다,,,,

0개의 댓글