iOS/Swift

[Swift] Closure์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž

๊ฒฝํ‘ธ 2023. 3. 13. 18:30
๋ฐ˜์‘ํ˜•

์•ˆ๋…•ํ•˜์„ธ์š”~

 

์˜ค๋Š˜์€ ๊ธฐ์กด์— ์ž‘์„ฑํ–ˆ๋˜ ํด๋กœ์ € ํฌ์ŠคํŒ…์ด

์ข€ ์–ด์„คํ”ˆ ๋Š๋‚Œ์ด ๋“ค์–ด ๋‹ค์‹œ ํ•˜๋‚˜์˜ ํฌ์ŠคํŒ…์œผ๋กœ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

Closure๋ž€?

Closure๋Š” Swift์—์„œ ์ด๋ฆ„์ด ์—†๋Š” ์ต๋ช… ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

ํ•จ์ˆ˜์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์•„ ์ถœ๋ ฅ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Swift์—์„œ๋Š” closure๊ฐ€ ์ผ๊ธ‰ ๊ฐ์ฒด๋กœ ์ทจ๊ธ‰๋˜์–ด, ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ closure๋Š” ์ค‘์ฒฉ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋” ๋ณต์žกํ•œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Closure์˜ ํ‘œํ˜„ ๋ฐฉ๋ฒ•

Swift์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ closure๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

{ (parameters) -> ReturnType in
   statements 
}

 

์˜ˆ๋ฅผ ๋“ค์–ด, map ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ closure์˜ ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 
let numbers = [1, 2, 3, 4, 5]
let mappedNumbers = numbers.map({ (number: Int) -> Int in
   return number * 2 
}) 
print(mappedNumbers) // [2, 4, 6, 8, 10]

 

์œ„ ์ฝ”๋“œ์—์„œ, map ๋ฉ”์„œ๋“œ๋Š” ํด๋กœ์ €๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

ํด๋กœ์ €๋Š” number๋ผ๋Š” Int ํƒ€์ž…์˜ ์ธ์ž๋ฅผ ๋ฐ›์•„, number * 2๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

1๊ธ‰ ๊ฐ์ฒด๋กœ์„œ์˜ Closure

Swift์—์„œ๋Š” closure๊ฐ€ 1๊ธ‰ ๊ฐ์ฒด๋กœ ์ทจ๊ธ‰๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

closure๋Š” ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜์— ์ €์žฅํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

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

let result = addClosure(1, 2)

print(result) // 3

 

์œ„ ์ฝ”๋“œ์—์„œ, addClosure๋Š” ๋‘ ๊ฐœ์˜ Int ํƒ€์ž… ์ธ์ž๋ฅผ ๋ฐ›์•„ ๋”ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” closure์ž…๋‹ˆ๋‹ค.

์ด closure๋Š” addClosure ์ƒ์ˆ˜์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

 

Closure์˜ ์ถ•์•ฝํ˜•ํƒœ

Swift์—์„œ๋Š” closure์˜ ์ถ•์•ฝํ˜•ํƒœ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

 

1. ์ถ•์•ฝํ˜•ํƒœ ์—†๋Š” ํด๋กœ์ € ํ‘œํ˜„์‹

 

{ (๋งค๊ฐœ๋ณ€์ˆ˜: ๋งค๊ฐœ๋ณ€์ˆ˜ํƒ€์ž…) -> ๋ฐ˜ํ™˜ํƒ€์ž… in
  // ํด๋กœ์ € ๊ตฌํ˜„
}

// ์˜ˆ์‹œ
let greeting = { (name: String) -> String in
    return "Hello, \(name)!"
}
print(greeting("Swift"))
// ์ถœ๋ ฅ: "Hello, Swift!"

 

2. ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž… ์ƒ๋žต

 

