본문 바로가기
Xcode/Swift - PlayGround

(Swift) Closure - Capturing Value

by 후르륵짭짭 2021. 9. 3.
728x90
반응형

 

안녕하세요.

정말 오랜만에 글을 작성하는 것 같습니다.

요즘엔 개발에 대한 의지가 많이 줄어서 블로그 활동도 뜸해진 것 같습니다.

내가 지금 행복을 위해서 일하고 있는지 돈을 위해서 일하고 있는지 잘 모를 때가 있습니다 ㅎㅎㅎㅎ.

그래서 저 스스로를 다짐하기 위해서 블로그를 다시 시작하려 합니다.

https://www.youtube.com/watch?v=DMDi2S-PEP0 

요즘 굉장히 자주 듣고 있는 정승환의 도망가자 입니다. 최근 들어서 도망가고 싶을 때가 있습니다 ㅎㅎㅎㅎ

 

** Capturing Value **

캡쳐링 벨류는 클로저 내부에 값을 이미지 처럼 사진을 찍고 있다가 나중에 지속적으로 사용하는 것을 의미합니다.

프로그래밍으로 따지면 주소값을 저장(캡쳐링) 했다가 사용 하는 것을 의미합니다.

바로 코드를 보시면 이해가 될 겁니다.

 

** 예제 **

func travel2() -> (String) -> Void {
    var counter = 1

    return { //중첩 함수라 생각하면 된다.
        print("\(counter). I'm going to \($0)")
        counter += 1
    }
}

let result2 = travel2()

result2("London") // 0, I'm going to London
result2("Seoul") // 1, I'm going to Seoul
result2("Tokyo") // 2, I'm going to Tokyo

Travel2() 함수를 보면 Void -> String -> Void 인 것을 확인 할 수 있습니다.

즉, Void 값을 받고 내부 함수는 (String)을 받고 최종 적으로 Void를 반환 하는 것을 의미합니다.

counter의 갯수가 내부 함수에서 증가시는데, 지속적으로 증가하는 것을 볼 수 있습니다.

travel2() 함수는 끝나지만 그 내부의 counter는 계속 살아 있습니다.

func travel3() -> ((String) -> Void ) -> Void {

    var list : [Int] = []

    return { item in
        item("Hello \(list.count) \(list)")
        list.append(0)
    }
}

let result = travel3()

let input : (String) -> Void = { item in
    print("I'm in \(item)")
}

result(input)
result(input)
result(input)

이렇게 배열을 저장 할 수도 있습니다.

조금 다른 점은 이렇게 함수를 입력으로 넣을 수도 있다는 겁니다.  

 

 - 클로저 정리 -

더보기

여러개의 입력이 있는 클로저 

 var test : (String) -> (Int) -> (Bool) -> String = { item in
        print(item)
        return { itemInt in
            print(itemInt)
            return { itemBool in
                print(itemBool)
                return "Hello"
            }
        }
    }
    
    let aaa = testclass.test("World")(10)(false) // Hello

 

 

** 내가 활용한 방식 **

회사 일을 하면서 기능 구현을 해야하는데 그런적이 있었다.

함수가 끝나더라도, 배열에 함수를 저장해서 그 함수를 필요할 때 사용하고, 함수를 새롭게 정의하면 기존의 것이 사라지고,,,

그런 것을 구현 해보고 싶었다.

class Test {

    //만약 결과 값을 특정 함수가 변형 되기 전 까지만 사용 하고 싶을 때, 사용하면 좋을 것 같다.
    var realCaptureValue : () -> ( @escaping (String) -> (String) ) -> [(String) -> (String)] = {

        var capturValue : [(String) -> (String)] = []

        return { closure in

            capturValue.append(closure)

            return capturValue
        }
    }

}

//넣어줄 함수
let item : (String) -> String = { item in
    return "haha \(item)"
}
//넣어줄 함수
let item2 : (String) -> String = { item in
    return "yup \(item)"
}

let testclass = Test()
var result = testclass.realCaptureValue()
result(item) //[(String) -> String]
result(item2) // [(String) -> String, (String) -> String]
var list = result(item) // [(String) -> String, (String) -> String, (String) -> String]

