함수 내의 인자값 수정이 외부 변수에 영향을 미칠 수 있는 방법. inout
키워드
인자를 '참조에 의한 전달방식'으로 전달한다.
var a = 10
func test(x: Int) -> Int {
var a = a
a += 1
return a
}
test(x: a)
print(a) // 10
//////
func testInout(x: inout Int) -> Int {
x += 1 // inout 때문에 따로 변수 선언 안해도 인자값 자체(= 외부 변수)로 그대로 실행
return x
}
testInout(x: &a) // 값이 아니라 주소를 전달하는 것이기 때문에 주소 추출 연산자 & 를 붙인다.
print(a) // 11
예외적으로 구현된 인스턴스는 inout 없이도 참조에 의해 전달됨.
전역변수 -> 프로그램의 최상위 레벨에 위치. 특별한 경우를 제외하고 프로그램 종료 전까지 삭제되지 않음.
지역변수 -> 실행 블록이 끝나면 제거 (변수의 생명 주기)
지역변수를 더 안쪽 범위에서 사용하려면 초기화가 된 상태여야함.
-> 초기화 전에는 메모리를 할당받지 못했으므로 주소값이 없음.
스위프트는 객체지향 언어이자 동시에 함수형 언어.
일급 객체
- 객체가 런타임에도 생성이 가능해야 한다.
- 인자값으로 객체를 전달할 수 있어야 한다.
- 반환값으로 객체를 사용할 수 있어야 한다.
- 변수나 데이터 구조 안에 저장할 수 있어야 한다.
- 할당에 사용된 이름과 관계없이 고유한 구별이 가능해야 한다.
함수가 위 조건을 만족하면 이를 일급 함수라 하고, 그 언어를 함수형 언어로 분류.
func foo(base: Int) -> String {
return "입력값 \(base)"
}
func foo(base: Int, base2: Int){
print(base + base2)
}
let fn1 = foo(base:)
let fn2 = foo(base:base2:)
print(fn1(123)) // 입력값 123
fn2(3, 5) // 8 출력
이때 식별자 없이도 함수 구분이 가능하다면(같은 이름의 함수가 없다면), let fn1 = foo
와 같은 형태만으로도 함수를 변수에 대입할 수 있다.
이런 타입을 함수 타입이라고 하며, 입력 타입과 반환 타입으로 표시한다.
func bar(age: Int) -> String {
return "\(age)"
}
let fn : (Int) -> String = bar // 함수 타입 어노테이션
func foo(x: Int) {
print(x + 5)
}
func bar() {
print("hello")
}
var x : (Int) -> () = foo // 반환값 없는 타입
var y : () -> () = bar // 입력, 반환 다 없는 타입
x(5) // 10
y() // hello
위처럼 반환값이 없는 경우, 빈 튜플 대신 Void
도 사용 가능.
func foo(x: Int) {
print(x + 5)
}
func bar() {
print("hello")
}
var f1 : (Int) -> Void = foo
var f2 : () -> Void = bar // 반환값에만 Void 키워드 사용 가능.
f1(10)
f2()
func foo() -> String {
return "foo() is excuted."
}
func pass() -> () -> String { // pass() 는 반환값으로 () -> String 함수 타입을 가짐
return foo
}
let p = pass()
print(p()) // "foo() is excuted."
예시
func plus(x: Int, y:Int) -> Int {
return x + y
}
func minus(x: Int, y:Int) -> Int{
return x - y
}
func cal(_ operand: String) -> (Int, Int) -> Int {
switch operand{
case "+":
return plus
case "-":
return minus
default:
return plus
}
}
let ex1 = cal("-")(5, 3) // 어떤 함수인지 명시 되어있기 때문에 라벨링 필요 X
let ex2 = cal("+")(5, 3)
let ex3 = cal("-")
print(ex1) // 2
print(ex2) // 8
print(ex3(4,3)) // 1
JS 의 콜백 함수 개념과 유사.
func incrase(param: Int) -> Int{
return param + 1
}
func pass(base: Int, function fn: (Int) -> Int) -> Int {
return fn(base)
}
print(pass(base: 5, function: incrase)) // 6
예시
func successExcute() {
print("SUCCESS")
}
func failExcute() {
print("FAIL")
}
func divide(x: Int, success sCallback: () -> Void, fail fCallback: () -> Void) -> Int {
guard (x != 0) else {
fCallback()
return 0
}
let answer = 100 / x
defer {
sCallback()
}
return answer
}
let ex1 = divide(x: 0, success: successExcute, fail: failExcute) // FAIL
let ex2 = divide(x: 100, success: successExcute, fail: failExcute) // SUCCESS
print(ex1) // 0
print(ex2) // 1
*위치와 관계없이 함수 종료 직전에 실행되는 구문이 defer
블럭 내에 들어간다.
- defer 블록을 읽기 전에 함수의 실행이 종료될 경우, defer 블록은 실행되지 않음.
- 하나의 함수나 메소드 내에서 defer 블록을 여러 번 사용할 수 있다. 이때는 **가장 마지막에 작성된 블록부터 역순으로 실행된다.
- 중첩해서 사용할 수 있다. 이때는 **바깥쪽 블록부터 실행되며 가장 안쪽에 있는 블록은 가장 마지막에 실행된다.
-> 함수가 연산을 처리하는 과정에 영향을 끼치지 않으면서 실행해야 할 다른 내용이 있을 때 사용하거나, 함수를 종료하기 직전에 정리해야 하는 변수나 상수값들을 처리하는 용도로 사용.
함수를 인자로 넘길 수 있다는 특성의 장점.
- 함수 내부에서 함수나 메소드의 객체를 직접 전달하므로, 검색을 통해 찾지 않아도 되어 효율적으로 실행할 수 있다.
- 함수를 주고받을 수 있으므로, 기존 함수를 데코레이션 하는 문법 구현이 가능하다. (파이썬 데코레이터)