본문 바로가기
iOS/Swift

Swift-4. 클로저, 프로퍼티

by 헤콩 2023. 2. 24.
반응형

** edwith - Swift 프로그래밍 입문 강의 참고 **

 

# 클로저 #

- 실행가능한 코드 블럭

- 함수와 다르게 이름정의는 필요하지 않지만, 매개변수 전달과 변환 값이 존재할 수 있다는 점이 동일

- 함수는 이름이 있는 클로저

- 일급객체로 전달인자, 변수, 상수 등에 저장 및 전달이 가능

// 클로저 기본 문법
{ (/* 매개변수 목록*/) -> /* 반환 타입 */ in
    /* 실행 코드 */
}
// 클로저 사용
// sum이라는 상수에 클로저 할당
let sum: (Int, Int) -> Int = { (a: Int, b: Int) in
    return a + b
}

let sumResult: Int = sum(1, 2)
print(sumResult)    // 3
// 함수의 전달인자로서의 클로저
// 함수 내부에서 원하는 코드블럭을 실행할 수 있습니다.
let add: (Int, Int) -> Int
add = { (a: Int, b:Int) in
    return a + b
}

let substract: (Int, Int) -> Int
substract = { (a: Int, b: Int) in
    return a - b
}

let divide: (Int, Int) -> Int
divide = { (a: Int, b: Int) in
    return a / b
}

func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int {
    return method(a, b)
}

var calculated: Int
calculated = calculate(a: 50, b: 10, method: add)
print(calculated)    // 60

calculated = calculate(a:50, b:10, method: substract)
print(calculated)    // 40

calculated = calculate(a:50, b:10, method: divide)
print(calculated)    // 5

calculated = calculate(a: 50, b:10, method: { (left: Int, right: Int) -> Int in
    return left * right
} )
print(calculated)    // 500

 

# 다양한 클로저 표현 #

- 후행 클로저 : 함수의 매개변수 마지막으로 전달되는 클로저는 후행 클로저(trailing closure)로, 함수 밖에 구현할 수 있습니다.

- 반환타입 생략 : 컴파일러가 클로저의 타입을 유추할 수 있는 경우 매개변수, 반환 타입을 생략할 수 있습니다.

- 단축 인자 이름 : 전달인자의 이름이 굳이 필요없고, 컴파일러가 타입을 유추할 수 있는 경우 축약된 전달인자 이름($0, $1, $2...)을 사용할 수 있습니다.

- 암시적 반환 표현 : 반환 값이 있는 경우, 암시적으로 클로저의 맨 마지막 줄은 return 키워드를 생략하더라도 반환 값으로 취급합니다.

 

/* 기본 클로저 */
func calculate(a: Int, b:Int, method: (Int, Int) -> Int) -> Int {
    return method(a, b)
}

var result: Int

/* 1. 후행 클로저 */
// 클로저가 함수의 마지막 전달인자 일 때, 마지막 매개변수 이름(method)를 생략한 후 함수 소괄호 외부에 클로저를 구현할 수 있습니다.
result = calculate(a: 10, b:10) { (left: Int, right: Int) -> Int in
    return left + right
}
print(result)	// 20

/* 2. 반환타입 생략 */
// calculate(a:b:method:) 함수의 method 매개변수는 Int 타입을 반환할 것이라는 사실을 컴파일러도 추측할 수 있기 때문에 굳이 클로저에서 반환타입을 명시해 주지 않아도 됩니다.
// 대신 in 키워드는 생략할 수 없습니다.
result = calculate(a: 10, b: 10, method: { (left:Int, right:Int) in
    return left + right
} )
print(result)	// 20

/* 3. 단축 인자 이름 */
// 클로저의 매개변수 이름이 굳이 불필요하다면 단축 인자이름을 활용할 수 있습니다.
// 단축 인자이름은 클로저의 매개변수의 순서대로 $0, $1, $2... 처럼 표현합니다.
result = calculate(a: 10, b: 10, method: {
    return $0 + $1
} )
result = calculate(a: 10, b:10) {	// + 후행 클로저
    return $0 + $1
}
print(result)	// 20

/* 4. 암시적 반환 표현 */
// 클로저가 반환하는 값이 있다면 클로저의 마지막 줄의 결과값은 암시적으로 반화값으로 취급
result = calculate(a: 10, b: 10) {
    $0 + $1
}
result = calculate(a: 10, b: 10) { $0 + $1 }
print(result)	// 20

 

 

# 프로퍼티 #

프로퍼티의 종류

- 인스턴스 저장 프로퍼티

- 타입 저장 프로퍼티

- 인스턴스 연산 프로퍼티

- 타입 연산 프로퍼티

- 지연 저장 프로퍼티

 

 

정의와 사용

- 프로퍼티는 구조체, 클래스, 열거형 내부에 구현 가능 (열거형 내부에는 연산 프로퍼티만 구현 가능)

