본문 바로가기

멋쟁이 사자처럼 앱스쿨 1기/회고록

멋사 앱 스쿨 1기 15일차 회고록/TIL (22.10.05) - 클로저, Objective-C 맛보기

안녕하세요 진코드 93입니다~!

15일차에는 클로저에 대해서 알아보고 예제를 위주로 클로저를 다뤄봤어요~!

그리고 대망의 Objective-C...

새로 나오는 앱들이야 Swift를 주로 사용하지만 오래된 앱에는 아직도 Objective-C의 잔재가 많이 남아있기 때문에 iOS 앱 개발자가 되려면 그래도 최소한 읽고 어떠한 코드인줄은 알아야한다는 취지에서 Objective-C를 찍먹해보는? 시간을 가졌습니다...ㅎ

클로저

클로저

컴퓨터 공학 용어에서의 클로저(closure)는 함수나 클로저 표현식과 같은 독립적인 코드 블록과 코드 블록 주변에 있는 하나 이상의 변수가 결합된 것을 말한다.

오른쪽 코드에서 functionA는 functionB라는 이름의 함수를 반환한다.

사실 functionB는 functionB 내부 영역 밖에 선언된 counter 변수에 의존하기 때문에 functionA는 클로저를 반환하고 있다.

다시 말해, functionB는 counter 변수를 “잡고 있다(captured)” 또는 “가두고 있다(closed over)”라고 말할 수 있으므로 전통적인 컴퓨터 공학 용어인 클로저(closure)로 간주된다.

Swift에서는 클로저(closure)와 클로저 표현식(closure expression)용어가 혼용되기 시작했지만, 어쨌든 두 다 지원한다.

func functionA() -> () -> Int {

  var counter = 0
  
  func functionB() -> Int {
    return counter + 10
  }
  
  return functionB
}

let myClosure = functionA()
let result = functionB()

 

간단한 클로저 예제

func functionA(counter: Int) -> () -> Int {

  // functionA 함수 안에 functionB 함수 선언 - 중첩된 함수
  func functionB() -> Int {
    print("붙잡힌 counter는 \(counter) 입니다")
    
    // functionB 밖의 counter를 사용한 계산값을 반환한다
    return counter + 30
  }  

  let result = functionB()
  print("\(result)")

  // functionB() 이것은 functionB를 호출하는 문구
  // functionB   이것은 functionB 함수 그 자체를 지칭함
  return functionB
}

// functionA() 호출의 결과는 functionB 함수이고
// functionB 함수 안에는 counter값이 계속 붙잡혀 활용이 된다
// myClosure가 곧 functionB이기 때문에 myClosure가 존재하는 동안 counter도 붙잡혀 존재한다
let myClosure = functionA(counter: 9)
let result = myClosure()
print("\(result)")

 

간단한 클로저 예제 (배열의 정렬)

// 배열 정렬 예제
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var defaultNames = names.sorted()
print(defaultNames)

// 정렬 기준을 만들어주는 함수 만들기
func backward(_ s1: String, _ s2: String) -> Bool {

  // print("\(s1)과 \(s2)를 비교합니다")
  return s1 > s2
}

var reverseNames = names.sorted(by: backward)
print(reverseNames)
// 전역함수 backward와 똑같이 작동하는 클로저 표현식을 만들어
// sorted의 by: 매개변수로 인라인 클로저라는 구현법으로 바로 써서 보낸다
reverseNames = names.sorted(by: {(_ s1: String, _ s2: String) -> Bool in
  return s1 > s2
})
print(reverseNames)
// 한줄로도 가능
reverseNames = names.sorted(by: {(_ s1: String, _ s2: String) -> Bool in return s1 > s2})
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: {(_ s1, _ s2) -> Bool in return s1 > s2})
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: {s1, s2 in return s1 > s2})
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: {s1, s2 in s1 > s2})
print(reverseNames)
// 더 짧게
reverseNames = names.sorted(by: { $0 > $1 })
print(reverseNames)
// 최종
reverseNames = names.sorted(by: >)
print(reverseNames)

 

Array의 map 매소드 예제

// Array의 map 매소드 예제
let digitNames: [Int: String] = [
  0: "Zero",
  1: "One",
  2: "Two",
  3: "Three",
  4: "Four",
  5: "Five",
  6: "Six",
  7: "Seven",
  8: "Eight",
  9: "Nine"
]

let numbers: [Int] = [16, 58, 510]

// ["OneSix", "FiveEight", "FiveOneZero"] 배열 만들기
let strings: [String] = numbers.map { (number: Int) -> String in

  // 매개변수로 가져온 number는 상수이기 때문에, 변수로 다시 만들어줘야 변경 가능하다
  var number: Int = number
  var result: String = ""
  repeat {
    // result에 digitNames에서 key값 찾아서 대응 value 넣어주기
    // number % 10 문구는 number를 10으로 나눈 나머지, 그러니까 소숫점 앞 맨 아랫자리 수 한자리
    // 앞자리에 해당하는 문자열을 계속 앞에 더해주는 문자열 결합
    result = digitNames[number % 10]! + result
  
  // !처리는 피하고 싶어요 - 다른 방법
  /*
    if let name = digitNames[number % 10] {
      result = name + result
    } else {
      result = "?" + result
    }
  */
  
   // 또 다른 방법
   /*
    let name = digitNames[number % 10] ?? "?"
    result = name + result
   */
   
   // number는 Int이기 때문에, 10으로 나누면 수숫점 이하의 값은 내림처리한다.
    number /= 10
  } while number > 0
  return result
}
print("\(strings)")

 

캡처값 예제

