APP/Swift

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

개발자 aloe 2022. 10. 2. 17:16
728x90

 

 

 

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

 

ios 개발을 위한 swift 문법 배우기: 클로저 기본

클로저는 코드의 블럭입니다. 일급시민(first-citizen)으로, 전달인자, 변수, 상수 등으로 저장, 전달이 가능합니다. 함수는 클로저의 일종으로, 이름이 있는 클로저라고 생각하면 됩니다. 기본 클로

aloe-study.tistory.com

기본 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

 

ios 개발을 위한 swift 문법 배우기: 값 타입, 참조 타입

swift 에서 class, 구조체, 열거형을 살펴보면 class 전통적인 OOP관점에서의 클래스 와 크게 다른점 없음 단일상속 : 이게 가능함 (인스턴스/타입)메서드 (인스턴스/타입) 프로퍼티 참조타입 Apple 프레

aloe-study.tistory.com

 

그렇다면 진짜!!... 클래스란 무엇인가?

 

//쉽게 새로운 자료형을 만드는 것이라고 생각하자

+ 클래스를 통해 생성한 (객체)인스턴스는 무엇인가?

 

객체의 구성을 보면 클래스에서 정의한 변수, 메서드를 사용할 수 있음(찍어낸 것이라)

 

클래스를 통해 객체를 생성하는 것을 인스턴스화 한다라고 말함.

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

 

Smile Han

 

www.youtube.com

 

728x90