momodudu.zip

#2 Swift - Functions and Closures 본문

ios/Swift

#2 Swift - Functions and Closures

ally10 2019. 11. 25. 21:53

xSwift에는 Function, Closure, Method가 존재한다.

 

처음 Swift를 봤을 땐 이 세가지가 좀 헷갈렸는데, 자세히 의미를 들여다보면 c++을 제대로 알고 있다면 별로 어렵지 않다.

그중에서도 이번 글 에서는 Function과 Clousre에 대해서 정리해본다.

 

1. Function

Function은 Closure의 특정 한 분류로 볼 수 있다. 이름을 가지고 있고, 변수 캡쳐 scope도 한정적이다.

function의 선언은 아래와 같이 하면 된다.

 

func FunctioName(paramName: paramType) -> returnType{

  //// ... body

}

func greeting(person: String) -> String{
    return "hello \(person)"
}

 

그 외에도 여러가지 선언 문법들이 존재한다. 추가적으로 전 글에도 나와있듯이 리턴 타입 자체를 tuple로 줄 수도 있다.

그리고 swift compiler 내부에서 타입 추론을 해주므로, 위 예시에서 return을 빼주어도 상관은 없다고 한다.

 

 

그리고 기본적으로 함수 인자로 전달되는 값들은 모두 상수여서, 함수 body내부에서 변경이 불가능하다.

즉 c++에서 처럼 포인터로 넘겨서 변경하는것이 불가능하다. 

하지만 swift에서는 inout keyword를 써서 함수의 영향력이 미치는 scope를 밖까지 넓힐 수 있다.

 

func swapNumbers(_ a: inout Int, _ b: inout Int){
    var tmp: Int
    
    tmp = a
    a = b
    b = tmp
}

var a = 3
var b = 5
swapNumbers(&a, &b)

print(a)
print(b)

 

위 코드를 출력시켜보면 a,b가 swap된 것을 확인할 수 있다.

대신 이 Inout keyword paramter는 c++의 레퍼런스 키워드처럼 생긴 &를 붙여주어야 한다.

function 자체도 하나의 타입으로 간주되고 있으므로, function 자체도 함수 인자, 혹은 리턴값으로 기본 변수처럼 사용가능하다.

 

추가적으로 Nested Function이란 것도 있다. 별 건 없고, 함수안의 함수라는 의미인데 여기서 주의해야 할 점은 함수의 scope이다.

변수 캡쳐가 가능한 scope가 Nested function을 감싸고 있는 바깥 영역의 함수까지라고 보면 된다.

즉, nested function이 선언된 context가 변수를 캡쳐할 수 있는 scope다.

 

2. Closure

 

Swift에서 Clousre와 Function의 차이는, Function이 조금 더 좁은 개념이라고 보면 된다.

- Global function은 어떤 변수도 캡쳐하지 않는 이름을 갖는 Closure

- Nested function은 그 function을 감싸고 있는 scope의 변수를 캡쳐하는 이름을 갖는 Closure

 

Closure는 c++에서의 lamda와 매우 흡사한 개념이라고 보면 될 것 같다.

예를들어, c++에서 stl에 정의된 sort함수 사용시에 comparator를 인자로 넣어줄 수 있는데, 이때 comparator를 람다로 선언이 가능했다.

마찬가지로 swift에서도 기본 library로 제공하는 sorted(by:) 함수에도 clousre를 넣을 수 있다.

 

 

var names = ["aa", "zz", "cc"]

names.sorted(by: {(s1:String, s2:String) -> Bool in
    return s1 > s2
} )

 

여기에서 매우매우 축약도 가능한데... 왜 그렇게까지 해야하는지 모르겠다 (...)

그냥 명시적으로 잘 써놓으면 안되나..ㅠ

 

예를들어, 위의 코드에서 아래와 같은 단계로 축약이 계속해서 가능하다.

 

names.sorted(by: {(s1:String, s2:String) -> Bool in
    return s1 > s2
} )

// omitting return type.
names.sorted(by: {(s1:String, s2:String) in return s1 > s2} )

// omitting return keyword( type inferrence )
names.sorted(by: {(s1:String, s2:String) in s1 > s2 })

// omitting arguement name
names.sorted(by: { $0 > $1 })

// use operator method
names.sorted(by: >)

 

ㅎㅎ ..

 

Trailing Clousre

clousre expression의 한 종류. function call의 마지막 parameter로 clousre전달시에 사용할 수 있다.

func SomeFunction(clousre:()->Void){
    // body
}

 

위처럼 ()->void type의 closure를 paramter로 취하는 함수의 경우 기본적으로 아래와 같이 호출할 수 있다.

SomeFunction(clousre: { ()->Void in
    // clousre's body
})

Trailing closure구문으로는 아래와 같이 축약 할 수 있다.

 

SomeFunction(){
    // clousre's body
}

paramter의 유일한 인자가 clousre 인 경우에는 () 도 생략이 가능하다.

 

 

Escaping Closure

function call의 return 이후에 호출되는 clousre를 보고 escaping clousre라고 한다.

func SomeFunction(clousre:()->Void){
    // body
    clousre()
}

 

위의 코드 같으면 SomeFunction안에서 반드시 closure()가 호출된다. 즉, 함수의 리턴 전에 Clousre가 호출된다.

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

func SomeFunctionWithCompletionHandler(handler: @escaping ()->Void){
    completionHandler.append(handler)
}

 

위와같이 clousre paramter type앞에 @escaping keyword를 붙이면 escaping closure를 인자로 취할 수 있다.

이렇게 되면 handler 인자로 받은 클로져를 함수 내부에서 호출하지 않고, 클로져 배열에 append해서 clousre를 저장할 수 있다.

즉, 실행되지 않고 저장만 된다는 것이다. 즉, 클로저 내부에서 캡처한 변수의 scope가 function 밖까지 확장이 된다는 얘기다.

 

처음에 봤을땐 굳이 왜...? 라는 생각이 들었는데, 원래대로라면 SomeFunction 에서 parameter로 취한 clousre를 

SomeFunction이 끝난 이후에 clousre를 호출하도록 만들 수 있다, 즉 함수의 호출 순서를 control할 수 있다는 점에서 유용하다고 한다.

 

추가적으로 auto closure라는 syntax도 존재하는데, 가독성이 떨어진다고 자주 쓰지 말라고 한다 (...)

 

 

 

 

 

 

'ios > Swift' 카테고리의 다른 글

#6 Swift - Extension  (0) 2019.11.28
#5 Swift - Optional chaining  (0) 2019.11.27
#4 Swift - Initialization  (0) 2019.11.26
#3 Swift - Properties  (0) 2019.11.26
#1 Swift 기본 문법 - Basics : constant, variables, Optionals  (0) 2019.11.22