시작 하기 전에 저번주에 배웠던 함수, 메서드에 대해 복습하고 넘어가자.
함수와 메서드
함수와 메서드는 비슷한 의미이지만 클래스 내부에서 정의되고 사용되는 함수가 메서드이다.
스위프트에서는 함수를
func 함수명(argumentLabel parameterName: 자료형, argumentLabel parameterName: 자료형 ...) -> 반환 타입 {}
이렇게 정의한다.
func concatStr(s1: String, s2: String) -> String { // 함수 정의
return (s1 + s2)
}
print(concatStr(s1: "알고", s2: "리즘")) // 함수 호출
print(type(of: concatStr))
/* 실행 결과
알고리즘
(String, String) -> String
*/
외부 매개변수 레이블은 생략할 수 있다. 하지만 이 경우에는 내부 매개변수 명으로 대신 사용해야 한다. 사실 그냥 연결 연산자를 이용하면 문자열을 연결할 수 있지만 그냥 예시로 만들어 보았다.
// 함수 정의, 외부 매개변수명도 사용
func concatStr(name s1: String, nickname s2: String) -> String {
return (s1 + s2)
}
print(concatStr(name: "kim", nickname: "K")) // 함수 호출
/* 실행 결과
kimK
*/
func concatStr1(s1: String, s2: String) -> String { // 내부 매개변수명만
print(#function)
return (s1 + s2)
}
print(type(of: concatStr1))
print(concatStr1(s1: "kim", s2: "K"))
func concatStr2(name s1: String, nickname s2: String) -> String { // 내부, 외부 매개변수명
print(#function)
return (s1 + s2)
}
print(type(of: concatStr2))
print(concatStr2(name: "kim", nickname: "K"))
func concatStr3(_ s1: String, _ s2: String) -> String { // 외부 매개변수명 모두 생략
print(#function)
return (s1 + s2)
}
print(type(of: concatStr3))
print(concatStr3("kim", "K"))
func concatStr4(_ s1: String, nickname s2: String) -> String { // 첫 번째 외부 매개변수명만 생략
print(#function)
return (s1 + s2)
}
print(type(of: concatStr4))
print(concatStr4("kim", nickname: "K"))
/* 실행 결과
(String, String) -> String
concatStr1(s1:s2:)
kimK
(String, String) -> String
concatStr2(name:nickname:)
kimK
(String, String) -> String
concatStr3(_:_:)
kimK
(String, String) -> String
concatStr4(_:nickname:)
kimK
*/
네 가지 함수를 살펴보자 각각 외부 매개변수명, 내부 매개변수명을 표기하는데 차이가 있다.
| 외부 매개변수명 | 내부 매개변수명 | 외부 매개변수명 | 내부 매개변수명 |
| X | O | X | O |
| O | O | O | O |
| _ | O | _ | O |
| _ | O | O | O |
이런 식으로 여러가지 방법으로 사용할 수 있지만 Objective-C에서 네 번째 방법을 사용했으므로 네 번째 방법처럼 사용하는 것이 권장된다고 한다.
func ddd(x: Double) -> Double {
return x * 2
}
func setX(x: Double) -> Void { // Void 생략 가능
xx = x
}
func getX() -> Double {
return x
}
func mul(_ x: Double, with y: Double) -> Double {
return x * y
}
func setXY(_ x: Double, second y: Double) { // Void 생략
xx = x
yy = y
}



함수에 관한 정보를 얻고 싶을 때는 해당 함수의 두 번째 외부 매개변수명으로 검색을 하면 된다.
https://developer.apple.com/documentation/uikit/uitableviewdatasource
UITableViewDataSource | Apple Developer Documentation
The methods that an object adopts to manage data and provide cells for a table view.
developer.apple.com
해당 함수는 numberOfRowsInSection로 검색을 하면 된다.
guard 문
guard문은 if문과 비슷하지만 거짓일 때 실행되는 else문이 있다는 것이 다르다. guard문은 보통 optional 변수의 값을 꺼내오는데 사용하는 용도인데 nil이면 이후에 실행되는 문장들이 의미가 없으니까 조기 출구(early exit)로 빠져나오는 것을 염두해둔 설계이다.
var answer = false
for var i : Int in 1...5 {
if answer { // if문
print("참")
}
else {
print("거짓")
}
}
/* 실행 결과
거짓
거짓
거짓
거짓
거짓
*/
ㄴ 일반적인 if문의 사용이다.
var answer = false
for var i : Int in 1...5 {
guard answer else {
print("참")
break
}
print("거짓")
}
/* 실행 결과
참
*/
ㄴ 거짓일 때 else 블록 안의 문장이 실행 되는 것을 알 수 있다.
if let의 경우는 블록 안에서만 사용 가능하지만 guard let의 경우는 블록 바깥에서도 사용 가능하다. 그렇기 때문에 아래와 같이 사용할 수 있다.
func add(_ val1: Double?, with val2: Double?) -> Void {
guard let n1 = val1, let n2 = val2 else { // nil 일 때
print("계산 불가")
return
}
print(n1 + n2) // n1, n2를 여기서도 사용 가능
}
add(1.3, with: 4.9)
add(1.3, with: nil)
/*실행 결과
6.2
계산 불가
*/
if ~ let vs. guard ~ let
func hi(_ str1: String?) {
if let str1 { // nil이 아닐 때
print("나는" + str1)
}
else {
print("나는 nil이야")
}
}
hi("스위프트")
hi(nil)
/*실행 결과
나는스위프트
나는 nil이야
*/
func hi(_ str1: String?) {
guard let str1 else { // nil일 때
print("나는 nil이야")
return
}
print("나는" + str1)
}
hi("스위프트")
hi(nil)
/*실행결과
나는스위프트
나는 nil이야
*/
if문과 guard문의 차이를 알 수 있다. guard는 else문 에서 거짓일 경우를 처리한다.
디폴트 매개변수
디폴트 매개변수는 함수를 사용할 때 argument를 지정하지 않아도 자동으로 함수에서 세팅된 값을 사용할 수 있는 기능이다. 함수 정의부에서 매개변수 = 값 으로 지정할 수 있다. 자바에는 없는 기능이지만 C#에 있는 그것과 같다.
func compare(_ val1: Int, to val2: Int, desc order : Bool = false) {
if (!order) { // 오름차순
print(val1 > val2 ? "\(val2), \(val1)" : "\(val1), \(val2)")
}
else { // 내림차순
print(val1 > val2 ? "\(val1), \(val2)" : "\(val2), \(val1)")
}
}
compare(2, to: 3) // false, argument 생략, 오름차순
compare(2, to: 3, desc: false) // false, 오름차순
compare(2, to: 3, desc: true) // true, 내림차순
/*실행 결과
2, 3
2, 3
3, 2
*/
위 예시에서는 디폴트 매개변수 값으로 false를 지정했다. 그렇기 때문에 desc를 지정해주지 않거나 false를 입력하면 자동으로 오름차순으로 처리되고, true를 입력하면 내림차순으로 지정된다.

자주 사용하는 print() 함수도 기본으로 separator와 terminator에 값이 할당되어 있다. 그래서 그냥 괄호 안에 문자열만 넣어도 작동하는 이유가 이 때문이다. 직접 이것들을 지정해주면 여러 문자열 사이 조절 및 문자열 끝에서의 처리를 할 수 있다.
https://developer.apple.com/documentation/swift/print(_:separator:terminator:)
print(_:separator:terminator:) | Apple Developer Documentation
Writes the textual representations of the given items into the standard output.
developer.apple.com
print(1, 2, 3) // separator, terminator 생략
/* 실행 결과
1 2 3
*/
실행 결과를 보면 알아차리기 어려울 수도 있겠지만 1 2 3 뒤에 \n이 출력되므로 한 줄 띄어진다.
print(1, 2, 3, separator: ", ", terminator: "") // 직접 지정
/* 실행 결과
1, 2, 3
*/
여러 개 리턴
func convert(_ num: Int) -> (doubleNum: Double, stringNum: String, binaryStringNum: String) {
let doubleNum: Double = Double(num) // Double로 변환
let stringNum: String = String(num) // String으로 변환
let binaryStringNum: String = String(num, radix: 2) // 2진수 문자열로 변환
return (doubleNum, stringNum, binaryStringNum) // 튜플로 반환
}
var myTuple = convert(17)
print(myTuple)
print(myTuple.doubleNum)
print(myTuple.stringNum)
print(myTuple.binaryStringNum)
/* 실행 결과
(doubleNum: 17.0, stringNum: "17", binaryStringNum: "10001")
17.0
17
10001
*/
튜플을 통해 여러 값을 리턴할 수 있다. 반환 타입을 튜플로 지정하고 리턴값을 튜플에 저장해 해당 튜플을 이용하여 값을 조회하는 것도 가능하다.
import Foundation
func sss(x: Int, y: Int) -> (sum: Int, sub: Int, mul: Int, div: Double, mod: Int) {
let sum = x + y
let sub = x - y
let mul = x * y
let div = Double(x) / Double(y)
let mod = x % y
return (sum, sub, mul, div, mod)
}
var result = sss(x: 26, y: 4) // 튜플로 반환
print(result.sum)
print(result.sub)
print(result.mul)
print(String(format: "%.3f", result.div))
print(result.mod)
print(type(of: sss))
/* 실행 결과
30
22
104
6.500
2
(Int, Int) -> (sum: Int, sub: Int, mul: Int, div: Double, mod: Int)
*/
덧셈, 뺄셈, 곱셈, 나눗셈, 나머지 연산은 수행 하여 그 결과를 한번에 튜플로 저장할 수도 있다.
가변 매개변수
가변 매개변수란 특정 매개변수로 들어올 수 있는 값이 여러 개임을 의미한다. 자료형 뒤에 ...을 붙이는 것으로 구현할 수 있다. 아래 예를 통해 살펴보자.
func add(nums: Double...) -> Double {
var sum : Double = 0
for num in nums { // 매개변수 개수만큼 반복
sum += num
}
return sum
}
print(add(nums: 1.3, 1.6))
print(add(nums: 1.3, 1.6, 3.7))
print(add(nums: 1.3, 1.6, 3.7, 23.1))
/*실행 결과
2.9000000000000004
6.6000000000000005
29.700000000000003
/*
nums 값으로 여러 값을 받아도 실행이 잘 되는 것을 확인할 수 있다.
inout
스위프트는 기본적으로 함수 호출 시에 call by value로 동작한다. 이를 reference로 사용하고 싶다면 inout 키워드를 사용하면 된다.
var str: String = "안녕"
func change(str1: inout String) -> String { // inout 사용
str1 += "hi"
return str1
}
print(str)
print(change(str1: &str)) // &를 붙여서 호출
print(str)
/* 실행 결과
안녕
안녕hi
안녕hi
*/
String임에도 불구하고(자바와 다르다...) call by value로 동작하는 것을 알 수 있는데 함수의 매개변수 쪽에 inout를 작성하고 함수 호출 시 &와 함께 호출하면 call by reference로 작동되어 str의 값이 변경된 것을 알 수 있다. 이렇게 주소를 가지고 호출하지 않으면 함수 내부에서 기본적으로 값이 복사되어 let 변수로 동작하기 때문에 값을 변경할 수 없다.
import Foundation
func calcBMI(weight: Double, height: Double) -> String {
let bmi = weight / (height * height * 0.0001)
let shortenedBmi = String(format: "%.1f", bmi) // 소수점 한 자리만 표시
var body = ""
if bmi >= 40 {
body = "3단계 비만"
}
else if bmi >= 30 && bmi < 40 {
body = "2단계 비만"
}
else if bmi >= 25 && bmi < 30 {
body = "1단계 비만"
}
else if bmi >= 18.5 && bmi < 25 {
body = "정상"
}
else {
body = "저체중"
}
return "BMI: \(shortenedBmi), 판정: \(body)"
}
print(calcBMI(weight: 70.0, height: 177.0))
/* 실행 결과
BMI: 22.3, 판정: 정상
*/
BMI 계산을 함수로 만들어 계산하는 코드이다.
func checkStrLen(str: String) -> Void {
switch str.count {
case 0..<5: // 0 ~ 4
print("0 ~ 4자리")
case 5..<10: // 5 ~ 9
print("5 ~ 9자리")
default: // 10 ~
print("10자리 이상")
}
}
checkStrLen(str: "")
checkStrLen(str: "스위프트공부")
checkStrLen(str: "12345678910")
/* 실행 결과
0 ~ 4자리
5 ~ 9자리
10자리 이상
*/
함수 내에서 if ~ else 대신 switch ~ case를 사용하는 예제이다.
import Foundation
func calcBMI(weight: Double, height: Double) -> Void {
let bmi = weight / (height * height * 0.0001)
let shortenedBmi = String(format: "%.1f", bmi) // 소수점 한 자리만 표시
var body = ""
print("BMI: \(shortenedBmi), 판정: ", terminator: "")
switch bmi {
case 0.0 ..< 18.5:
print("저체중")
case 18.5 ..< 25.0:
print("정상")
case 25.0 ..< 30.0:
print("1단계 비만")
case 30.0 ..< 40.0:
print("2단계 비만")
default:
print("3단계 비만")
}
}
calcBMI(weight: 80.5, height: 177.0)
/* 실행 결과
BMI: 25.7, 판정: 1단계 비만
*/
앞서 살펴보았던 BMI 구하는 코드를 switch ~ case로 바꿔보았다.
1급 객체(First Class Object)
1급 객체란 변수에 저장할 수 있으며 / 매개변수로 전달할 수도 있고 / 리턴값으로도 사용할 수 있는 객체이다.
1. 변수에 저장
import Foundation
func isPrime(num val: Int) -> Bool {
var result = Set<Int>()
for var i in 1...Int(sqrt(Double(val))) {
if (val % i == 0) { // 약수일 때
result.insert(i)
result.insert(val / i)
}
}
return result.count == 2 ? true : false // 약수가 두 개뿐이면 참
}
print("13은 소수인가? ", isPrime(num: 13))
let getIsPrime = isPrime // 함수를 변수에 저장
print("8은 소수인가? ", getIsPrime(8))
print("53은 소수인가? ", getIsPrime(53))
print(type(of: getIsPrime)) // 타입도 원래 함수와 같음
/* 실행 결과
13은 소수인가? true
8은 소수인가? false
53은 소수인가? true
(Int) -> Bool
*/
소수인지를 판별하는 함수이다. 해당 함수를 getIsPrime 이라는 변수에 저장했더니 해당 변수를 함수처럼 사용할 수 있게 되었다. 타입도 원래와 같지만 사용할 때 주의해야할 점은 외부 매개변수명을 사용하지 않는다는 것이다.
2. 매개변수로 전달
import Foundation
func isPrime(num val: Int) -> Bool {
var result = Set<Int>()
for var i in 1...Int(sqrt(Double(val))) {
if (val % i == 0) { // 약수일 때
result.insert(i)
result.insert(val / i)
}
}
return result.count == 2 ? true : false // 약수가 두 개뿐이면 참
}
func getIsPrime(_ primeFunc: (Int) -> Bool, max num: Int) { // isPrime() 함수를 매개변수로 받음
for var i in 1...num {
print("\(i)은/는", primeFunc(i) ? "소수 입니다." : "소수가 아닙니다.")
}
}
getIsPrime(isPrime, max: 10) // isPrime() 함수를 argument로 사용
/* 실행 결과
1은/는 소수가 아닙니다.
2은/는 소수 입니다.
3은/는 소수 입니다.
4은/는 소수가 아닙니다.
5은/는 소수 입니다.
6은/는 소수가 아닙니다.
7은/는 소수 입니다.
8은/는 소수가 아닙니다.
9은/는 소수가 아닙니다.
10은/는 소수가 아닙니다.
*/
앞서 살펴보았던 소수인지를 판별하는 함수를 getIsPrime() 함수의 매개변수로 설정하였다. 해당 함수를 호출할 때 isPrime을 argument로 설정하여 isPrime() 함수를 사용할 수 있다.
3. 리턴값으로 사용
import Foundation
func isPrime(_ val: Int) -> Bool {
var result = Set<Int>()
for var i in 1...Int(sqrt(Double(val))) {
if (val % i == 0) { // 약수일 때
result.insert(i)
result.insert(val / i)
}
}
return result.count == 2 ? true : false // 약수가 두 개뿐이면 참
}
func getIsPrime() -> (Int) -> Bool { // 리턴값으로 함수를 사용
return isPrime
}
var f = getIsPrime()
print(f(15) ? "소수 입니다." : "소수가 아닙니다.")
/* 실행 결과
소수가 아닙니다.
*/
getIsPrime() 함수의 리턴값으로 함수의 타입을 지정했다. 이렇게 하면 해당 타입을 가지는 함수를 리턴할 수 있는데 이 값을 변수에 저장해서 1번에서 살펴봤던 것처럼 해당 변수를 함수처럼 사용할 수 있다.
import Foundation
func isPrime(num val: Int) -> Bool {
var result = Set<Int>()
for var i in 1...Int(sqrt(Double(val))) {
if (val % i == 0) { // 약수일 때
result.insert(i)
result.insert(val / i)
}
}
return result.count == 2 ? true : false // 약수가 두 개뿐이면 참
}
func getIsPrime1(_ primeFunc: (Int) -> Bool, max num: Int) { // isPrime() 함수를 매개변수로 받음
for var i in 1...num {
print("\(i)은/는", primeFunc(i) ? "소수 입니다." : "소수가 아닙니다.")
}
}
func getIsPrime2() -> (Int) -> Bool { // 리턴값으로 함수를 사용
return isPrime
}
// 1. 함수를 변수에 저장
let getIsPrime = isPrime // 함수를 변수에 저장
print("8은 소수인가? ", getIsPrime(8))
// 2. 함수를 매개변수로 전달
print()
getIsPrime1(isPrime, max: 3) // isPrime() 함수를 argument로 사용
// 3. 함수를 리턴값으로 사사용
print()
var f = getIsPrime2()
print(f(15) ? "소수 입니다." : "소수가 아닙니다.")
/* 실행 결과
8은 소수인가? false
1은/는 소수가 아닙니다.
2은/는 소수 입니다.
3은/는 소수 입니다.
소수가 아닙니다.
*/
앞서 살펴봤던 세 가지 경우를 한번에 사용한 예이다.
클로저
클로저란 익명 함수로 자바의 람다와 동일한 기능을 수행한다.
사용 방법은 { (매개변수) -> 리턴타입 in 문장 } 이렇게 사용하면 된다.
let result = { (str1: String, str2: String) -> String in
return str1 + " / " + str2
}
print(result("스위", "프트"))
print(type(of: result))
/* 실행 결과
스위 / 프트
(String, String) -> String
*/
주의할 점은 앞에서도 살펴봤듯이 외부 매개변수명은 무시하고 사용해야한다.
let mod = { (num1: Int, num2: Int) -> Int in
return num1 % num2
}
print(mod(16, 6))
print(type(of: mod))
/* 실행 결과
4
(Int, Int) -> Int
*/
두 개의 수를 받아서 나머지 연산을 하는 함수이다.
'공부 > Swift' 카테고리의 다른 글
| [Swift][9주차] Xcode 및 Mac 사용 (0) | 2023.10.30 |
|---|---|
| [Swift][6주차] 클로저, 클래스 (0) | 2023.10.16 |
| [Swift][4주차] 제어문 및 함수와 메서드 (0) | 2023.09.25 |
| [Swift][3주차] 연산자와 Optional (0) | 2023.09.18 |
| [Swift][1, 2주차] Swift 들어가기 및 자료형 (0) | 2023.09.11 |