swift의 함수는 일급 객체이므로 하나의 데이터 타입으로 사용할 수 있다. 함수를 데이터 타입으로 사용할 수 있다는 것은 함수를 전달인자로 받을 수도, 반환 값으로 돌려줄 수도 있다는 의미이다.
C언어는 일반선언문과 함수선언문의 연속이라고 모 교수가 설명했던 것처럼 하나의 프로그램은 하나의 큰 함수라고 말할 수 있다.
기본적으로 함수라는 것 자체에는 변함이 없지만 상황이나 위치에 따라 다른 용어로 부르고 있다.
조건문이나 반복문은 소괄호를 생략할 수 있던 것과는 달리, 함수에서는 소괄호를 생략할 수 없다.
swift의 함수는 재정의(오버라이드)와 중복 정의(오버로드)를 모두 지원한다. 즉, 매개변수의 타입이 다른 같은 이름의 함수를 만들 수 있고 매개변수의 개수가 다른 같은 이름의 함수를 만들 수 있다.
함수의 기본 형태는 아래와 같다.
func 함수 이름(매개변수...) -> 반환 타입 {
실행 구문
return 반환 값
}
func hello(name: String) -> String {
return"Hello \(name)"
//"Hello \(name)"
}
let name: String = hello(name: "silverCastle")
print(name)
결과
Hello silverCastle
// 매개변수가 필요 없는 함수라면 매개변수 위치를 공란으로 비워두자.
func hello() -> String {
return "Hello silverCastle"
}
print(hello())
결과
Hello silverCastle
매개변수가 여러 개 필요한 함수라면 쉼표로 매개변수를 구분해 여러 개 정의할 수 있다.
func hello(from myName: String, to yourName: String) -> String {
return "Hello, \(yourName)! I'm \(myName)"
}
print(hello(from: "silverCastle", to: "goldCastle"))
결과
Hello, goldCastle! I'm silverCastle
함수를 호출할 때 매개변수 이름을 사용했는데 이와 더불어 전달인자 레이블을 지정해 함수 호출 시 이를 사용할 수 있다.
매개변수 이름과 전달인자 레이블을 지정할 때의 형태는 다음과 같다.
func 함수 이름(전달인자 레이블 매개변수 이름: 매개변수 타입, 전달인자 레이블 매개변수 이름: 매개변수 타입...) -> 반환 타입 {
실행 구문
return 반환 값
}
즉, 함수 내부에서 전달인자 레이블을 사용할 수 없으며, 함수를 호출할 때는 매개변수 이름을 사용할 수 없다.
만약, C언어처럼 전달인자 레이블을 사용하고 싶지 않다면?
와일드카드 식별자를 사용해 함수를 호출할 때 C언어와 같이 호출할 수 있다.
또한 매개변수에 기본값을 지정할 수 있다. 매개변수가 전달되지 않으면 기본값을 사용한다.
매개변수로 몇 개의 값이 들어올지 모를 때, 가변 매개변수를 사용할 수 있다. 이때 받아올 수 있는 범위는 0개 이상이다.
func hello(me: String, friends: String...) -> String {
var result: String = ""
for friend in friends {
result += "Hello " + friend + "! "
}
return "I'm " + me + result
}
print(hello(me: "silverCastle ",friends: "bronzeCastle","goldCastle","platinumCastle"))
결과
I'm silverCastle Hello bronzeCastle! Hello goldCastle! Hello platinumCastle!
함수의 전달인자로 값을 전달할 때는 보통 값을 복사해서 전달하지만 값이 아닌 참조를 전달하려면 입출력 매개변수를 사용해야한다.
C언어의 포인터처럼 값 타입 데이터의 참조를 전달인자로 보내면 함수 내부에서 참조하여 원래 값을 변경한다. 참조는 inout 매겨변수로 전달될 변수 또는 상수 앞에 &를 붙여서 표현한다.
var numbers: [Int] = [1, 2, 3]
func nonReferenceParameter(arr: [Int]) {
var copiedArr: [Int] = arr
copiedArr[1] = 100
}
func referenceParameter(arr: inout [Int]) {
arr[1] = 100
}
nonReferenceParameter(arr: numbers)
print("참조하지 않은 결과: ", numbers[1])
referenceParameter(arr: &numbers)
print("참조한 결과: ", numbers[1])
결과
참조하지 않은 결과: 2
참조한 결과: 100
값의 반환이 굳이 필요하지 않은 함수가 종종 있다. 이럴 때 반환 타입을 Void로 표기하거나 아예 반환 타입 표헌을 생략한다.
func hello() {
print("hello, swift world!")
}
hello()
결과
hello, swift world!
매개변수 타입과 반환 타입으로 구성된 하나의 타입으로 사용할 수 있다.
typealias CaculateTwoInts = (Int, Int) -> Int
func add (a: Int, b: Int) -> Int {
return a+b
}
func multiple (a: Int, b: Int) -> Int {
return a*b
}
var number: CaculateTwoInts = add
print(number(10, 20))
number = multiple
print(number(10, 20))
결과
30
200
함수의 중첩은 함수 안에 함수를 넣을 수 있다는 의미이다. 지금까지의 예제들은 모듈 어디서든 사용할 수 있는 전역함수이다. 그러나 함수 안의 함수로 구현된 중첩 함수는 상위 함수의 몸통 블록 내부에서만 함수를 사용할 수 있다.(물론, 외부에서 사용할 수 없는 것은 아니다.)
아래의 예제는 원점인 0으로 이동하는 함수의 구현이다.
typealias MoveFunc = (Int) -> Int
func moveToZero (_ shouldGoLeft: Bool) -> MoveFunc {
func goRight(_ position: Int) -> Int {
return position + 1
}
func goLeft(_ position: Int) -> Int {
return position - 1
}
return shouldGoLeft ? goLeft : goRight
}
var position: Int = 5
let goZero: MoveFunc = moveToZero(position > 0)
while position != 0 {
print(position)
position = goZero(position)
}
print("원점 도착!")
결과
5
4
3
2
1
원점 도착!
종료되지 않는다는 의미는 정상적으로 끝나지 않았다는 의미이다. 즉, 오류를 내뱉고 싶다거나 중대한 시스템 오류를 보고하기 위해 사용하는데 이를 비반환 함수 또는 비반환 메서드라고 한다.
비반환 함수는 반환 타입을 Never라고 명시하면 된다.
func crash() -> Never {
fatalError("Something wrong!")
}
crash()
결과
__lldb_expr_54/MyPlayground.playground:84: Fatal error: Something wrong!