- 연산 프로퍼티는 var로만 선언 가능

- 연산 프로퍼티를 읽기 전용으로는 구현할 수 있지만, 쓰기 전용으로는 구현 불가능

- 연산 프로퍼티를 읽기 전용으로 구현하려면 get 블럭 작성. (읽기 전용은 get 블럭 생략 가능)

- 읽기, 쓰기 모두 가능하게 하려면 get 블럭과 set 블럭 모두 구현

- set 블럭에서 암시적 매개변수 newValue 사용 가능

struct Student {
    // 인스턴스 저장 프로퍼티
    var name: String = ""
    var 'class': String = "튤립"
    var koreanAge: Int = 1
    
    // 인스턴스 연산 프로퍼티
    var westernAge: Int {
    	get {
            return koreanAge - 1
        }
        set(inputValue) {
            koreanAge = inputValue + 1
        }
    }
    
    // 읽기전용 인스턴스 연산 프로퍼티 - 인스턴스 메서드 selfIntroduce()를 간단하게 대체 가능
    var selfIntroduction: String {
    	get {
            return "저는 \(self.class)반 \(name)입니다."
        }
    }
    
    // 타입 저장 프로퍼티
    static var typeDescription: String = "학생"
    
    // 읽기전용 타입 연산 프로퍼티 - 읽기전용에서는 get 생략 가능
    static var selfIntroduction: String {
        return "학생 타입입니다."
    }
    print(Student.selfIntroduction)	// 학생 타입입니다.
    
    
    
    // 인스턴스 메서드
    func selfIntroduce() {
    	print("저는 \(self.class)반 \(name)입니다.")
    }
    // 타입 메서드
    static func selfIntroduce() {
    	print("학생 타입입니다.")
    }
    
    // 인스턴스 생성
    var been: Student = Student()
    been.koreanAge = 24
    
    // 인스턴스 저장 프로퍼티 사용
    been.name = "been"
    print(been.name)	// been
    
    // 인스턴스 연산 프로퍼티 사용
    print(been.selfIntroduction)	// 저는 튤립반 been입니다.
}

 

 

응용

struct Money {
    var currencyRate: Double = 1100
    var dollar: Double = 0
    var won: Double {
    	get {
            return dollar * currencyRate
        }
        set {
            dollar = newValue / currencyRate
        }
    }
}


var moneyInMyPocket = Money()
moneyInMyPocket.won = 11000
print(moneyInMyPocket.won)	// 11000

moneyInMyPocket.dollar = 5
print(moneyInMyPocket.won)	// 5500

 

 

# 프로퍼티 감시자 #

- 프로퍼티 감시자 사용 시, 프로퍼티의 값이 변경될 때 원하는 동작 수행 가능

- 값이 변경되기 직전, willSet 블럭이 호출 됨.

- 값이 변경된 직후에는 didSet 블럭이 호출 됨.

- 둘 중 필요한 하나만 구현해 주어도 무관

- 변경되려는 값이 기존 값과 똑같더라도 프로퍼티 감시자는 항상 동작

- willSet 블럭에서는 암시적 매개변수 newValue를, didSet 블럭에서는 oldValue를 사용할 수 있습니다.

- 프로퍼티 감시자는 연산 프로퍼티에는 사용 불가능

- 프로퍼티 감시자는 함수, 메서드, 클로저, 타입 등의 지역/전역 변수에 모두 사용 가능

// 정의 및 사용
struct Money {
    // 프로퍼티 감시자 사용
    var currencyRate: Double = 1100 {
    	willSet(newRate) {
            print("환율이 \(currencyRate)에서 \(newRate)으로 변경될 예정입니다.")
        }
        didSet(oldRate) {
            print("환율이 \(oldRate)에서 \(currencyRate)으로 변경되었습니다.")
        }
    }
    
    // 프로퍼티 감시자 사용
    var dollar: Double = 0 {
    	// willSet의 암시적 매개변수 이름 newValue
        willSet {
            print("\(dollar)달러에서 \(newValue)달러로 변경될 예정입니다.")
        }
        // didSet의 암시적 매개변수 이름 oldValue
        didSet {
            print("\(oldValue)달러에서 \(dollar)달러로 변경되었습니다.")
        }
    }
    
    // 연산 프로퍼티
    var won: Double {
    	get {
            return dollar * currencyRate
        }
        set {
            dollar = newValue / currencyRate
        }
    }
}


var moneyInMyPocket: Money = Money()

// 환율이 1100.0에서 1150.0으로 변경될 예정입니다.
moneyInMyPocket.currencyRate = 1150
// 환율이 1100.0에서 1150.0으로 변경되었습니다.

// 0.0달러에서 10.0달러로 변경될 예정입니다.
moneyInMyPocket.dollar = 10
// 0.0달러에서 10.0달러로 변경되었습니다.

print(moneyInMyPocket.won)	// 11500.0

 

 

 

 

반응형

댓글