[Swift 프로그래밍] 프로퍼티 (property)

이정훈·2022년 5월 21일
0

Swift 기본

목록 보기
7/22
post-thumbnail

본 내용은 스위프트 프로그래밍 3판 (야곰 지음) 교재를 공부한 내용을 바탕으로 작성 하였습니다.

프로퍼티(Property)는 클래스, 구조체, 열거형에 관련된 값을 말한다.

프로퍼티에는 세가지의 종류가 존재한다.

  • 저장 프로퍼티
  • 연산 프로퍼티
  • 타입 프로퍼티

저장 프로퍼티


저장 프로퍼티는 클래스, 구조체의 인스턴스와 관련된 값을 저장하는 프로퍼티이다. 예를 들면 변수나 상수가 있다.
var를 사용하여 저장 프로퍼티를 선언하면 변수 저장 프로파티, let을 사용하여 저장 프로퍼티를 선언하면 상수 프로퍼티로 선언이 되며, 변수 프로퍼티는 초기값을 변경 할 수 있으나 상수 프로퍼티는 초기값 변경(재정의)가 불가능하다.

다음은 구조체를 이용하여 저장 프로퍼티를 정의하고 초기화하는 방법이다.

struct Position {
    var x: Int = 1      //저장 프로퍼티
    var y: Int = 1      //저장 프로퍼티
}

var personLeePosition: Position = Position()
let personKimPosition: Position = Position(x: 5, y: 7) //이니셜라이저 사용
print("personLee의 위치: \(personLeePosition.x), \(personLeePosition.y)")
print("personKim의 위치: \(personKimPosition.x), \(personKimPosition.y)")

class NowPosition {
    var point: Position     //저장 프로퍼티(변수)
    let personName: String  //저장 프로퍼티(상수)
    
    //이니셜라이저
    init(point: Position, personName: String) {
        self.point = point
        self.personName = personName
    }
}

let personKim: NowPosition = NowPosition(point: personKimPosition, personName: "김영희")
print(personKim.personName)
print("\(personKim.point.x), \(personKim.point.y)")

위의 Position 구조체 처럼 저장 프로퍼티 선언시 초깃값을 지정해 줄 수 있으며, 혹은 구조체에서 기본적으로 제공하는 이니셜라이저로 프로퍼티 값을 다시 할당 할 수 있다. 아니면, NowPosition 클래스 처럼 처음부터 이니셜라이저를 사용하여 저장 프로퍼티를 초기화 할 수 있다.

연산 프로퍼티


연산 프로퍼티란 값에 대한 연산이 이루어지는 프로퍼티라고 할 수 있다. 연산 프로퍼티의 구성으로 인스턴스로 부터 값을 받아 연산하여 결과를 돌려주는 접근자(getter)와 값을 설정하는 설정자(setter)으로 구성한다.
연산 프로퍼티의 특징 중 하나로 설정자(setter)만을 구현하여 연산 프로퍼티를 구현 할 수 없다는 점이다. 물론 메서드를 사용하면 설정자만을 이용하여 구현하는것이 가능하다.

위의 코드에 대칭이 되는 좌표를 구하는 연산 프로퍼티를 구현해 보았다.

struct Position {
    var x: Int = 1      //저장 프로퍼티
    var y: Int = 1      //저장 프로퍼티
    
    //연산 프로퍼티
    var oppositePosition: Position {
        get {
            return Position(x: -x, y: -y)
        }
        set(opposite) {
            x = -opposite.x
            y = -opposite.y
        }
    }
}

var personLeePosition: Position = Position()
let personKimPosition: Position = Position(x: 5, y: 7) //이니셜라이저 사용
print("personLee의 위치: \(personLeePosition.x), \(personLeePosition.y)")
print("personKim의 위치: \(personKimPosition.x), \(personKimPosition.y)")
print(personLeePosition.oppositePosition)   //대칭 좌표
personLeePosition.oppositePosition = Position(x: 10, y: 20)
//대칭좌표 재설정
print(personLeePosition)

연산 프로퍼티에서 설정자(getter)가 한줄로 구현되어 있고 반환 값이 연산 프로퍼티의 타입과 일치한다면 return 키워드를 생략해줄 수도 있다.

프로퍼티 감시자


프로퍼티 감시자는 프로퍼티의 값이 변경 감지 되었을때 특정 작업을 수행한다.

