본문 바로가기
프로그래밍/Spring

17. 스위프트의 프로퍼티와 메서드에 대한 이해

by Mr-후 2019. 2. 24.
반응형

스위프트의 프로퍼티와 메서드에 대한 이해


슬슬 집중도가 떨어지고 있다. 주중반을 넘어가면 힘이 빠지는 법인데 오늘도 꾸준히 포스팅을 작성한다. 

오늘 첫 포스팅은 스위프트의 프로퍼티와 메서드에 대한 내용을 간략하게 정리한다. 


1. 프로퍼티 

저장프로퍼티와 연산 프로퍼티, 타입 프로퍼티로 나눌 수 있다. 

저장프로퍼티는 일반적으로 인스턴스의 변수 또는 상수를 의미하며 구조체와 클래스에서만 사용가능하고, 연산 프로퍼티는 특정 연산을 실행한 결과값의 의미하는데 클래스, 구조체, 열거형에 쓰일 수 있다. 타입프로퍼티는 특정 타입에 사용되는 프로퍼티을 말한다. 

간단하게 요약하자면 

*** 프로그래밍 언어에서 사용되던 인스턴스 변수는 저장 프로퍼티로, 클래스 변수는 타입 프로퍼티로 구분지을 수 있다. ***


책에 보면 구조체와 크래스의 저장 프로퍼티에 대해서 다음과 같은 설명이 있다.

구조체의 저장 프로퍼티가 옵셔널이 아니더라도, 구조체는 저장 프로퍼티를 모두 포함하는 이니셜라이저를 자동으로 생성하지만 클래스의 저장 프로퍼티는 옵셔널이 아니라면 프로퍼티 기본값을 지정해주거나 사용자정의 이니셜라이저를 통해 반드시 초기화해주어야 합니다. 또 클래스 인스턴스의 상수 프로퍼티는 인스턴스가 초기화(이니셜라이즈)될 때 한 번만 값을 할당할 수 있으며, 자식 클래스에서 이 초기화를 변경(재정의)할 수 없습니다. 


저장 프로퍼티의 선언 및 인스턴스 생성 예제 

struct CoordinatePoint {

var x: Int //저장 프로퍼티

var y: Int

}


//구조체는 기본적으로 저장 프로퍼티를 매개변수로 갖는 이니셜라이저가 있다. 

let branchPoint: CoordinatePoint = CoordinatePoint(x: 10, y: 5) 


//대리점의 위치 정보 

class BranchPosition {

var point: CoordinatePoint

//저장 프로퍼티(변수) - 위치(point)는 변경될 수 있음을 뜻한다. 

let name: String //저장프로퍼티 (상수) 


//프로퍼티 기본값을 지정해주지 않는다면 이니셜라이저를 따로 정의해주어야 한다. 

int(name: String, currentPoint: CoordinatePoint) { 

self.name = name

self.point = currentPoint

}

}


//사용자정의 이니셜라이저를 호출해야만 한다.프로퍼티 초기값을 할당할 수 없기 때문에 인스턴스 생성이 불가능하다.

let myBranch: BranchPosition = BranchPosition(name: "명동", currentPoint: branchPoint) 


* 클래스의 저장 프로퍼티에 초기값을 지정해주면 따로 사용자정의 이니셜라이저(init())를 구현해줄 필요가 없다. 

인스턴스를 생성할 때 이니셜라이저를 통해 초기값을 보내야 하는 이유는 프로퍼티가 옵셔널이 아닌 값으로 선언되어 있기 때문이다. 꼭 값이 있어야하는 프로퍼티가 아니라면 옵셔널로 선언을 하고 이니셜라이저에서 옵셔널 프로퍼피에 꼭 값을 할당해주지 않아도 된다. 

var point: CoordinatePoint? 


지연 저장프로퍼티(Lazy Stored Properties)가 있는데 호출이 있어야 값을 초기화하는 프포퍼티다. 지연 저장프로퍼티는 var키둬드를 사용하는 변수로 정의한다. 

lazy var point: CoordinatePoint = CoordinatePoint() 


연산프로퍼티. 

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


