본문 바로가기
스파르타코딩 클럽/사전 캠프

13. 스파르타 코딩클럽 [사전캠프 - 클로저 / 객체지향 프로그래밍]

by UDDT 2025. 2. 19.

기본

클로저

    1. 클로저 : 코드에서 독립적으로 전달 및 사용할 수 있는 기능을 가진 코드 블록

        - 변수나 상수에 저장 가능

        - 함수의 인자로 전달 가능

// Closure 문법
{ (매개변수) -> 반환타입 in
	실행코드
}

   

    * 외부 변수나 상수의 값을 캡처하여 저장할 수 있음(클로저 선언 이후에 값이 변하면 최신의 값을 불러오기는 함.)

var number = 10

// 클로저 정의, number 변수 캡처
let captureClosure = {
    print(number)
}

number = 20

// 클로저를 실행하면, number 값은 20으로 출력.
captureClosure()  // 출력: 20

 

// 기본 클로저 형태
let greet = { (name: String) -> String in
	return "Hello, \(name)!"
}
print(greet("Uddt")) 
// "Hello, Uddt!"

// 함수의 인자로 클로저 전달 가능
func performAction(action: () -> Void) {
	action()
}

performAction {
	print("Action performed!")
}

 

 

함수를 클로저로 바꾸는 방법(문법 최적화)

// 함수
func name(parameters) -> Type {
	code
}
// 최적화1 : func 키워드 삭제
name(parameters) -> Type {
	code
}

// 최적화2 : 함수 이름(여기서는 name) 삭제
(parameters) -> Type {
	code
}

// 최적화3 : { } 위치 조정, in 추가
{ (parameters) -> Type in
	code
}

// 최적화4 : parameter, Return 값이 없으면 생략 가능
{ () in
	code
}

// 최적화5 : (), in 키워드도 생략 가능
{
	code
}

// 최적화6 : 줄바꿈 삭제 (가장 단순한 형태의 Closure)
{ code }

 

객체지향 프로그래밍(OOP, Object - Oriented Programming)

    1. 객체지향 프로그래밍(OOP): 객체(Object)를 기반으로 프로그램을 설계하는 방식

        * 주요 원칙

           - 캡슐화(Encapsulation) : 데이터를 숨기고 외부에는 필요한 인터페이스만 제공.

           - 상속(Inheritance) : 기존 클래스를 확장하여 새로운 클래스를 생성.

           - 다형성(Polymorphism) : 같은 메서드를 다양한 방식으로 동작하게 함.

           - 추상화(Abstrcation) : 불필요한 세부 사항을 숨기고 중요한 부분만 표현.

 

   2. 클래스와 객체

       - 클래스(Class) : 객체를 정의하기 위한 청사진

       - 객체(Object) : 클래스에서 생성된 실제 인스턴스

// OOP 기본 예제

class Animal {
	var name: String
    
    init(name: String) {
    	self.name = name
    }
    
    func makeSound() {
    	print("Some generic sound")
    }
}

class Dog: Animal {
	override func makeSound() {
    	print("Bark!")
    }
}

let dog = Dog(name: "Buddy")
dog.makeSound() 
// "Bark!"

 

실습

 클로저 구현하기

    1. 간단한 클로저 구현

숫자 배열 [1, 2, 3, 4, 5]를 생성하고, 클로저를 사용해 배열의 모든 값을 2배로 만든 결과를 출력하세요.
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)
// [2, 4, 6, 8, 10]

 

let doubled = numbers.map { $0 * 2 } 코드는 문법 최적화가 적용된 상태다.

최적화 전에는 어떤 코드였을까?

let numbers = [1, 2, 3, 4, 5]

let doubled = numbers.map({ (number: Int) -> Int in
    return number * 2
})

// 문법 최적화1 : parameter Type은 컴파일러가 인식할 수 있으므로 생략 가능
let doubled = numbers.map({ (number) -> Int in
    return number * 2
})

// 문법 최적화2 : return Type도 인식할 수 있으므로 생략 가능
let doubled = numbers.map({ (number) in
    return number * 2
})

// 문법 최적화3 : closure body에서 parameter 이름을 사용하고 있다면, parameter 이름과 in 생략 가능
let doubled = numbers.map({
    return number * 2
})

// 문법 최적화4 : number라는 이름으로 더이상 접근 불가. index parameter인 $n를 사용
let doubled = numbers.map({
    return $0 * 2
})

// 문법 최적화5 : closure body에 return문 하나만 사용하고 있다면 생략 가능
let doubled = numbers.map({
    $0 * 2
})

// 문법 최적화6 : 코드가 짧으면 이어쓰기 가능
let doubled = numbers.map({ $0 * 2 })

// 문법 최적화7 : closure가 유일한 parameter이면서 마지막 parameter일 때는 잘라내서 () 뒤에 작성
let doubled = numbers.map() { $0 * 2 }

// 문법 최적화8 : ()도 생략 가능
let doubled = numbers.map { $0 * 2 }

 

  2. 클로저 캡처 이해하기 

var counter = 0
let incrementCounter = {
    counter += 1
}
incrementCounter()
incrementCounter()
print(counter) // 출력: 2

 객체지향 프로그래밍 구현하기

   1. 동물 클래스 설계

Animal 클래스: name 속성과 makeSound() 메서드를 포함.
Dog 클래스: Animal을 상속받고 makeSound() 메서드를 오버라이드하여 "Bark!" 출력.
Cat 클래스: Animal을 상속받고 makeSound() 메서드를 오버라이드하여 "Meow!" 출력.
class Animal {
    var name: String

    func makeSound() {
        print("Sound is default")
    }

    init(name: String) {
        self.name = name
    }
}

class Dog: Animal {
    override func makeSound() {
        print("Bark!")
    }
}

class Cat: Animal {
    override func makeSound() {
        print("Meow!")
    }
}

let dog = Dog(name: "Woody")
dog.makeSound()
// Bark!

let cat = Cat(name: "Tosi")
cat.makeSound()
// Meow!

 

심화

클로저 Capture

    보통 우리가 'Capture'라고 하면 스크린샷처럼, 그 장면을 기억하는 것으로 생각하게 된다.

var number = 10

// 클로저 정의, number 변수 캡처
let captureClosure = {
    print(number)
}

number = 20

// 클로저를 실행하면, number 값은 20으로 출력.
captureClosure()  // 출력: 20

 

 number가 10인 상태로 캡처를 했으면,

 number가 20으로 바뀌어도 captureClosure를 불러오면 10이 나와야하는거 아닌가?!

 

 

Closure Capture의 종류(값 캡처, 참조 캡처)

    1. 값 캡처(Default Capture) : 외부 변수의 현재 값을 캡처하는 것.

      let captureClosure = { print(number) }로 정의된 클로저는 number의 현재 값인 10을 캡처한 것이 맞음.

 

    2. 참조 캡처(Explicit Capture) : 클로저가 외부 변수에 참조를 캡처하도록 명시할 수 있음.

       이 경우 number를 변경할 때마다 클로저 내에서 참조하는 값이 바뀜.

 

var number = 10

// 클로저 정의, number 변수 캡처 (사실상 이것도 참조캡처지만, 편의상 값 캡처처럼 이해)
let captureClosure = { print(number) }
captureClosure()  // 출력: 10

number = 20
captureClosure()  // 출력: 20

 

 위의 코드에서는 변수의 참조가 캡처된 것으로, number의 값이 변경된 이후 클로저를 호출했기 때문에,

클로저가 최신 상태의 number 값을 참조한 것.

최근댓글

최근글

skin by © 2024 ttuttak