Swift 문법 심화, Ios 개발자 면접 직전 Swift 문법 중요 포인트 정리3 1급 객체, 1급 시민, 클로저, 클래스,
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