{ (๋งค๊ฐœ๋ณ€์ˆ˜) -> ๋ฐ˜ํ™˜ํƒ€์ž… in
  // ํด๋กœ์ € ๊ตฌํ˜„
}

// ์˜ˆ์‹œ
let greeting = { name -> String in
    return "Hello, \(name)!"
}
print(greeting("Swift"))
// ์ถœ๋ ฅ: "Hello, Swift!"

 

3. ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ๋ฐ˜ํ™˜ํƒ€์ž… ์ƒ๋žต

 

{ ๋งค๊ฐœ๋ณ€์ˆ˜ in
  // ํด๋กœ์ € ๊ตฌํ˜„
}

// ์˜ˆ์‹œ
let greeting = { name in
    return "Hello, \(name)!"
}
print(greeting("Swift"))
// ์ถœ๋ ฅ: "Hello, Swift!"

 

4. ์ถ•์•ฝ ์ธ์ž ์ด๋ฆ„ ์‚ฌ์šฉ

 

{ $0 + $1 }

// ์˜ˆ์‹œ
let add = { $0 + $1 }
print(add(1, 2))
// ์ถœ๋ ฅ: 3

 

5. ํ›„ํ–‰ ํด๋กœ์ €

someFunction(argument1, argument2) { ๋งค๊ฐœ๋ณ€์ˆ˜ in
  // ํด๋กœ์ € ๊ตฌํ˜„
}

// ์˜ˆ์‹œ
let numbers = [1, 2, 3]
let mappedNumbers = numbers.map { $0 * 2 }
print(mappedNumbers)
// ์ถœ๋ ฅ: [2, 4, 6]

 

 

@autoclosure

@autoclosure๋Š” ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ํ‘œํ˜„์‹์„ ์ž๋™์œผ๋กœ closure๋กœ ๋ณ€ํ™˜ํ•ด ์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ์ธ์ž๋กœ ์ „๋‹ฌ๋œ closure๋ฅผ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 
func printResult(_ result: @autoclosure () -> Void) {
   print("before closure")
   result()
   print("after closure")
}

printResult(print("Hello, world!"))

 

์œ„ ์ฝ”๋“œ์—์„œ, printResult ํ•จ์ˆ˜๋Š” @autoclosure ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ

์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” print("Hello, world!") ํ•จ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ closure๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ closure๋ฅผ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

@escaping

@escaping์€ closure๊ฐ€ ํ•จ์ˆ˜์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์‹คํ–‰๋  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค. ์ด ์†์„ฑ์€ closure๋ฅผ escaping closure๋กœ ์ •์˜ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

 
func myFunc(closure: @escaping () -> Void) {
   DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
      closure()
   } 
}

myFunc { print("Hello, world!") }

 

์œ„ ์ฝ”๋“œ์—์„œ myFunc ํ•จ์ˆ˜๋Š” @escaping ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ closure๋ฅผ escaping closure๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ •์˜๋œ closure๋Š” ํ•จ์ˆ˜์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

myFunc ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” 1์ดˆ ํ›„์— closure๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด์ฃ .

 

Closure์˜ ์บก์ฒ˜์™€ ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ

Closure๋Š” ํ•ด๋‹น closure๊ฐ€ ์„ ์–ธ๋œ ์ฝ˜ํ…์ŠคํŠธ์—์„œ ์ •์˜๋œ ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜๋ฅผ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด closure๊ฐ€ ์„ ์–ธ๋œ ์‹œ์ ์˜ ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
   var runningTotal = 0
   func incrementer() -> Int {
      runningTotal += amount
      return runningTotal
   }
   return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)

print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30

let incrementByFive = makeIncrementer(forIncrement: 5)
print(incrementByFive()) // 5
print(incrementByFive()) // 10
print(incrementByTen()) // 40

 

