Swifter {Swift Developer}

메뉴

10. 함수 정의와 호출

[vc_row][vc_column][vc_custom_heading text=”3장 함수와 클래스
” font_container=”tag:h1|text_align:left” use_theme_fonts=”yes”][vc_separator][vc_column_text]현대적인 언어의 프로그래밍을 한다면 필수적으로 알아야 할 함수와 클래스, 구조체, 열거형도 Swift언어(에서는 간단하게 사용할 수 있습니다. 함수는 기본 파라미터를 사용하고 구조체는 클래스 수준으로 강력한 기능을 제공합니다. 이를 처리하기 위해 필요한 기능도 가지고 있습니다. 여러가지 값을 한번에 리턴해주는 튜플과 처리를 하나로 하는 폐쇄, 여러가지 파라미터를 필요라하는 함수를 하나의 파리미터로 처리할 수 있는 쿼리등의 기능이 있습니다. 또한 클래스의 기능을 확장할 수 있는 확장이나 필요한 메소드와 속성을 임시적으로 정의할 수 있는 프로토콜등이 제공됩니다.[/vc_column_text][vc_separator][/vc_column][/vc_row][vc_row][vc_column][vc_custom_heading text=”10. 함수 정의와 호출
” use_theme_fonts=”yes”][vc_separator][vc_custom_heading text=”(1) 기본 함수 정의와 호출

” use_theme_fonts=”yes”][vc_column_text]Swift언어의 함수는 전역함수와 중첩 함수(함수 정의안에 정의된 함수), 메소드(형식 메소드와 인스턴스 메소드), 폐쇄(클로저)가 있습니다. 기본 정의하는 형태는 다음과 같습니다.

예로, 문자열 배열의 처음에 문자열을 추가하는 addFrontText()가 있다고 가정해 봅시다. 추가대상이 되는 문자열, 앞뒤로 추가하는 문자열등 3개의 인수를 지정하는 함수를 고려하면 아래와 같이 정의할 수 있습니다.
[/vc_column_text][vc_column_text]

[code lang=”swift”]
func addFrontText(strArr arr: Array<String>, frontStr front: String) -> Array<String> {
var newArr = arr // 인수를 변경할 수 없기 때문에 var로 선언
newArr.insert(front, atIndex: 0) // 문자열 front를 배열 앞에 추가
return newArr // 문자열이 추가된 배열을 리턴
}
[/code]

[예제 3-1] 함수정의[/vc_column_text][vc_column_text]이 함수를 호출하려면 아래와 같이 선언할 수 있습니다.

[code lang=”swift”]
var arr = addFrontText(strArr: ["가", "나", "다"], frontStr: "응")
// 결과: ["응", "가", "나", "다"]
[/code]

함수 정의에서 선언하지 않는 경우를 제외하고는 인수 변수의 값은 변경할 수 없습니다. 이 말은 내부 인수에 선언된 변수는 함수 정수에서 상수로 처리됩니다.

인수를 변경할 수 있는 경우는 그 함수를 호출하는 측에서 고려해본다면, 함수 호출시 인수에 변수를 전달하면 그 변수가 변경될 가능성이 있는 경우, 실행단계에서 어떤 변수가 변경되었는지 바로 확인하기 어려워 버그가 발생할 가능성이 높습니다. 반대로 인수를 변경할 수 없는 경우에는 이런 문제를 해결할 수 있습니다.

참고로 필자가 Objective-C언어로 앱개발을 할 때 const를 인수로 지정하면 값을 변경할 수 없게 해서 해결하던 경우가 있지만 가끔 const를 빼먹고 사용하지 못한 경우 오류가 발생한 적이 있습니다. 그렇지만 Swift언어에서는 어떠한 것도 지정하지 않고 자동으로 변경할 수 없습니다. 즉, 자동으로 안정성을 가지는 Swift언어가 우수합니다.

또한, 인수를 변경해서 추가하려면, 위 예제 함수의 시작부분처럼 var newArr = arr 처럼 변수를 선언해야 합니다. 즉, 인수선언에 var를 선언하면 번거로움을 줄일 수 있고 호출자의 변수를 다시 작성하는 것이 아니라 호출자 변수를 다시 변경해서 복사하기 위한 호출자 변수는 변경되지 않다는 것을 기억하기 바랍니다.[/vc_column_text][vc_custom_heading text=”(2) 외부 인수

” use_theme_fonts=”yes”][vc_column_text]함수 정의에서 외부 인수명은 생략할 수 있습니다. 함수 정의에서 인수 앞에 “_”을 넣으면 호출시 외부인수명을 생략할 수 있습니다. 외부 인수명을 생략표기는 C언어등에서 이미 자주 사용하던 함수 스타일이지만, 각 인수가 무엇을 할당하는지 알기 어렵기 때문에 어떤 인수를 몇번째 배치하는지 기억해야 합니다. 또한 Objective-C언어에서는 외부인수명이 기본적으로 부여된 형태로 API가 정의되어 있습니다. Swift언어는 Objective-C언어와 C언어 스타일 모두 대응하도록 되어 외부 인수명을 부여하거나 생략할 수 있습니다.

외부 인수명을 생략하면 함수 정의 외부 인수 생략이외에 2번째 인수부터 명시적으로 외부 인수가 없다는 것을 나타내기 위해 “_ 내부인자명: 인수형”에 “_”을 추가해야 합니다. 예제 3-1을 그대로 적용해보면 다음과 같습니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func addFrontText(var arr: Array<String>, _ front: String) -> Array<String> {
arr.insert(front, atIndex: 0) // 문자열 front를 배열 앞에 추가
return arr // 문자열이 추가된 배열을 리턴
}

addFrontText(["가", "나", "다"], "응")
[/code]

[/vc_column_text][vc_column_text]
또한, 외부 인수명과 내부 인수명을 동일하게 정의할 수 있습니다. 1번째 인수는 동일한 외부인수명과 내부인수명을 지정하고 함수를 정의하고 두번째 인수부터 외부인수명을 생략하고 내부인수명으로 외부 인수를 지정할 수 있습니다.

[code lang=”swift”]
func addFrontText(var arr arr: Array<String>, front: String) -> Array<String> {
arr.insert(front, atIndex: 0) // 문자열 front를 배열 앞에 추가
return arr // 문자열이 추가된 배열을 리턴
}

addFrontText(arr: ["가", "나", "다"], front:"응")
[/code]

[/vc_column_text][vc_custom_heading text=”(3) 기본 인수값

” use_theme_fonts=”yes”][vc_column_text]Swift언어는 인수의 기본값을 “인수명: 인수형 = 기본값”형태로 지정할 수 있습니다.  외부 인수명은 추가 및 생략도 가능하지만, 기본값은 여러 개의 인수로 설정한 경우 애매할 경우를 예방하기 위해 외부 인수명을 추가하는 것이 바람직합니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func addFrontText(var strArr arr: Array<String>, frontStr front: String = "응") -> Array<String> {
arr.insert(front, atIndex: 0)
return arr
}

var arr1 = ["가", "나", "다"]
var arr2 = addFrontText(strArr: arr1) // 결과: ["응", "가", "나", "다"]
var arr3 = addFrontText(strArr: arr1, frontStr: "하") // 결과: ["하", "가", "나", "다"]

[/code]

[예제 3-2] 기본 인자값을 가진 함수 정의 예제
[/vc_column_text][vc_column_text]외부 인수명을 선언하지 않고 여러 개의 인자에 기본값을 선언한 경우, 자동으로 내부 인수명을 외부 인수명으로도 사용이 가능합니다. 즉, 함수를 호출하는 측에서 부분적으로 인수를 준다고 해도 몇번째 인수에 대한 값인지를 명확하게 알 수 있습니다.

그럼 2개의 인수에 기본값을 선언하고 호출할 경우에 외부 인수명을 노출해서 어떤 쪽의 인수에 값을 전달하는지를 보여주는 예제를 살펴봅시다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func testString(str1 str1: String = "!!!", str2: String = "***") ->String {
return str1 + str2
}

testString() // 결과: "!!!***"
testString(str1: "###") // 결과: "###***"
testString(str2: "$$$") // 결과: "!!!$$$"
testString(str1: "%%%", str2: "&&&") // 결과: "%%%&&&"
[/code]

[예제 3-3] 내부인수명이 자동으로 외부 인수명이 되는 함수 [/vc_column_text][vc_custom_heading text=”(4) 가변 인자

” use_theme_fonts=”yes”][vc_column_text]Swift언어는 함수 정의할 때 가변길이 인수를 사용할 수 있지만, 가변되는 인수는 1개만 가능합니다. Swift 버전1.x에서는 마지막 인수만 가변적으로 사용할 수 있었지만, Swift 버전2.x부터는 이 제한이 없어졌습니다.

가변인수는 인수형 뒤에 “…”을 붙여서 정의합니다. 함수 호출시 내부인수의 변수에 인수를 배열로 저장합니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func testString(str1: String = ",", str2: String…) ->String {
var value = ""
for s in str2 {
value += s + str1
}
return value
}

var str = testString(",", str2: "가", "나", "다") // 결과: "가,나,다,"
[/code]

[예제 3-4] 가변인자 함수 정의 예제
[/vc_column_text][vc_custom_heading text=”(5) 리턴값

” use_theme_fonts=”yes”][vc_column_text]함수는 반드시 리턴값이 필요하지는 않습니다. 리턴값이 없는 함수는 리턴 형을 “Void”로 명시적으로 지정하거나 다음과 같이 리턴값을 선언하지 않고 정의합니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func output(str: String, cnt: Int) {
for _ in 0..<cnt {
print(str)
}
}
[/code]

[예제 3-5] 리턴형 생략한 함수정의
[/vc_column_text][vc_column_text]그리고 Swift언어의 함수는 여러개의 리턴값을 가질수 있고 튜플을 사용하여 클래스나 구조체정도의 큰 데이터구조를 준비하지 않고 호출자에서 튜플의 각각 값의 의미를 파악할 수 있습니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func runAdd(runcode: String) -> (code:Int, msg:String) {
if runcode == "0" {
return (0, "추가명령을 실행합니다")
} else {
return (404, "오류가 발생했습니다. 오류코드:" + runcode)
}
}

let run1 = runAdd("0")
run1.code // 결과: 0
run1.msg // 결과: "추가명령을 실행합니다"
let run2 = runAdd("1")
run2.code // 결과: 404
run2.msg // 결과: "오류가 발생했습니다. 오류코드:1"
[/code]

[예제 3-6] 리턴형 생략한 함수정의
[/vc_column_text][vc_custom_heading text=”(6) 참조 인수

” use_theme_fonts=”yes”][vc_column_text]Swift언어의 함수에서는 선언하지 않은한 인수 변수값을 변경할 수 없습니다. 단, 인수에 “inout”을 지정하면 해당 인수가 참조형태로 보내지기 때문에 함수에서 값을 변경할 수 있습니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func valueUp(inout value:Int) {
value = value + 1
}

var test = 9
valueUp(&test)
test // 결과: 10

func textSum(inout strArr arr: Array<String>, frontStr front: String) -> Void {
arr.insert(front, atIndex: 0)
}

var arr = ["가", "나", "다"]
textSum(strArr: &arr, frontStr: "응")
arr // 결과: ["응", "가", "나", "다"]
[/code]

[예제 3-7] 인수 참조로 전달하는 함수 정의
[/vc_column_text][vc_column_text]예제에서 valueUp()과 textSum()의 test와 arr값이 변경되는지를 살펴보십시오.  Inout 함수를 호출하는 경우 해당 인수는 “&”으로 선언해서 보내야 합니다. 즉, 호출하는 측에서는 명시적으로 참조로 보내려는 의도하지 않는 변수 변경이 일어나는 문제를 줄이고 있습니다.[/vc_column_text][vc_custom_heading text=”(7) 중첩 함수

” use_theme_fonts=”yes”][vc_column_text]함수나 메소드에 정의된 함수를 중첩 함수라고 합니다. 정의하는 방법은 일반적인 함수와 비슷하지만 함수 외부에서 선언된 변수를 읽고 쓸 수 있다는 것을 기억해야 합니다.

중첩 함수는 일반적인 함수 정의 형태뿐만 아니라 폐쇄(클로저)형으로도 정의할 수 있습니다. 폐쇄에 대해서는 나중에 따로 설명합니다.[/vc_column_text][vc_column_text]

[code lang=”swift”]
func countString(count: Int) ->String { // 외부함수
var value = ("", "") // 튜플형으로 선언

func cnt(i: Int) ->String {
return value.0 + String(i*i) + value.1
}

var result = ""
value = ("#", "#")
result += cnt(count – 1) // 결과: "#16#"
value = ("*", "*")
result += cnt(count) // 결과: "#16#*25*"

return result
}

countString(5) // 결과: "#16#*25*"
[/code]

[예제 3-8] 중첩 함수 [/vc_column_text][vc_column_text]위 예제는 countString()은 선언된 값을 제곱하여 그 앞뒤로 문자를 붙이는 중첩함수 cnt()를 정의한 경우입니다. 변수 value는 중첩함수에서 이용되고 value값은 cnt()실행시 값이 적용됩니다. Cnt()를 선언시 값은 없습니다.

또한, 중첩 함수의 범위는 정의된 함수 내에서만 가능하기 때문에 countString()외부에서는 cnt()함수를 호출할 수 없습니다.[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_separator][vc_btn title=”목차로 가기” color=”orange” align=”center” link=”url:%2Fswift-2-korean-book%2F|title:%EB%AA%A9%EC%B0%A8|”][/vc_column][/vc_row]

Facebook Comments

카테고리:   Swift Books

댓글

죄송하지만 댓글은 닫혀 있습니다.