Chapter 3: Variables and Simple Types - (1)

캣코타·2022년 3월 28일
0

overview

  • variable을 선언하는 것은 눈에 보이는 값을 참조할 수 있는 무언가를 제공함과 동시에 그 scopelinfetime을 제공한다.

Variable Scope and Lifetime

<Variable의 종류>

  • global
    • 스위프트 file 내부 top-level에 선언되어있는 것
    • scope : top-level / lifetime : 프로그램이 실행되는 동안 계속 존재
    • 어디서나 접근할 수 있음
    • 단 fileprivate 접근제어자를 추가하는 경우 외부 파일에서 참조하지 못 함
      //Variable_scope_lifetime.file 
      fileprivate let secret = "sercret"
      
      let secondSecret = "SecondSecret"
      
      //main.file
      
      print(secret) // error
  • Properties
    • 객체 타입 내부 top-level에 선언하는 Variable(&constant)
      1. Instance : property의 기본적인 의미
      2. static/class
    • 객체타입을 통해서만 접근할 수 있다.
      class Example {
      	static let name = "iOS"
      }
      
      Example.name // ok
      name // error
      
  • Local variables : 함수(or 메서드)내에서 선언하는 variable. 함수의 실행이 스택에서 종료되면 메모리에서 해제

Variabl Declaration

  • 보통 Declaration 할 때 initialization도 이루어진다
    • 타입명시 하거나 초기화를 통해 타입을 추론할 수 있다.

      class Example2 {
      	let name = "example"
      }
      // Type Implement
      
      let example: Example2
      //declaration
      
      let example2 = Example2()
      //declaration + initialization
  • 하지만 타입을 구체적으로 명시해야하는 경우가 있다.
    1. numeric Type의 Double, CGFloat 같이 같은 value이지만 여러개의 타입으로 사용가능한 경우

      • 예시 : 내가 쓰고싶은 타입은 CGPoint인데 아래처럼만 명시하면 Double로 인식
        let number = 2.0
        
        let number = 2.0
        if number is Double {
            print("yes")
        }
        // yes 가 프린트됨 
    2. 컴파일러의 타입추론을 통해 선언 및 초기화 할 때

      예시 : 아래 autoreverse, repeat는 UIView.AnimationOpions의 값들인데 컴파일러 입장에선 모르기 때문에 명시해야한다.

      var ops = [.acutoreverse, .repeat]
      //compiler error
      
      var ops: UIView.AnimationOptions = [.acutoreverse, .repeat]
    3. 프로그래머가 타입을 추론하기 힘들 때

      예시 : 아래의 track은 AVAssetTrack이고 컴파일러는 아래의 코드를 타입명시하지 않아도 잘 알고있지만 프로그래머입장에서 (나중에 봐도?) 알고싶을때

      let duration: CMTime = track.timeRange.duration
  • 초기화 해주는게 좋은(혹은 반드시해야하는) 경우
    1. instance property를 선언할 땐 타입만선언하지말고 초기화 까지 해줘라. 단 conditional initialization은 제외.

      struct Test {
      	let a: Int
      
      	//초기화 조건이 필요한 경우 
      	init(_ num: Int) {
      		if num > 0 {
      			a = num
      		} else {
      			a = 0
      		}
      }
    2. 만약 variable이 함수의 인자로 사용되는 경우 반드시 초기화 되어야한다(타입만 명시하면 안된다). 가짜 값이더라도 할당해주어야함.

    3. 초기화 되기도 전에 self를 사용하려고 하는경우 (당연한 얘기)

      //Cocoa가 가진 UIApplication instance method 
      func beginBackgroundTask(
          expirationHandler handler: (() -> Void)? = nil)
              -> UIBackgroundTaskIdentifier
      
      let bti = UIApplication.shared.beginBackgroundTask {
      	UIApplication.shared.endBackgroundTask(bti)
      }
      //compiler error
      
      var bti: UIBackgroundTaskIdentifier = .invalid
      bti = UIApplication.shared.beginBackgroundTask {
      	UIApplication.shared.endBackgroundTask(bti)
      }
      // fake initial value를 할당해준 후 호출 

Computed Variable Intialization

  • Computed variable : 값 대신 함수를 가지고 있다는 의미!
    • setter : variable이 호출된 후 값을 assign해주는 블럭
    • 반드시 var 로 선언(당연)
  • getter, setter에서 이용한 인스턴스가 초기화 될 때 까진 초기화되지 않는다.
  • computed property는 언제쓸까?
    1. 자주쓰는 객체의 타입이 너무 길어서 하나로 묶어둘 때

      //예시 
      var myTableView : MyMusicPlayerTableView {
      	SomeViewController.playerTableView
      }
      
      var playerItem : PlayerItem? {
      	self.myTableView.item
      }
    2. 정교한 계산을 수행할 나타낼 때

      Q. 함수(메소드)로도 구현할 수 있지 않은가?
      A. 함수는 과정을 나타내고, computed Property는 과정이 아닌 결과(thing)로서 직관적인 측면이 있기 때문

      class tableView: UITableView {
      	var imgageSize: CGRect {
      		/* massive calculation
      	   .
      	   .
      	   .
      	   .
      	   */
      		return CGRext value
      	}
      }
    3. Facade for storage

      • 특정 조건의 값을 저장할 때 사용하는게 조금 더 현실적임
      // 이전에 내가 사용했던 방법
      
      class Example {
      	private var _example: Int = 10
      	
      	var showedExample : Int {
      		get {
      			return self._example
      		} set {
      			self._example = newValue
      		}
      }
      
      // refactoring
      class Example2 {
      	private(set) var _example: Int = 10
      
      	func changeExmapleValue(to x: Int) {
      		self._example = x
      	}
      }
      
      // storage로서 computedProperty
      
      class Example3 {
      	private var _example: Int = 10
      	
      	var showedExample : Int {
      		get {
      			return self._example
      		} set {
      			//새로 할당되는 값을 0과 5사이로 제한
      			self._example = max(min(newValue,5), 0)
      		}
      }

Property Wrappers

  • 여러개의 Computed Property를 비슷한 storage로서 사용한다면 Property Wrapper로 만들어서 적용하는게 좋다.
    //newValue가 0과 5사이인 값을 가지는 propertyWrapper를 만듬  
    //PropertyWrapper_Chpater3.swift 파일내부
    @propertyWrapper struct Example {
        private var _number = 0
        var wrappedValue: Int {
            get {
                return self._number
            } set {
                self._number = max(min(newValue, 5), 0)
            }
        }
    }
    //선언만하면됨. - 초기화, getter와 setter없어도 됨
    //이미 propertyWrapper에 작성해 두었기 때문 
    //장사잘되는 음식점에서 미리 수저, 소스, 휴지 가방에 넣어논것과 같은 개념 
    
    //main.swift 파일내부
    struct Example2 {
        @Example var propertyWrapperExample
    }
    
  • 프로퍼티 래퍼를 이용해 선언한 변수는 초기화 될 필요가 없다. 왜냐하면 연산 프로퍼티이기 때문에.
  • 또한 타입을 선언 할 필요도 없어진다 (마치 lazy같다는 생각이 든다)
  • 연산프로퍼티 패턴encapsulate 하는 것
    • 프로퍼티 래퍼를 가진 다른 변수를 또 선언할 수 있다.

Setter Observer

  • willSet, didSet
  • 해당 속성이 초기화 되었을 때, didset에 의해 속성이 변경되었을 땐 호출되지 않는다.
var number = 10 {
	willSet {
		print("\(newValue)")
	}
	didSet {
		print("\(oldValue)")
	}
}

Lazy Initialization

  • lazy 하다
    = 변수의 값에 실행중인 코드(running code)가 접근하기 전 까진 할당(assign), 평가(evaluated)가 진행되지 않고 declaration만 되어있는 상태.

<lazily하게 초기화되는 것들>

  • Global variables
    1. 자동으로 lazy하게 초기화된다
    2. 앱이 런치되면서 파일과 탑-레벨 코드가 계산되는데 그 때 만약 전역변수가 초기화 되면 말이 안 됨(앱이 실행중이 아니기 때문) → 따라서 전역변수의 초기화는 다른 코드에 의해 언급되기 전까진 이루어지지 않는다.
      1. “Under the hood, this behavior is implemented in such a way as to make initialization both singular (it can happen only once) and thread-safe.”

        이런식의 구현은 단인할 초기화와( 단 한번 초기화가 일어나도록 하는 것) 스레드 안정성을 만드는 방법이다.

  • Static Properties
    1. automatically lazy init
  • Instance Properties
    1. 기본적으로는 lazy init 이 아님
    2. lazy 키워드를 인스턴스 만들 때 추가 해야함. (let 아닌 var)
  • Singleton
    1. Lazy init

singleton

  • 단일한 class타입의 인스턴스에 접근하도록 할 때 구현하는 패턴

  • staticData영역에 할당

  • 단점 : immutable 하게 선언할 수 없다.

  • 사용하는 이유

  • 내부에서 초기화 되기 이전에 인스턴스를 refer 할 수 있다.

    class MyClass {
            //lazy 하게 초기화 
        static let shared = MyClass()
    
        private init() { }
    }
    
    let myClass = MyClass.shared 
    let myClass2 = Myclass.shared 
profile
to be iOS Developer

0개의 댓글