프로퍼티와 메서드

Gooreum·2021년 10월 28일
0

Swift

목록 보기
9/16

정의

  • 프로퍼티 : 클래스, 구조체 또는 열거형 등에 관련된 값을 뜻한다.
  • 메서드 : 특정 타입에 관련된 함수를 뜻한다.

프로퍼티

프로퍼티는 저장 프로퍼티(stored property), 연산 프로퍼티(Computed Property), 타입 프로퍼티(Type Property)로 나눌 수 있다.

저장 프로퍼티 (인스턴스 변수)

  • 인스턴스의 변수 또는 상수를 의미
  • 구조체와 클래스에서만 사용 *프로퍼티 감시자(Property Observer) 프로퍼티의 값이 변할 때 값의 변화에 따른 특정 작업을 실행한다. 프로퍼티 감시자는 저장 프로퍼티에 적용될 수 있으며, 부모클래스로부터 상속받을 수 있다.

연산 프로퍼티

  • 값을 저장한 것이 아니라 특정 연산을 실행한 결과값을 의미한다.
  • 연산 프로퍼티는 클래스, 구조체, 열거형에 사요될 수 있다.

타입 프로퍼티 (클래스 변수)

  • 저장 프로퍼티와 연산 프로퍼티는 특정 타입의 인스턴스에 사용되는 것을 뜻하지만 특정 타입에 사용되는 프로퍼티도 존재한다. 이를 타입 프로퍼티라고 한다.

저장 프로퍼티

  • 클래스 또는 구조체의 인스턴스와 연관된 값을 저장하는 가장 단순한 개념의 프로퍼티이다.
    • 저장 프로퍼티를 정의할 때 프로퍼티 기본값과 초깃값을 지정해줄 수 있다.
  • 구조체 타입 인스턴스를 상수로 생성하면, 인스턴스 프로퍼티가 변수로 선언되어 있다할지라도 수정 불가하다.
    • 구조체는 '값 타입' 이기 때문에, 값 타입의 인스턴스가 상수로 선언되면, 그 내부의 모든 프로퍼티도 상수가 된다.
    • 반면, 클래스는 참조 타입이기 때문에 클래스 타입의 인스턴스를 상수로 생성하여도, 내부 프로퍼티를 변경할 수 있다.

지연 저장 프로퍼티

  • 인스턴스 생성시 필요할 때 값이 할당되는 지연 저장 프로퍼티(Lazy Stored Property)가 있다.
    • 지연 저장 프로퍼티는 호출이 있어야 값을 초기화하며, 이때 'lazy' 키워드를 사용한다.
  • 상수는 인스턴스가 완전히 생성되기 전에 초기화해야 하므로 필요할 때 값을 할당하는 지연 저장 프로퍼티와는 맞지 않다. 따라서 지연 저장 프로퍼티는 var 키워드를 사용하여 변수로 정의한다.
  • 지연 저장 프로퍼티는 주로 복잡한 클래스나 구조체를 구현할 때 많이 사용된다. *다중 스레드와 지연 저장 프로퍼티 다중 스레드 환경에서 지연 저장 프로퍼티에 동시다발적으로 접근할 때는 한 번만 초기화된다는 보장이 없다. 생성되지 않은 지연 저장 프로퍼티에 많은 스레드가 비슷한 시점에 접근한다면, 여러 번 초기화될 수 있다.

연산 프로퍼티

  • 연산 프로퍼티는 실제 값을 저장하는 프로퍼티가 아니라, 특정 상태에 따른 값을 연산하는 프로퍼티이다.
    • 인스턴스 내/외부의 값을 연산하여 적절한 값을 돌려주는 접근자(getter)의 역할이나, 은닉화된 내부의 프로퍼티 값을 간접적으로 설정하는 설정자(setter)의 역할을 할 수도 있다.
    • 클래스, 구조체, 열거형에 연산 프로퍼티를 정의할 수 있다.
      • *연산 프로퍼티를 사용하는 이유?

        코드의 가독성을 높일 수 있다.

    • getter/setter 용도의 두개의 메서드를 생성해서 사용할 수 있지만,  연산 프로퍼티를 사용하는 것이 간결하고 확실하게 표현할 수 있다.
  • 연산 프로퍼티는 접근자인 get 메서드만 구현해둔 것처럼 읽기 전용 상태로 구현하기 쉽지만, 쓰기 전용 상태로 구현할 수 없다는 단점이 있다.
    • 메서드는 설정자 메서드만 구현하여 쓰기 전용 상태로 구현할 수 있지만 연산 프로퍼티는 그것이 불가능하다.
  • 설정자의 매개변수로 원하는 이름을 소괄호 안에 명시해주면 set 메서드 내부에서 전달받은 전달인자를 사용할 수 있다.
    • 관용적인 표현으로 newValue로 매개변수 이름을 대신할 수 있다.
    • 그럴 경우에는 매개변수를 따로 표기하지 말아야 한다.
    • 또, 접근자 내부의 코드가 단 한줄이고, 그 결괏값이 타입이 프로퍼티의 타입과 같다면 return 키워드를 생략해도 그 결괏값이 접근자의 반환값이 된다.