연산 프로퍼티의 정의와 사용 예: 

 struct CoordinatePoint {

var x: Int //저장 프로퍼티 

var y: Int 


//대칭 좌표 

var oppositePoint: CoordinatePoint { //연산 프로퍼티 

//접근자

get {

return CoordinatePoint(x: -x, y: -y) 

}

//설정자 

set(opposite) {

x = -opposite.x

y = -opposite.y

}

}

}

var myPosition: CoordinatePoint = CoordinatePoint(x: 10, y: 20) 


//현재 좌표

print(myPosition) //10, 20 

//대칭 좌표 

print(myPosition.oppositePoint) //-10, -20 

//대칭 좌표를 (15, 10)으로 설정하면 

myPosition.oppositePoint = CoordinatePoint(x: 15, y: 10) 

//현재 좌표는 -15, -10으로 설정. 

print(myPosition) // -15, -10 


*관용적인 표현으로 newValue로 매개변수 이름을 대신할 수 있다. 

x = -newValue.x 

x = -newValue.y 


읽기 전용은 get 메서드만 사용하면 된다. 


프로퍼티 감시자. 

프로퍼티의 값이 변경됨에 따라 적절한 액션을 취할 수 있다. 프로퍼티의 값이 새로 할당될 때마다 호출하고 이때 변경되는 값이 현재의 값과 같더라도 호출된다. 감시자는 지연 저장 프로퍼티에 사용할 수 없고 일반 저장 프로퍼티에만 적용할 수 있다. 

감시자에는 값이 변경되기 직전에 호출하는 willSet메서드와 프로퍼티의 값이 변경된 직후에 호출하는 didSet메서드가 있다. 각 메서드는 매개변수를 하나씩 가지고 있다. willSet은 변경될 값이고 didSet은 변경되기 전의 값이다. 역시 관용적인 표현으로  newValue, oldValue라는 매개변수 이름이 자동 지정된다. 


프로퍼티 감시자 사용예제: 