list[0]("Hello") // "haha Hello"
list[1]("Hello")// "yup Hello"

result = testclass.realCaptureValue()

result(item2) // [(String) -> String] : 초기화 됨

 

** Capturing Closure의 기술? **

이건 캡쳐링 Value에서 주소값과 값 타입의 관계에 대한 정리한 코드들이다. 

https://velog.io/@kimdo2297/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture

 

클로저 캡쳐에 대해서 (about closure capture)

출처 :

velog.io

여기에 정말 자세히 정리 되어 있다.

 - 기본 코드 -

class A {

    var name : String

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

    deinit{
        print("\(self.name) escaped!")
    }

}

func delay(_ second : Int , clousre : @escaping () -> ()){
    let time = DispatchTime.now() + .seconds(second)

    DispatchQueue.main.asyncAfter(deadline: time, execute: {
        print("째깍째깍")
        clousre()
    })
}

- 예제 1 -

func demo1(){
    let a = A(name: "hello")
    print("A init")
    delay(1, clousre: {
        print("inside closure :\(a)") //사용 할 때 a를 캡쳐링 했다. 따라서 사라지지 않는다.
    })
    print("bye")
}

demo1()

//
A init
bye
째깍째깍
inside closure :__lldb_expr_129.A
hello escaped!

 

- 예제 2 - 

func demo2() {

    var a = A(name: "a")
    print("A init")
    delay(1, clousre: {
        print("inside closure : \(a)") //a를 캡처링 해서 사용하기 전에, a가 B로 바뀜 그래서 B가 출력 됨, // 주소값 동일
        // 즉, a를 사용 할 때 캡쳐링을 한다.
    })
    a = A(name: "b")
    print("after closure : \(a)")

}

demo2()

//
A init
a escaped!
after closure : __lldb_expr_131.A
째깍째깍
inside closure : __lldb_expr_131.A
b escaped!

 

- 예제 3 -

func demo3() {

    var a = A(name: "a")
    print("A init")
    delay(1, clousre: { [a] in
        print("inside closure : \(a)") // capture list를 사용하면 클로저 생성 시점에 캡쳐 할 수 있다.
    })
    a = A(name: "b")
    print("after closure : \(a)")

}

demo3()

//
A init
after closure : __lldb_expr_133.A
b escaped!
째깍째깍
inside closure : __lldb_expr_133.A
a escaped!

- 예제 4 -

//value를 변경할 경우
//레퍼런트 타입
func demo4() {

    let a = A(name: "a")
    print("A init")
    delay(1, clousre: { [a] in
        print("inside closure : \(a)") //capture list를 사용해도 a의 내부 값은 변경이 된다.
        //왜냐 레퍼런스 타입이기 때문에
    })
    a.name = "hello"
    print("after closure : \(a)")

}

demo4()

//
A init
after closure : __lldb_expr_135.A
째깍째깍
inside closure : __lldb_expr_135.A
hello escaped!

- 예제 5 -

struct AS {

    var name : String

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

}

func demo5(){

    var a = AS(name: "a")
    print("A init")

    delay(1, clousre: { [a] in
        print("inside closure : \(a)") // 캡처하는 대상이 value 타입이라면 값을 받기 때문에 변경 되지 않는다.
    })
    a.name = "world"
    print("after closure : \(a)")

}

demo5()

//
A init
after closure : AS(name: "world")
째깍째깍
inside closure : AS(name: "a")

 

참고 사이트 : 

정의 글 :

https://onelife2live.tistory.com/7

 

[Swift] Capturing Values 값을 캡쳐한다는 것

Swift에서 값을 캡쳐(Capture)한다는 것의 의미를 한 번 알아보겠습니다. 클로저(Closure)는 정의된 주변의 컨텍스트에 있는 상수나 변수들을 캡처할 수 있습니다. 그런 다음 클로저는 상수와 변수를

onelife2live.tistory.com

 

정의 글 : 

https://www.hackingwithswift.com/sixty/6/11/capturing-values

 

Capturing values - a free Hacking with Swift tutorial

Was this page useful? Let us know! 1 2 3 4 5

www.hackingwithswift.com

 

 

728x90
반응형

댓글