프로퍼티 감시자

  • 프로퍼티 감시자(Property Observers)를 사용하면 프로퍼티의 값이 변경됨에 따라 적절한 작업을 취할 수 있다.
    • 프로퍼티 감시자는 프로퍼티의 값이 새로 할당될 때마다 호출된다.
    • 이때 변경되는 값이 현재의 값과 같더라도 호출된다.
    • 프로퍼티 감시자는 아래 상황에서 추가할 수 있다. 1.정의하고자 하는 저장 프로퍼티 2.상속받은 저장 프로퍼티 3.상속받은 연산 프로퍼티
  • 프로퍼티 감시자는 저장 프로퍼티뿐만 아니라 프로퍼티를 재정의해 상속받은 저장 프로퍼티 또는 연산 프로퍼티에도 적용할 수 있다.
    • 물론 상속받지 않은 연산 프로퍼티에는 프로퍼티 감시자를 사용할 필요가 없으며 할 수도 없다.
    • 연산 프로퍼티의 접근자와 설정자를 통해 프로퍼티 감시자를 구현할 수 있기 때문이다.
    • 연산 프로퍼티는 상속받았을 때만 프로퍼티 재정의를 통해 프로퍼티 감시자를 사용한다.
  • 프로퍼티 감시자에는 프로퍼티의 값이 변경되기 직전에 호출하는 willSet 메서드와 프로퍼티의 값이 변경된 직후에 호출하는 didSet 메서드가 있다.
  • willSet 메서드와 didSet 메서드에는 매개변수가 하나씩 있다.
    • willSet 메서드에 전달되는 전달인자는 프로퍼티가 변경될 값이고, didSet 메서드에 전달되는 전달인자는 프로퍼티가 변경되기 전의 값이다.
    • 그래서 매개변수의 이름을 따로 지정하지 않으면 willSet 메서드에는 'newValue'가, didSet 메서드에는 'oldValue'라는 매개변수 이름이 자동 지정된다.
  • newValue 혹은 oldValue 매개변수 이름 대신에 다른 이름을 사용하고 싶다면 willSet(newValueName)이나 didSet(oldValueName)처럼 willSet이나 didSet 다음에 소괄호로 감싼 이름을 적어주면 된다.
    import Foundation
    class Account {
    	var credit: Int = 0 {
    		willSet {
    			print("the rest of your money will be changed from \(credit) to \(newValue).")
    		}
    		
    		didSet {
    		print("Your money has been changed from \(oldValue) to \(credit).")
    		}	
    	}
    }
    
    var account: Account = Account()
    print(account.credit)
    account.credit = 100
    print(account.credit)
    
    //결과값
    0
    the rest of your money will be changed from 0 to 100.
    Your money has been changed from 0 to 100.
    100
  • 클래스를 상속받았다면 기존의 연산 프로퍼티를 재정의하여 프로퍼티 감시자를 구현할 수도 있다.
    • 연산 프로퍼티를 재정의해도 기존의 연산 프로퍼티 기능(접근자와 설정자, get과 set 메서드)은 동작한다.

      import Foundation
      class Account {
      	var credit: Int = 0 {   // 저장 프로퍼티
      		willSet {
      			print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정입니다.")
      		}
      		didSet {
      			print("잔액이 \(oldValue)원에서 \(credit)원으로 변경되었습니다.")
      		}
      	}
      			
      	var dollarValue: Double {   // 연산 프로퍼티
      		get {
      			return Double(credit)   // 1000.0
      		}
      		set {
      			credit = Int(newValue * 1000)
      			print("잔액을 \(newValue)달러로 변경 중입니다.")
      		}
      	}
      }
      	
      class ForeignAccount: Account {
      	override var dollarValue: Double {
      		willSet {
      			print("잔액이 \(dollarValue)달러에서 \(newValue)달러로 변경될 예정입니다.")
      		}
      
      		didSet {
      			print("잔액이 \(oldValue)달러에서 \(dollarValue)달러로 변경되었습니다.")
      		}
      	}
      }
      
      var foreignAccount: ForeignAccount = ForeignAccount()
      foreignAccount.credit = 1000
      print("")
      foreignAccount.dollarValue = 3
      
      //결과값
      잔액이 0원에서 1000원으로 변경될 예정입니다.
      잔액이 0원에서 1000원으로 변경되었습니다.
      잔액이 1000.0달러에서 3.0달러로 변경될 예정입니다.
      잔액이 1000원에서 3000원으로 변경될 예정입니다.
      잔액이 1000원에서 3000원으로 변경되었습니다.
      잔액을 3.0달러로 변경 중입니다.
      잔액이 1000.0달러에서 3000.0달러로 변경되었습니다.

전역변수와 지역변수

  • 연산 프로퍼티와 프로퍼티 감시자는 전역변수와 지역변수 모두에 사용할 수 있다.
  • 따라서 프로퍼티에 한정하지 않고, 전역에서 쓰일 수 있는 변수와 상수에도 두 기능을 사용할 수 있다.
  • 함수나 메서드, 클로저, 클래스, 구조체, 열거형 등의 범위 안에 포함되지 않았던 변수나 상수, 즉 우리가 프로퍼티를 다루기 전에 계속해서 사용했던 변수와 상수는 모두 전역변수 또는 전역상수에 해당된다.