기본적으로 프로퍼티 감시자는 저장 프로퍼티연산 프로퍼티에서 사용할 수 있다. 다만 상속 받지 않은 연산 프로퍼티는 프로퍼티 감시자를 사용할 수 없으며, 클래스의 상속을 받았을때 프로퍼티 감시자를 사용할 수 있다.

프로퍼티 감시자의 구성으로 willSet메서드와 didSet메서드가 있다.

willSet메서드의 newValue에 전달되는 전달인자는 프로퍼티에 변경될 값이 전달되고 didSet 메서드의 oldValue에 전달되는 전달인자는 프로퍼티의 변경 이전의 값이 전달된다.

다음은 Student 클래스의 상속을 받아 재정의로 연산 프로퍼티프로퍼티 감시자를 구현한 코드이다.

class Student {
    var studentCount: Int = 10 {
        willSet {
            print("수강 인원이 \(studentCount)명에서 \(newValue)명으로 변경될 예정입니다.")
        }
        didSet {
            print("수강 인원이 \(oldValue)명에서 \(studentCount)명으로 변경 되었습니다.")
        }
    }
    //연산 프로퍼티
    var doubleValue: Int {
        get {
            return studentCount
        }
        set {
            studentCount = newValue
            print("수강 인원을 \(newValue)로 변경 중입니다.")
        }
    }
}

class MoreStudent: Student {
    override var doubleValue: Int {
        willSet {
            print("수강인원이 \(doubleValue)명에서 \(newValue)명으로 변경 예정입니다.")
        }
        
        didSet {
            print("수강인원이 \(oldValue)명에서 \(doubleValue)명으로 변경 되었습니다.")
        }
    }
}

let school: MoreStudent = MoreStudent()
school.studentCount = 15
print()
school.doubleValue = 30

지역변수와 전역변수


변수에는 클래스나 메서드, 구조체, 열거형 내부에서 접근이 가능한 지역 변수와 전역에서 접근이 가능한 전역변수가 존재한다.
이러한 지역변수와 전역 변수는 변수 안에 값을 저장하는 기능을 하는 저장변수라고 한다.
또한 지역변수전역변수를 연산을 수행하는연산 변수로 구현 할 수 있으며 뿐만 아니라 프로퍼티 감시자도 구현 할 수 있다.

다음은 전역 변수에 연산 변수를 구현한 코드이다.

var studentName: String = "이철수"
var studentID: Int = 12345
var studentInfo: String {
    get {   //get 메서드로 읽기 전용 상태로 구현
        "이름: \(studentName) 학번: \(studentID)"   //한 줄짜리 접근자는 return 생략 가능
    }
}
print(studentInfo)
  • 전역 변수와 전역 상수는 처음 접근할때 연산이 이루어지는 지연 연산된다. 그 자체로 지연 연산이 이루어 지므로 lazy 키워드는 생략한다. 반대로 지역변수와 지역상수는 그 차제로는 지연 연산이 되지 않는다.

타입 프로퍼티


인스턴스의 생성과 관계 없이 모든 인스턴스가 함께 공유하고 접근하여 값을 변경 할 수 있는 프로퍼티를 타입 프로퍼티라고한다. (다른 객체 지향 언어의 static과 유사하다.)
타입 프로퍼티의 종류도 두 가지로 분류되는데 저장 타입 프로퍼티는 상수와 변수로 선언이 가능하며 연산 타입 프로퍼티는 변수로만 선언이 가능하다.

저장 타입 프로퍼티는 반드시 초기에 초깃값을 설정해 주어야하며 지연 연산된다. 전역 변수, 전역 상수와 마찬가지로 그 자체로 지연 연산되므로 lazy 키워드는 생략한다.

class Customer {
    static let price: Double = 15000    //저장 타입 프로퍼티(상수)
    static var discountRate:Double = 0  //저장 타입 프로퍼티(변수)
    
    static var setDiscountRate: Double {  //연산 타입 프로퍼티
        get {
            return discountRate
        }
        set {
            discountRate = newValue
        }
    }
    
    func discountPrice() -> Double {
        return Self.price * Self.discountRate    //Self는 타입 자체를 말함
    }
}

var normalCustomer: Customer = Customer()
Customer.setDiscountRate = 0.95     //일반 고객 할인 비율 설정
print(normalCustomer.discountPrice())

var VIPCustomer: Customer = Customer()
Customer.setDiscountRate = 0.90     //VIP 고객 할인 비율 설정
print(VIPCustomer.discountPrice())

또한 위와 같이 타입 프로퍼티는 인스턴스 생성 없이 타입 이름으로 프로퍼티를 사용 할 수 있다는 특징이 있다.

profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글