class Account {

var credit: Int = 0 {

willSet {

print("잔액이 \(credit)워너에서 \(newValue)원으로 변경될 예정")

}

didSet {

print("잔액이 \(oldValue)원에서 \(credit)원으로 변경됨")

}

}

let myAccount: Account = Account()  //잔액이 0워너에서 1000원으로 변경될 예정.

myAccount.credit = 1000 //잔액이 0원에서 100으로 변경됨. 


클래스를 상속받은경우 기존의 연산 프로퍼티를 재정의하여 감시자를 구현할 수도 있다. 


* 입출력 매개변수와 프로퍼티 감시자 

만약 프로퍼티 감시자가 있는 프로퍼티를 함수의 입출력 매개변수의 전달인자로 전달한다면 항상 WillSet과 didSet감시자를 호출합니다. 함수 내부에서 값이 변경되든 되지 않든 함수가 종료되는 시점에 값을 다시 쓰기 때문입니다. 


타입 프로퍼티. 

인스턴스가 아닌 타입 자체에 속하는 프로퍼티를 타입 프로퍼티라고 하고 타입 자체에 영향을 미치는 프로퍼티이다. 

인스턴스 생성 여부와 상관없이 타입 프로퍼티의 값은 하나며, 그 타입의 모든 인스턴스가 공통으로 사용하는 값, 모든 인스턴스에서 공용으로 접근하고 값을 변경할 수 있는 변수(C언어의 static변수와 유사) 등을 정의할 때 유용하다. 


타입 프로퍼티는 저장 타입 프로퍼티는 변수 또는 상수로 선언할 수 있고 연산 타입 프로퍼티는 변수로만 선언할 수 있다. 저장 타입 프로퍼티는 반드시 초기값을 설정해야하며 지연 연산된다. 

다중 스레드 환경이라고 하더라도 단 한번만 초기화된다는 보장을 받는다.  지연 연산된다고 해서 lazy 키워드로 표시해주지 않는다. 


타입프로퍼티의 사용 예제: 

class Account {

static let dollarExchangeRate: Double = 1000.0 //타입 상수 


var credit: Int = 0

var dollarValue: Double {

get {

return Double(credit) / Account.dollarExchangeRate

}

set {

credit = Int(newValue * Account.dollarExchangeRate) 

print("잔액을 \(newValue)달러로 변경 중입니다.")

}

}

}


프로퍼티의 키 경로. 

키 경로를 잘 활용하면 프로토콜과 마찬가지로 타입 간의 의존성을 낮추는 데 많은 도움을 준다. 애플의 프레임워크는 키-값 코딩 등 많은 곳에 키 경로를 활용하므로 애플 프레임워크 기반의 애플리케이션을 만든다면 잘 알아두면 많은 도움이 된다. 


2. 메서드 

메서드는 특정 타입에 관련된 함수를 뜻한다. (타입 : 클래스나, 구조체나, 열거형이거나...)

각각의 인스턴스가 특정 작업을 실행하는 기능을 캡슐화하기 위해 인스턴스 메서드를 정의할 수 있다. 타입 자체와 관련된 기능을 실행하기 위해 타입 메서드를 정의할 수도있다.  타입 메서드는 기존의 프로그래밍 언어에서의 클래스 메서드와 유사한 개념이다. 

자신의 프로퍼티 값을 수정할 때 클래스의 인스턴스 메서드는 크게 신경쓸 필요가 없지만 구조체나 열거형 등은 값 타입이므로 메서드 앞에 mutating 키워드를 붙여서 해당 메서드가 인스턴스 내부의 값을 변경한다는 것을 명시해야 한다. 

mutating func levelUp() {

print("Level Up!")

level += 1

}


self프로퍼티. 

모든 인스턴스는 암시적으로 생성된  self 프로퍼티를 갖는다. 자바의 this와 비슷하게 인스턴스 자기 자신을 가리키는 프로퍼티인데, self프로퍼티는 인스턴스를 더 명확히 지칭하고 싶을 때 사용한다. 

func jumpLevel(to level: Int) {

print("Jump to \(level)")

self.level = level

}


타입 메서드. 

메서드에도 인스턴스 메서드와 타입 메서드가 있다. 타입 메서드 = 클래스 메서드 

메서드 앞에 static 키워드를 사용한다. 클래스의 타입 메서드는 static키워드와 class키워드를 사용할 수 있는데 static으로 정의하면 상속후 메서드 재정의(오버라이드)가 불가능하고 class로 정의하면 상속 후 메서드 재정의가 가능하다. 

인스턴스 메서드에서는 self가 인스턴스를 가리킨다면 타입 메서드의 self는 타입을 가리킨다. 타입 메서드 내부에서 타입 이름과 self는 같은 뜻이라고 볼 수 있다. 


타입 프로퍼티와 타입 메서드의 사용예제: 

//시스템 음량은 한 기기에서 유일한 값이어야 한다. 

struct SystemVolume {

//타입 프로퍼티를 사용하면 언제나 유일한 값이 된다. 

static var volume: Int = 5

//타입 프로퍼티를 제 어하기 위해 타입 메서드를 사용한다. 

static func mute() {

self.volume = 0    //SystemVolumne.volume = 0과 동일한 표현 

}

}


//내비게이션 역할은 여러 인스턴스가 수행할 수 있다. 

class Navigation {

//내비게이션 인스턴스마다 음량을 따로 설정할 수 있다. 

var volume: Int = 5


//길 안내 음성 재생

func guideWay() {

//내비게이션 외 다른 재생원 음소거

System.Volumne.mute()

}


//길 안내 음성 종료 

func finishGuideWay() {

//기존 재생원 음량 복구 

SystemVolume.volume = self.volume

}

}

SystemVolume.volume = 10 

let myNavi: Navigation = Navigation() 


myNavi.guideWay()

print(SystemVolume.volume) // 0 

myNavi.finishGuideWay()

print(SystemVolume.volume) // 5 









반응형

'프로그래밍 > Spring' 카테고리의 다른 글

19. 스위프트의 접근제어  (0) 2019.02.25
18. 인스턴스의 생성과 소멸  (0) 2019.02.25
16. 구조체와 클래스  (0) 2019.02.23
15. 옵셔널(Optional)  (0) 2019.02.22
14. Swift의 다양한 함수들  (0) 2019.02.21