타입 프로퍼티

  • 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티를 '타입 프로퍼티'라고 한다.
    • 타입 프로퍼티는 타입 자체에 영향을 미치는 프로퍼티이다.
    • 인스턴스의 생성 여부와 상관없이 타입 프로퍼티의 값은 하나며,
    • 그 타입의 모든 인스턴스가 공통으로 사용하는 값(C 언어의 static constant와 유사), 모든 인스턴스에서 공용으로 접근하고 값을 변경할 수 있는 변수(C 언어의 static 변수와 유사) 등을 정의할 때 유용하다.
  • 타입 프로퍼티는 두 가지이다. [저장 타입 프로퍼티]는
    • 변수 또는 상수로 선언할 수 있으며,

    • 반드시 초깃값을 설정해야 하며 지연 연산된다.

    • 지연 저장 프로퍼티와는 조금 다르게 '다중 스레드 환경이라고 하더라도 단 한번만 초기화된다는 보장을 받는다.'

    • 지연 연산 된다고 해서 'lazy' 키워드로 표새해주지는 않는다.

      [연산 타입 프로퍼티]는 변수로만 선언할 수 있다.

      import Foundation
      class AClass {
      //stored type property
      static var typeProperty: Int = 0
      
      //stored instance property
      var instanceProperty: Int = 0 {
      	didSet {
      		Self.typeProperty = instanceProperty + 100
      	}
      }
      
      //computed type property
      static var typeComputedProperty: Int {
      	get {
      		return typeProperty
      	}
      	set {
      		typeProperty = newValue
      	}
      }
      }
      
      AClass.typeProperty = 123
      let classInstance: AClass = AClass()
      classInstance.instanceProperty = 100
      print(AClass.typeProperty)
      print(AClass.typeComputedProperty)
    • 위의 코드에서 보듯이 타입 프로퍼티는 인스턴스를 생성하지 않고도 사용할 수 있으며, 타입에 해당하는 값이다.

    • 그래서 인스턴스에 접근할 필요 없이 타입 이름만으로도 프로퍼티를 사용할 수 있다.

    • 타입 프로퍼티를 '상수'로도 사용 가능하다.

메서드

  • 메서드는 특정 타입에 관련된 함수를 뜻한다.
    • 클래스, 구조체, 열거형 등은 실행하는 기능을 캡슐화한 인스턴스 메서드를 정의할 수 있다.
    • 또한, 타입 자체와 관련된 기능을 실행하는 타입 메서드를 정의할 수도 있다.
  • 구조체와 열거형이 메서드를 가질 수 있다는 것은 기존 프로그래밍 언어와 스위프트간의 큰 차이점이다.

인스턴스 메서드

  • 인스턴스 메서드는 특정 타입의 인스턴스에 속한 함수를 뜻한다. 인스턴스 내부의 프로퍼티 값을 변경하거나 특정 연산 결과를 반환하는 등 인스턴스와 관련된 기능을 실행한다.
  • 자신의 프로퍼티 값을 수정할 때 클래스의 인스턴스 메서드는 크게 신경 쓸 필요 없지만, 구조체나 열거형 등은 '값 타입' 이므로 메서드 앞에 mutating 키워드를 붙여서 해당 메서드가 인스턴스 내부의 값을 변경한다는 것을 명시해야 한다.
  • self 프로퍼티
    • 모든 인스턴스는 암시적으로 생성된 'self' 프로퍼티를 갖는다.
    • self 프로퍼티는 인스턴스를 더 명확히 지칭하고 싶을 때 사용한다.
    • 스위프트는 자동으로 '메서드 내부에 선언된 지역변수를 먼저 사용하고, 그 다음 메서드 매개변수, 그 다음 인스턴스의 프로퍼티를 찾아서 변수가 무엇을 지칭하는지 유추한다.
    • self 프로퍼티의 다른 용도는 값 타입 인스턴스 자체의 값을 치환할 수 있다.
    • 클래스의 인스턴스는 참조 타입이라서 self 프로퍼티에 다른 참조를 할당할 수 없는데, 구조체나 열거형 등은 self 프로퍼티를 사용하여 자신 자체를 치환할 수도 있다.

타입 메서드

  • 타입 자체에 호출이 가능한 메서드를 타입 메서드라고 한다.
    • 메서드 앞에 static 키워드를 사용하여 타입 메서드임을 나태내준다.
    • 클래스의 타입 메서드는 static 키워드와 class 키워드를 사용할 수 있는데 static으로 정의하면 상속 후 메서드 재정의가 불가능하고 class로 정의하면 상속 후 메서드 재정의가 가능하다.
  • 타입메서드의 self는 인스턴스 메서드와는 달리 self 프로퍼티가 타입 그 자체를 가리킨다는 점이 다르다.
    • 인스턴스 메서드에서는 self가 인스턴스를 가리킨다면 타입 메서드의 self는 타입을 가리킨다.
profile
하루하루 꾸준히

0개의 댓글