→ 프로퍼티 : 클래스, 구조체, 열거형 등에 관련된 값을 뜻함
→ 메서드 : 특정 타입에 관련된 함수
저장 프로퍼티 (Stored Properties)
→ 인스턴스의 변수/상수
연산 프로퍼티 (Computed Properties)
→ 특정 연산을 실행한 결과값
타입 프로퍼티 (Type Properties)
→ 특정 타입에만 사용되는 프로퍼티
구조체 ex)
struct Coordinate{
var x: Int
var y: Int
}
//x,y = 저장 프로퍼티
let point: Coordinate = Coordinate(x: 10, y: 10)
클래스 ex)
class Position{
var point: Coordinate
let name: String
//프로퍼티 기본값을 지정해주지 않는다면 initializer 를 따로 정의해야 함
init(name: String, currentPoint: Coordinate){
self.name = name
self.point = currentPoint
}
}
let myPosition: Position = Position(name: "kevin", point: point)
인스턴스를 생성할 때 initializer 를 통해 초깃값을 보내야 하는 이유는 프로퍼티가 *옵셔널이 아닌 값*** 으로 선언되어 있기 때문이다.
→ 그러므로 인스턴스는 생성할 때 프로퍼티에 값이 꼭 있는 상태여야 함
→ 물론 옵셔널도 설정되어 있으면 상관 X
→ 호출이 있어야 값을 초기화하며, 이때 lazy 키워드 사용
→ 상수 X, var 사용하여 변수로 정의
struct Coordinate{
var x: Int = 0
var y: Int = 0
}
class Position{
lazy var point: Coordinate = Coordinate()
let name: String
init(name: String){
self.name = name
}
}
let myPosition: Position = Position(name: "yagom")
When to use?
→ 복잡한 구조체나 클래스 구현할 때 많이 사용
→ 불필요한 성능 저하나 공간 낭비를 줄일 수 있음
Why use?
→ 인스턴스 외부에서 메서드를 통해 인스턴스 내부 값에 접근하려면 (접근자, 설정자)를 구현해야 함. 이는 분산되어 있어 가독성이 떨어질 수 있음
struct Coordinate{
var x: Int
var y: Int
//연산 프로퍼티
var oppositePoint: Coordinate{
get{
return Coordinate(x: -x, y: -y)
}
set(opposite){
x = -opposite.x
y = -opposite.y
}
//아래도 사용 가능 (newValue는 관용적인 표현)
set{
x = -newValue.x
y = -newValue.y
}
//즉, oppositePoint 라는 변수에 대한 get set 메소드를 설정해 주는 것
//get set 중 하나만 구현해도 무방, 대신 안 쓰면 그건 못 씀
}
var myPosition: Coordinate = Coordinate(x: 10, y:20)
print(myPosition)
//10,20
print(myPosition.oppositePoint)
//-10, -20
myPosition.oppositePoint = Coordinate(x: 15, y:10)
print(myPosition)
//15, 10
→ 이런 식으로 연산 프로퍼티를 사용하면 하나의 프로퍼티에 접근자와 설정자가 모두 모여 있고, 더 간결하고 명확하게 표현 가능
프로퍼티의 값이 변경됨에 따라 적절한 작업을 취할 수 있음
프로퍼티의 값이 새로 할당될 때마다 호출됨
지연 저장 프로퍼티에 사용 X
프로퍼티 감시자
→ 프로퍼티 값 변경되기 직전에 호출되는 willSet 메서드 (매개변수: newValue) → 변경될 값
→ 값 변경된 직후에 호출하는 didSet 메서드 (매개변수: oldValue) → 변경되기 전의 값
class Account{
var credit: Int = 0{
willSet{
print("잔액이 \(credit)원에서 \(newValue)원으로 변경될 예정")
}
didSet{
print("잔액이 \(oldValue) 에서 \(credit)으로 변경되었음")
}
}
}
//newValue 하고 oldValue 는 따로 변수 선언을 안 해줘도 있는 값
let myAccount: Account = Account()
//잔액이 0원에서 1000원으로 변경될 예정
myAccount.credit = 1000
//잔액이 0원에서 1000원으로 변경되었음
var wonInPocket: Int = 2000{
willSet{
print("주머니의 돈이 \(wonInPocket) 에서 \(newValue)로 변경 예정")
}
didSet{
print("주머니의 돈이 \(oldValue) 에서 \(wonInPocket)으로 변경 완료")
}
}
class AClass{
static var typeProperty: Int = 0
var instanceProperty: Int = 0{
didSet{
Self.typeProperty = instanceProperty+100
}
}
static var typeComputedProperty: Int{
get{
return typeProperty
}
set{
typeProperty = newValue
}
}
}
let classInstance: AClass = AClass()
classInstance.typeProperty = 1000 //오류! 인스턴스로 접근 불가
AClass.typeProperty = 100 //이렇게 공통으로 접근해야 함
classInstance.instanceProperty = 200
print(AClass.typeProperty) //400
\타입이름.경로.경로.경로
→ 경로 : 프로퍼티 이름
class Person{
var name: String
init(name: String){
self.name = name
}
}
print(type(of: \Person.name))
//ReferenceWritableKeyPath<Person, String>
ex)
struct Person {
var firstname: String
var secondname: String
var age: Int
}
let dave = Person(firstname: "Dave", secondname: "Trencher" , age: 21)
let firstname: String = dave[keyPath: \Person.firstname]
print (firstname)
// Dave
class LevelClass{
var level: Int = 0
func levelUp(){
level += 1
}
}
var levelClassInstance: LevelClass = LevelClass()
levelClassInstance.levelUp()
→ 위 instance method 는 level 인스턴스 프로퍼티 값을 수정함
**mutating 키워드
→ 자신의 property 값을 수정할 때 instance of class 는 크게 신경 안 써도 되지만, struct 나 enum 은 "값 타입"이므로 메서드 앞에 mutating 키워드를 붙여서 해당 메서드가 인스턴스 내부의 값을 변경한다는 것을 명시해야 함.
struct LevelStruct{
var level: Int = 0
mutating func levelUp(){
level += 1
}
}
var levelClassInstance: LevelClass = LevelClass()
levelClassInstance.levelUp()
When to use? → Instance 를 더 명확히 지칭하고 싶을 때 사용
class LevelClass{
var level: Int = 0
func jumpLevel(to level: Int){
print("Jump to \(level)")
self.level = level
//매개변수로 들어온 level과 명확하게 구분을 짓기 위함
}
}
→ 만약 위에서 self 를 붙여주지 않고 그냥 level = level 로 적어준다면 컴파일러는 좌측의 level을 함수의 매개변수로 넘어온 level 을 지칭하는 것으로 판단해버림.
struct LevelStruct{
var level: Int = 0
mutating func levelUp(){
level+=1
}
mutating func reset(){
self = LevelStruct()
}
}
var levelStructInstance: LevelStruct = LevelStruct()
levelStructInstance.levelUp() //1
levelStructInstance.reset())
print(levelStructInstance.level) //0
enum OnOffSwitch{
case on, off
mutating func nextState(){
self = self == .on? .off : .on
}
}
var toggle: OnOffSwitch = OnOffSwitch.off //case 둘 중 하나를 할당
toggle.nextState()
print(toggle) //on
→ static 으로 정의하면 상속 후 메서드 재정의가 불가능.
→ 그러나 method 앞에 class 키워드를 쓰면 상속 후 재정의 가능
class AClass{
static func staticTypeMethod(){
print("static type method")
}
class func classTypeMethod(){
print("class type method")
}
}
class BClass: AClass{
override static func staticTypeMethod(){ }//오류! 재정의 불가!
override class func classTypeMethod(){
print("Bclass class type method")
}
}