์ด ์ฝ”๋“œ๋Š” ์บก์ฒ˜์™€ ์บก์ฒ˜๋ฆฌ์ŠคํŠธ๋ฅผ ์„ค๋ช…ํ•  ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์˜ˆ์‹œ๋ผ๊ณ  ํ•ด์„œ ๊ฐ€์ ธ์™€๋ดค์Šต๋‹ˆ๋‹ค.

 

์ฝ”๋“œ๋ฅผ ์„ค๋ช…ํ•ด ๋ณด์ž๋ฉด

์œ„ ์ฝ”๋“œ์—์„œ makeIncrementer ํ•จ์ˆ˜๋Š” closure๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๋œ closure๋Š” runningTotal ๋ณ€์ˆ˜๋ฅผ ์บก์ฒ˜ํ•˜์—ฌ, amount ๋งŒํผ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

incrementByTen๊ณผ incrementByFive๋Š”

๊ฐ๊ฐ makeIncrementer ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐ˜ํ™˜๋œ closure๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‘ closure๋Š” ๊ฐ๊ฐ ๋‹ค๋ฅธ runningTotal ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋˜๊ณ  ์„œ๋กœ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

Closure์—์„œ๋Š” ์บก์ณ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ์บก์ฒ˜๋˜๋Š” ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜์˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, [weak self]๋ฅผ ์บก์ณ ๋ฆฌ์ŠคํŠธ๋กœ ์ง€์ •ํ•˜๋ฉด

ํ•ด๋‹น closure์—์„œ self๋ฅผ ๊ฐ•ํ•œ ์ฐธ์กฐ๊ฐ€ ์•„๋‹Œ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 
 
class MyClass { 
   var myProperty: String = "Hello"
   func myFunc() {
      DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
         guard let self = self else { return }
            print(self.myProperty)
      }
   }
}

let myObject = MyClass() 
myObject.myFunc()

 

์ฝ”๋“œ์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•ด ๋ณด์ž๋ฉด

์œ„ ์ฝ”๋“œ์—์„œ MyClass ํด๋ž˜์Šค๋Š” myFunc ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ closure๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด closure๋Š” myProperty ํ”„๋กœํผํ‹ฐ๋ฅผ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

myFunc ๋ฉ”์„œ๋“œ์—์„œ๋Š” DispatchQueue.main.asyncAfter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ closure๋ฅผ 1์ดˆ ํ›„์— ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ด๋•Œ, [weak self]๋ฅผ ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ๋กœ ์ง€์ •ํ•˜์—ฌ self๋ฅผ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, MyClass ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•ด์ œ๋˜๋ฉด closure๊ฐ€ ์‹คํ–‰๋  ๋•Œ self๋Š” nil์ด ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด guard let self = self else { return }๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ self๊ฐ€ nil์ธ ๊ฒฝ์šฐ์—๋Š”

๋” ์ด์ƒ closure๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

 

 


 

 

Closure์˜ ์ข…๋ฅ˜, ํ‘œํ˜„ ๋ฐฉ๋ฒ•, 1๊ธ‰ ๊ฐ์ฒด๋กœ์„œ์˜ ํŠน์ง•

์ถ•์•ฝ ํ˜•ํƒœ, @autoclosure, @escaping, ์บก์ฒ˜, ์บก์ฒ˜ ๋ฆฌ์ŠคํŠธ ๋“ฑ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ดค์Šต๋‹ˆ๋‹ค~

 

@autoclosure๋ฅผ ์œ ์ผํ•˜๊ฒŒ ์•„์ง ์‚ฌ์šฉํ•ด๋ณด์ง€ ์•Š์•˜๋˜ ๊ฒƒ ๊ฐ™๋„ค์š”.

ํ˜น์‹œ๋‚˜ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ด ํฌ์ŠคํŒ…์— ์ถ”๊ฐ€ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ด๋งŒ ๐Ÿ‘‹๐Ÿป ๐Ÿ‘‹๐Ÿป ๐Ÿ‘‹๐Ÿป 

๋ฐ˜์‘ํ˜•