func makeIncrementer(forIncrement amount: Int) -> () -> Int {

  var runningTotal: Int = 0

  func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
  }
  return incrementer
}

// 생성된 반환되는 함수는 runningTotal = 0, amount = 10으로 캡쳐되어 작동
let increFuncTen = makeIncrementer(forIncrement: 10)

// 생성된 반환되는 함수는 runningTotal = 0, amount = 7로 캡쳐되어 작동
let increFuncSeven = makeIncrementer(forIncrement: 7)
print("\(increFuncTen())") // 0 + 10 = 10
print("\(increFuncSeven())") // 0 + 7 = 7
print("\(increFuncTen())") // 10 + 10 = 20
print("\(increFuncSeven())") // 7 + 7 = 14
print("\(increFuncTen())") // 20 + 10 = 30
print("\(increFuncSeven())") // 14 + 7 = 21

// 두 함수의 관계는 참조 타입이라는 걸 알아보려 한다.
// 동일한 runningTotal 값을 공유하고 있다.
let alsoIncreFuncTen = increFuncTen
print("\(increFuncTen())") // 30 + 10 = 40
print("\(alsoIncreFuncTen())") // 0 + 10 = 10 ? -> 실제로는 50
print("\(increFuncTen())") // 40 + 10 = 50 ? -> 실제로는 60

// 필요하면 새로 만들어야...
let newIncreFuncTen = makeIncrementer(forIncrement: 10)
print("\(newIncreFuncTen())") // 0 + 10 = 10

 

이스케이프 클로저 (Escaping Closures) 예제

var comletionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosures(comletionHandler: @escaping () -> Void ) {
  comletionHandlers.append(comletionHandler)
}

func someFunctionWithNonescapingClosures(closure: () -> Void) {
  closure()
}

class SomeClass {
  var x = 0

  func doSomething() {
    someFunctionWithEscapingClosures { () -> Void in
      print("Hello")
      self.x = 100
    }

    someFunctionWithNonescapingClosures { () -> Void in
      print("World")
      x = 200
    }
  }
}

let instance = SomeClass()
instance.doSomething()
print("someFunctionWithNonescapingClosures - x : \(instance.x)") // 당연히 200

if let comletionHandler = comletionHandlers.first {
  comletionHandler()
  print("someFunctionWithEscapingClosures - x : \(instance.x)") // 200이 아니라 100
}

 

이스케이프 클로저 추가 예제

var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosures(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

func someFunctionWithNonescapingClosures(closure: () -> Void) {
    closure()
}

func sayHello() {
    print("Hello")
}

someFunctionWithNonescapingClosures(closure: sayHello)

class SomeClass {
    var x: Int = 0
    
    func doSomething() {
        someFunctionWithEscapingClosures { self.x += 100 }
        someFunctionWithNonescapingClosures { x += 200 }
        someFunctionWithEscapingClosures { self.x += 400 }
    }
}

let instance = SomeClass()
instance.doSomething()
print("someFunctionWithNonescapingClosures - x : \(instance.x)")

if let completionHandler = completionHandlers.first {
    completionHandler()
    print("someFunctionWithEscapingClosures - x : \(instance.x)")
}

if let completionHandler = completionHandlers.popLast() {
    completionHandler()
    print("someFunctionWithEscapingClosures - x : \(instance.x)")
}

print("someFunctionWithNonescapingClosures - x : \(instance.x)")

 

mutating 예제

class SomeClass {
  var name: String = ""

  func changeName(newName: String) {
    self.name = newName
  }
}

var someThingByClass = SomeClass()
someThingByClass.changeName(newName: "ned")
print("someThingByClass.name = \(someThingByClass.name)")

struct SomeStruct {
  var name: String = ""

  // 이 메소드에 의해서 프로퍼티가 바뀔 수 있음을 알리기 위해 mutating을 앞에 붙인다
  mutating func changeName(newName: String) {
    self.name = newName
  }
}

var someThingByStruct = SomeStruct()
someThingByStruct.changeName(newName: "뽀로로")
print("someThingByStruct.name = \(someThingByStruct.name)")

 

Objective-C 맛보기

예제 1

// Objective-C로 작성

#import <Foundation/Foundation.h>

// import는 C언어의 include와 달리 중복으로 가져오는 걸 방지해준다.
// C 언어 계열은 main 함수를 통해 처음 실행될 코드 위치를 찾아간다.
int main (int argc, const char * argv[]) {

    // NSAutoreleasePool 부터 [pool drain] 까지는 메모리 관리를 해주는 자동 영역이며
    // 그 안에 우리의 코드가 들어가게 된다.
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   
    // NSLog는 print문과 같은 식의 출력문
    // Objective-C의 문자열은 @"...." 로 표현한다.
    NSLog(@"Hello World");    
    [pool drain];
    return 0;
}

 

예제 2

// Objective-C로 작성

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {        
        int sum = 0;
        int a = 50;
        int b = 25;
        sum = a + b;       
        NSLog(@"Hello World\n\n\tMy name is Ned!\n");       

        // print("sum : \(sum)")
        NSLog(@"%i + %i = %i", a, b, sum);      
    }
    return 0;
}

 

예제 3

// Objective-C로 작성

#import <Foundation/Foundation.h>

int main ()
{
   int a = 30;
   if ( a < 20 ) {
       NSLog(@"a는 20보다 작습니다.");
   } else if ( a < 40 ) {
       NSLog(@"a는 40보다 작고 20보다 크거나 같습니다.");
   } else {
       NSLog(@"a는 40보다 크거나 같습니다.");
   }     
   NSLog(@"a는 %d입니다.", a);   
   return 0;
}

 



Calendar
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Visits
Today
Yesterday