[Swift] - 변수에 대해 잘 알고 있는 것이 맞습니까?


변수에 대한 내용을 점검해보자.


https://medium.com/codex/swift-5-do-you-really-know-about-variables-6073d48fc468

변수는 변명의 여지 없이 프로그래밍에서 중요하고, 변수 없이 프로그래밍은 거의 불가능에 가까울 것이다. 처음 개발을 시작하는 개발자들도 변수와 함수를 가장 먼저 공부한다. 하지만 개발을 오래하고 개발 경력이 좀 됐다 하더라도 변수에 대해 잘 안다고 말할 수 있을까?

변수란?

데이터를 받거나 저장하는 데이터 컨테이너다.

변수 선언하는 방법

var name = "Yoojin"
var age: Int = 35
var height: Double = 8.9
var isTall: Bool = {
  return height > 7
}()

변수를 선언하는 다양한 방법들이 있다.

변수 naming convention

1. 축약어를 사용하지 않는다.

mutableVariablemutVar로 사용한다던가, titleLabeltitleLbl과 같이 사용하지 않는다. 물론 자기는 알기 쉽겠지만 코딩은 기본적으로 다른 사람과 협업하는 작업이며 다른 사람이 봤을 때 한 번에 알기 쉽게 설명적이고, 분명해야 한다. 이를 테스트하는 방법으로, 내가 작성한 코드를 다른 개발자에게 보여줘보면 된다.

2. bool 타입 변수의 이름은 단일 보조자로 시작하게 한다.

아래의 예제르 보자.

struct User {
    var verifiedEmail: Bool
    var activated: Bool
    var haveAJob: Bool
}
let user = User(verifiedEmail: false, activated: true, haveAJob: true)

위 사용자의 프로퍼티에 접근하려면 아래와 같이 해야 한다.

user.verifiedEmail
user.activated
user.haveAJob

직접 읽으면 문법적인 오류가 있고 프로퍼티의 목적을 알기 어렵다는 것을 알 수 있다. 이를 아래와 같이 고치면 훨씬 좋다.

struct User {
    var didVerifyEmail: Bool
    var isActivated: Bool
    var hasAJob: Bool
}

user.didVerifyEmail
user.isActivated
user.hasAJob

변수와 프로퍼티의 차이는?

기본적으로는 같은 것이다. 데이터 컨테이너 자체를 얘기할 때 변수라 한다. 그리고 객체의 데이터 컨테이너를 프로퍼티라고 하는 것이다.

class User {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

위의 예제에서 name은 String타입의 변수이다.

let user = User(name: "Dylan")
user.name

그리고 username 프로퍼티가 있다.

Custom Getter, Setter

Swift는 변수의 게터와 세터를 커스텀할 수 있게 해준다. 기본적으로는 아래와 같다.

var name: String

name = "Dylan" 
print(name)

이를 아래와 같이 커스터마이징 할 수 있다.

var contactDictionary = [String: String]()

var name: String? {
    get { return contactDictionary["name"] } 
    set { contactDictionary["name"] = newValue }
}
name = "Dylan" 
print(name)

위 코드에서는 name의 값을 가져올 때, contactDictionary에서 값을 갖오고, 값을 설정할 때는 contactDictionary에 값을 설정한다.

연산 프로퍼티 vs 저장 프로퍼티

연산 프로퍼티와 저장 프로퍼티는 둘 다 setter와 getter가 있다. 여기에 추가로 연산 프로퍼티는 값을 가져오거나 값을 짱하기 전에 변수가 연산을 수행할 수 있게 한다.

var subtotal = 12.9
var tax = 1.95
var total: Double {
    subtotal + tax
}
print(total) // 14.85

subtotaltax는 저장 프로퍼티다. 얘네들은 값을 변수에 저장하고, 호출되었을 때 이 값들을 반환한다.

반면 total은 연산 프로퍼티다. total이 호출되면 값을 가지고 연산을 해서 연산된 값을 리턴한다. 연산 프로퍼티는 값을 저장하는 것이 아니라 말 그대로 연산을 한다.

non-void 함수가 있을 때 computed 변수를 사용하는 이유?

var subtotal = 12.9
var tax = 1.95
var total: Double {
    subtotal + tax
}
func getTotal() -> Double {
    subtotal + tax
}
print(total) // 14.85
print(getTotal()) // 14.85

위의 totalgetTotal()은 같은 값을 리턴하며, 동작도 같다. subtotaltax가 이미 클래스에서 제공이 되었기 때문에 연산 프로퍼티를 사용하는 것만으로 충분하다. 동적인 값을 위해 non-void 함수를 사용하는 것에 대해 고려해봐야 한다.

var subtotal = 12.9
var tax = 1.95
var total: Double {
    subtotal + tax
}
func getTotal(with deliveryFees: Double) -> Double {
    subtotal + tax + deliveryFees
}
print(total) // 14.85
print(getTotal(with: 5.68)) // 20.53
print(getTotal(with: 3.36)) // 18.21
print(getTotal(with: 2.8)) // 17.65

위 코드에서 getTotal()함수에 동적 값인 deliveryFees를 전달할 수 있지만, 이 또한 total 변수를 이용해서 같은 작업을 구현할 수 있다.

Function Type 변수

변수는 함수를 포함해서 다양한 타입의 데이터를 저장할 수 있다.

class Order {
  var getTotal: ((Double) -> Double)?
}

class Store {
  let subtotal = 12.95
  let tax = 2.34
  
  func getTotal(with deliveryFees: Double) -> Double {
    subtotal + tax + deliveryFees
  }
}

let order = Order()
let store()

order.getTotal = store.getTotal

print(order.getTotal!(4.5))