Swift의 함수는 1급 객체, 1급 시민이다.
aloe야... 이게 도대체 무슨말이냐? 괜찮다.
아래 설명을 본다면 충분히 이해할수 있을것이며 면접시 재대로 답한다면 분위기가 좋아질 것이다.
(논리 회로 대신 행복회로를 그려보자)
이 3가지를 다 만족하면 그 언어의 함수는 1급 객체라고 부를 수 있는 것임
그래서 스위프트 함수는 일급 객체임 아니면 일급 시민이다 first class object, first class citizen 다 같은 말이다.
first class object : (1) 함수를 변수에 저장 가능
// Swift는 함수를 데이터 타입처럼 처리할 수 있음
// 다음과 같이 함수를 상수 또는 변수에 할당하는 것이 가능
func inchesToFeet (inches: Float) -> Float {
return inches * 0.0833333
}
let toFeet = inchesToFeet //함수를 자료형처럼 사용
//함수를 호출하려면 원래의 함수 이름 대신에 상수 이름을 이용하여 호출 가능
print(inchesToFeet(inches:10))
print(type(of:toFeet))
print(toFeet(10)) //주의 : 매개변수명(inches:) 안씀 : 레이블 없이 넘겨주면 됨c 처럼
// 0.833333
// (Float) -> Float 함수의 자료형과 똑같음 그렇기 때문에 함수의 자료형 잘 확인할 수 있어야함
// 0.833333
이전 글에서 말했듯이 함수의 타입 즉 자료형을 아는 것이 중요 하다는 이유가 여러가지 방면에서 활용도가 높기 때문임
first class object : (2) 함수를 매개변수로 사용
// Swift는 함수를 데이터 타입처럼 처리할 수 있음
// 다음과 같이 함수를 상수 또는 변수에 할당하는 것이 가능
func inchesToFeet (inches: Float) -> Float {
return inches * 0.0833333
}
let toFeet = inchesToFeet //함수를 자료형처럼 사용
//함수를 호출하려면 원래의 함수 이름 대신에 상수 이름을 이용하여 호출 가능
print(inchesToFeet(inches:10))
print(type(of:toFeet))
print(toFeet(10)) //주의 : 매개변수명(inches:) 안 씀 : 레이블 없이 넘겨주면 됨c 처럼
func outputConversion(converterFunc: (Float) -> Float, value: Float) {//함수를 매개변수로 사용
//첫번째 매개변수를 함수를 받음 (Float) -> Float 이런 형의 함수를 받겠다
let result = converterFunc(value) //toFeet(10)
print("Result = \(result)")
}
outputConversion(converterFunc:toFeet, value: 10) // 피트로 변환하는 inchesToFeet함수 호출
// 0.833333
// (Float) -> Float
// 0.833333
// Result = 0.833333
// 즉 매개변수가 (Float) 형이고 리턴 값이 Float형인 함수를 넣어주겠다 라는 것임
first class object : (3) 함수를 리턴 값으로 사용
func inchesToFeet (inches: Float) -> Float {
return inches * 0.0833333
}
func inchesToYards (inches: Float) -> Float {
return inches * 0.0277778
}
let toFeet = inchesToFeet
let toYards = inchesToYards
//반환 타입으로 함수의 타입을 선언하면 함수도 반환될 수 있음
//다음 함수는 Boolean 매개변수의 값에 따라 toFeet 함수 또는 toYards 함수를 반환
func decideFunction (feet: Bool) -> (Float) -> Float
{ //매개변수형 리턴형이 함수형
if feet {
return toFeet //함수를 리턴
} else {
return toYards
}
}
좀더 이해하기 쉬운 실습 코드와 화면이다.
위 소스 코드는 1급 객체를 만족하는 3가지 조건을 해당 사항에 맞게 번호를 마킹 한 것이다.
func up(num: Int) -> Int {
return num + 1
}
func down(num: Int) -> Int {
return num - 1
}
let toUp = up
print(up(num:10))
print(toUp(10))
let toDown = down
func upDown(Fun: (Int) -> Int, value: Int) {
let result = Fun(value)
print("결과 = \(result)")
}
upDown(Fun:toUp, value: 10) //toUp(10)
upDown(Fun:toDown, value: 10) //toDown(10)
func decideFun(x: Bool) -> (Int) -> Int {
//매개변수형 리턴형이 함수형
if x {
return toUp
} else {
return toDown
}
}
let r = decideFun(x:true) // let r = toUp : 함수를 리턴해서 변수에 저장
print(type(of:r)) //(Int) -> Int
print(r(10)) // toUp(10)
// 11
// 11
// 결과 = 11
// 결과 = 9
// (Int) -> Int
// 11
함수 : 일급 객체 응용해보기 : 한번 위 소스코드를 수정해서 자기것으로 만들어 이해하길 바란다.
ex
func up(name: String, age: Int) -> String {
return "\(name)는 \(age + 1)"
}
func down(name: String, age: Int) -> String {
return "\(name)는 \(age - 1)"
}
let toUp = up
print(up(name:"우기", age:23))
print(toUp("부기", 22))
let toDown = down
func upDown(Fun: (String, Int) -> String, name: String, age: Int) {
let result = Fun(name, age)
print("결과 = \(result)")
}
upDown(Fun:toUp, name:"우기", age:23) //toUp(우기,23)
upDown(Fun:toDown, name:"우기", age:23) //toDown(우기,23)
func decideFun(x: Bool) -> (String, Int) -> String {
//매개변수형 리턴형이 함수형
if x {
return toUp
} else {
return toDown
}
}
let r = decideFun(x:true) // let r = toUp : 함수를 리턴해서 변수에 저장
print(type(of:r))
print(r("징니", 23)) // toUp(10)
// 우기는 24
// 부기는 23
// 결과 = 우기는 24
// 결과 = 우기는 22
// (String, Int) -> String
// 징니는 24
클로저란??
함수는 클로저의 일종으로, 이름이 있는 클로저라고 생각하면 됩니다.
함수를 클로저로 바꿔보기 실습
func add(x: Int, y: Int) -> Int {
return(x+y)
}
print(add(x:10, y:20))
let add1 = { (x: Int, y: Int) -> Int in
return(x+y)
}
//print(add1(x:10, y:20)) //주의 error: extraneous(관련 없는) argument labels 'x:y:' in call
print(add1(10, 20)) //OK
print(type(of:add1)) //과제
// 함수를 클로저로 바꿀때는 앞에 fun 키워드와 이름을 지우고
// 블락을 땡겨 전체로 묶은 다음 in 키워드를 사용해줌
// 일종의 함수이니 변수에 넣을수 있음 : 즉 일급 객체이기 때문에 대입 가능
// 클로저를 만드는 이유는 한번만 호출될거 같을때 함수를 따로 만들 필요 없이 그 자리에 기능을 넣어주겠다 할때 많이 사용함
// 30
// 30
// (Int, Int) -> Int
함수는 클로저의 일종으로, 이름이 있는 클로저라고 생각하면 됩니다.
https://aloe-study.tistory.com/121
기본 Swift 문법 : 클로저 글을 보고 오면 이해하기 쉽습니다.
후행 클로저란??
즉 어떠한 함수에서 매개변수로 함수를 받을 때 클로저를 넘겨줄 수 있는데
중간에 작성한 코드처럼 저렇게 넘기는 방법이 기본적인 방법이지만.
가독성을 위해서 클로저가 함수의 마지막 argument라면 마지막 매개변수명을 생략한후 소괄호 뒤에 클로저를 작성해 넘겨줄 수 있다는 것임
요약 : 어렵지 않음 그냥 문법 순서를 바꿔서 가독성을 높여준다 라고 생각!
질문 : 아니 근데 왜? 이렇게 쓰는건데? 정말 화딱지 난다. 라고 생각 할 수 있지만.
+
아래글에 클래스 문법에서 사용할
사진과 같은 함수를 보면
저렇게 마지막 매개변수로 함수를 받게 되어있음 .
일반적으로 위 사진과 같이 init 함수를 활용한다고 할 때 매개변수를 함수로 받기 때문에
저렇게 클로저를 사용해 넘겨줄 수 있는것은 이해가 되었을거라고 생각함
하지만!!!!!! 클로저가 함수의 마지막 argument 이기 때문에
이렇게 클로저를 함수 뒤쪽에 따로 빼서 넘겨줄 수 있다는 것임
이것을 “후행 클로저”라고 함
참조 문헌
https://developer.apple.com/documentation/uikit/uialertaction/1620097-init
소스가 길어지니까 가독성을 위한 것
이것을 알아야 나중에 앱 개발을 하거나 문서를 볼 때 당황하지 않고 뭔지 알 수 있음
그런데!!!!!!!!! 주의!!!!!!!!!!!!!!!!!!!!!!! 지금부터 혼란 올 수 있음
어렵지 않음 아래 코드 주석을 보며 이해하기
클로저의 축약 표현들
/*
아래와 같은 함수를 클로저로 바꿔 보고 다양한 클로저 축약 표현들을 익혀보자
func mul(val1: Int, val2: Int) -> Int
{
return val1 * val2
}
let result = mul(val1:10, val2:20)
*/
let multiply = {(val1: Int, val2: Int) -> Int in
return val1 * val2
}
var result = multiply(10, 20)
print(result)
let add = {(val1: Int, val2: Int) -> Int in
return val1 + val2
}
result = add(10, 20)
print(result)
// 함수 대신 클로저를 활용하여 상수에 값을 저장하기
func math(x: Int, y: Int, cal: (Int, Int) -> Int) -> Int { // 일급 시민 함수를 마지막 매개변수로 받기
return cal(x, y)
}
result = math(x: 10, y: 20, cal: add)
print(result)
result = math(x: 10, y: 20, cal: multiply)
print(result)
result = math(x: 10, y: 20, cal: {(val1: Int, val2: Int) -> Int in
return val1 + val2
}) //클로저 소스를 매개변수에 직접 작성
print(result)
// 클로저가 함수의 마지막 argument라 매개 변수 이름을 생략후 : 후행 클로저로 변환
result = math(x: 10, y: 20) {(val1: Int, val2: Int) -> Int in
return val1 + val2
}//trailing closure
print(result)
// 클로저의 다양한 축약 표현들
result = math(x: 10, y: 20, cal: {(val1: Int, val2: Int) in // -> Int
return val1 + val2
}) //리턴형 (반환 타입) 생략
print(result)
result = math(x: 10, y: 20) {(val1: Int, val2: Int) in // -> Int
return val1 + val2
} //trailing closure, 리턴형 (반환 타입)생략
print(result)
result = math(x: 10, y: 20, cal: {
return $0 + $1 //$0 + $1 숫자 증가에 따라 순서대로 들어오는 매개변수 순서가 됨
}) //매개변수 생략하고 단축인자(shorthand argument name)사용
print(result)
result = math(x: 10, y: 20) {
return $0 + $1
} //trailing closure, 매개변수 생략하고 단축인자사용
print(result)
result = math(x: 10, y: 20, cal: {
$0 + $1
}) //클로저에 리턴값이 있으면 마지막 줄을 리턴하므로 return생략
print(result)
result = math(x: 10, y: 20) { $0 + $1 } //return 생략
print(result)
// 200
// 30
// 30
// 200
// 30
// 30
// 30
// 30
// 30
// 30
// 30
// 30
// 클로저 사람마다 생략(축약)하는게 다다름 이렇게 다양하게 축약해서 사용 할수 있다는것을 알아야함
// 다른 사람 코드를 보더라도 문제없이 어떤 건지 알 수 있음
// 너무 많이 축약하면 오히려 협업 시 가독성이 떨어질 수 있음(역설적으로) 그렇기 때문에 적당히 줄여서 사용 해야함
// 이 모든 게 다 같은 소스라는 걸 알고 있어야함
참조 문서
https://docs.swift.org/swift-book/LanguageGuide/Closures.html
추가 이해 자료 <------------------------------- 이해하지 못하더라도 괜찮다 조금더 상세하게 글을 적었다.
https://aloe-study.tistory.com/121
https://aloe-study.tistory.com/122
클래스란? 객체란? 인스턴스란?
용어 정리 부터 넘어가자
https://en.wikipedia.org/wiki/Class_(computer_programming)
+ tmi
우리나라에서는 인스턴스라는 말 잘 사용 x
영어권에서는 인스턴스라는 말을 많이 사용
객체 지향 언어의 핵심은 재사용임
덩치가 커지면 재사용하기가 쉽지가 않음 코드가 어디부터 어디까지 인가? 라는 의문도 들고 그래서 대부분 객체지향 언어의서 재사용 단위가 클래스임
이런 것 들이 모여 있는 것을 SDK 라고 하며
그게 커지고 어떤 틀 안에서 내가 작업하는 것을 프레임 워크라고 함
https://aloe-study.tistory.com/158
+ 프레임 워크 용어 정리
혹시 다른 객체 지향 용어를 사용해보았는가?
필자도 JAVA, C++, 최근 플러터 프레임 워크를 사용하기 위해 dart 라는 언어도 배워 보았다.
아래 정리된 표와 같이
객체지향 언어 별 용어를 비교하면 이와 같은 형태로 나타나는 것을 알 수 있음 일반 적으로 클래스 안에 선언 되어 있는 변수를 Swift에서 프로퍼티 라고 하며 클래스 안에 선언 되어 있는 함수를 메소드라고 부름
본격 Class 문법을 설명하기 전 마지막으로 하나먼 더 알고가자
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
스위프트에서 구조체와 클래스 많이 사용하는데 이 두개가 어떤 차이가 있는지 설명할 수 있어야함
문법 구조는 비슷하지만 상속 같은 기능을 이용하겠다 하면 클래스를, 간단하게 데이터를 가지고 놀겠다 하면 구조체를 사용하면 됨 정답은 없음
가장 큰 특징을 비교하면 구조체, 열거형 둘 다 일반적인 값 타입이고
클래스는 참조(주소) 타입임
이 글을 필독 하길 바란다 : 스위프트에서는 구조체 사용을 선호하며 apple이 지원하는 프레임 워크들은 class 사용을 선호 한다.
https://aloe-study.tistory.com/120
그렇다면 진짜!!... 클래스란 무엇인가?
//쉽게 새로운 자료형을 만드는 것이라고 생각하자
+ 클래스를 통해 생성한 (객체)인스턴스는 무엇인가?
객체의 구성을 보면 클래스에서 정의한 변수, 메서드를 사용할 수 있음(찍어낸 것이라)
클래스를 통해 객체를 생성하는 것을 인스턴스화 한다라고 말함.
https://blog.hexabrain.net/104
참고 자료 (자바와 문법 비교)
상속을 받을 때만: 부모 클래스 해주고
그냥 독자적인 클래스면 필요 없음
ex 자바에서는: 대신 extends
클래스에서 메서드는 두 가지임 (인스턴스, 타입) 메서드
프로퍼티(property) : Swift 에서 제공하는 프로퍼티는 2가지로 나뉨
stored property와 Computed Properties
https://docs.swift.org/swift-book/LanguageGuide/Properties.html
우선 저장 프로퍼티를 만드는 방법은 3가지임
// 프로퍼티는
// 초기값이 있거나
// init을 이용해서 초기화하거나
// 옵셔널 변수(상수)로 선언(자동으로 nil로 초기화)
class Man{
var age : Int
var weight : Double
} //오류
// 초기 값이 없어서 에러가 남
// main.swift:1:7: error: class 'Man' has no initializers
// class Man{
// ^
// main.swift:2:6: note: stored property 'age' without initial value prevents synthesized initializers
// var age : Int
var a : Int
// 일반 변수는 초기 값이 있어도 없어도 되지만
// 클래스에 저장 프로퍼티는 초기 값이 없으면 안됨
// 1, 2, 3 조건을 따라야함
우선 옵셔널 변수로 선언 하는 방법과 초기값을 지정 하는 방법을 살펴 보겠음
class Man{
var age : Int? // var age : Int? = nil 이렇게 자동으로 nil 값 들어 감
var weight : Double!
}
//이렇게 옵셔널 변수로 만드는 것임
class Man2{
var age : Int = 1
var weight : Double = 3.5
}
마지막 3번째는 생성자를 사용하는 것임
3번째 init 을 이용한 초기화 방법 (원래 우리가 사용하고 있지만 숨겨져서 안보였던 것 우리가 정의 해서 초기화 시켜줄 수 있음) 원래 클레스를 이용해 인스턴스를 만들때 생성자를 우리는 알게 모르게 사용하고 있음
클래스명 다음의 괄호를 눈에 보이지 않는 default initializer 라고 함
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
//init() 보이지 않고 숨겨져 있음
}
var kim : Man = Man() // default initializer 를 이용하여 인스턴스 생성
kim.display()
print(kim.age)
// 나이=1, 몸무게=3.5
// 1
직접 생성자를 만들어보자 직접 생성자를 만드는 것을 designated initializer 라고함
생성자를 직접 하나 만든다면 default initializer는 사라짐
생성자 정의해보자
class Man{
var age : Int
// = 1 생성자 있어서 생략 가능 프로퍼티 초기화 3가지 조건 중 하나만 만족 하면 됨
var weight : Double // = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(yourAge: Int, yourWeight : Double){
age = yourAge //self.age = yourAge 현재 self 생략가능 이름이 다르기 때문에
weight = yourWeight
} //designated initializer
}
//var kim : Man = Man() //오류
//init()을 하나라도 직접 만들면 default initializer는 사라짐
var kim : Man = Man(yourAge:10, yourWeight:20.5) // 자동으로 initializer 호출 됨
kim.display()
// 나이=10, 몸무게=20.5
생성자를 만들때 매개변수와 프로퍼티 이름이 같을 때 self 키워드를 사용할수 있음
일반적으로 이렇게 코드를 작성하는 경우가 많음
// 현재 클래스 내 메서드나 프로퍼티를 가리킬 때 메서드나 프로퍼티 앞에 self.을 붙임
// 다른 언어의 this와 같음(다른 포인터를 사용하지 않는 언어 ) 포인터 관련해서 this 키워드가 정의 되어 있기 때문에 다른 언어와 차이를 둠
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){
self.age = age //프로퍼티 = 매개변수
self.weight = weight
}
}
var kim : Man = Man(age:10, weight:20.5)
kim.display()
// 나이=10, 몸무게=20.5
자바에 static 메소드처럼 타입 메소드가 있음
이렇게 func 앞에 아무것도 없으면 일반적인 인스턴스 메서드 인스턴스가 가지고 노는
저 앞에 class or type이 들어가면 타입 메서드임 (클래스가 가지고 노는)
class 를 사용하느냐 type을 사용하느냐에 따라 상속하는데 있어서 조금 달라지는데 다음 시간에 이어 설명하겠음
인스턴스 만들고 메서드와 프로퍼티 접근
이제 객체를 생성해보고 사용해보자
주의!!!!!!!!!!!!!!!!!!!!!!!!!!
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){ //인스턴스 메서드
print("나이=\(age), 몸무게=\(weight)")
}
}
var kim : Man
print(kim.age)
// 이러한 에러가 남
// 주의 할 점 위와 같이 코드를 작성하면 제대로 인스턴스가 만들어 지지 않음
// 인스턴스 만들 때 조심 해야함
// main.swift:13:7: error: variable 'kim' used before being initialized
// print(kim.age)
// ^
// main.swift:12:5: note: variable defined here
// var kim : Man
// ^
위 코드는 오류남
// initialize 함수때문에 그럼
// 자바처럼 클래스를 만들 때 자동으로 생성해 주는 함수인데
// Swift에서도 반드시 객체 만들 때 는 initialize 함수 이용해서 만들어야 함
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){ //인스턴스 메서드
print("나이=\(age), 몸무게=\(weight)")
}
}
var x : Int = 3
//var kim : Man
var kim : Man = Man()
kim.display() //인스턴스 메서드는 인스턴스가 호출
var kim2 = Man() // 이렇게 생략 가능 함
print(kim.age) // 1 인스턴스.프로퍼티 호출
// Swift 변수를 만들 때 는 그냥 저렇게 만들어도 되지만
// 클래스의 인스턴스 객체를 만들 때 는
// 반드시 = 클래스 () 이렇게 작성 해야함
// initialize 함수때문에 그럼
// 자바처럼 클래스를 만들 때 자동으로 생성해 주는 함수인데
// Swift에서도 반드시 객체 만들 때 는 initialize 함수 이용해서 만들어야 함
to be continued
부족하지만 저와 같은 학생, 취준생들을 위해 글즐 작성해 보았으며 공부한것을 다지기 위해 글을 작성 하였습니다.
끝가지 봐주셔서 감사합니다 .
블로그에 작성된 ppt 와 자료들을 대한민국에서 가장 Swift, Ios 프로그래밍에 대하여 지식이 많으신
유튜버 스마일 한 님 (제가 가장 존경하는 교수님 중 한 분이십니다.) 자료를 참고하였습니다.
https://www.youtube.com/channel/UCM8wseo6DkA-D7yGlCrcrwA