하... 이제 옵셔널이다

 

옵셔널

- 사실 옵셔널에는 내용이 굉장히 적다.

- B.U.T. 어느 문법이던 중요하겠지만 Swift에서도 굉장히 중요한 표현이다

- 안드로이드 개발을 진행할 때는, 옵셔널이라는 명칭보다는 nullable이라는 단어를 사용했다.

- 스위프트의 특징인 안전성을 담보로 사용할 수 있는 기능이다

- 옵셔널과 옵셔널이 아닌 값은 철저히 다른 타입으로 인식하기 때문에, 컴파일 시점에서 오류를 걸러낸다.

 

그렇다면 왜 옵셔널을 사용하는가?

  • 함수의 전달인자의 값이 잘못된 값일 때
  • 매개변수를 굳이 넘기지 않아도 될 때
  • 열거형을 불러올 때, case에 존재하지 않을 때

앞에서 enum에서 알아봤 듯이, 옵셔널은 열거형이다

 

옵셔널 추출

  • 옵셔널 바인딩
    • if 사용 - if블록 내에서만 사용 가능
    • guard 사용 - 블록외의 블록에서 사용 가능
  • 암시적 추출
    • ! 사용 - 위험한 방법
struct Person{
	var name: String?
}

var person: Person = Person(name: "tree")

//if 사용
if let name = person.name{
	print(name) // tree
}

//guard 사용
guard let name = person.name else { ... }

print(name) // tree

//암시적 추출 사용(1)
let name = person.name!
print(name) // tree

var person2 = Person()
let name2 = person2.name! // 런타임 에러 발생

 

정리하려고 맘 먹고 작성 중인데... 잘못 먹은 것 같다..

함수

  • 앞서 말했 듯, Swift에서의 함수는 일급 시민이다

함수 vs 메서드

  • 메서드 : 구조체, 클래스, 열거형 등 특정 타입에 연관되어 사용하는 함수
  • 함수 : 모듈 전체에서 전역적으로 사용하는 함수
    • 개발자들끼리 약속된 것 같다. 누구는 클래스의 메소드를 메소드라하고, 누구는 함수라 하고, 누구는 펑션이라 한다면 의사소통이 비효율적일 것이기 때문에, 약속(?)한 것이 아닐까
    • 나도 이때부터 확실히 머리속에 박아두고 설명할 때는 인지하며 말하는 편이다.
//매개변수가 없는 함수
func sayHello(){
	print("Hello, My name is tree")
}

//매개변수가 있는 함수
func sayHello(name: String){
	print("Hello, My name is \(name)")
}

//매개변수의 기본값이 있는 함수
func sayHello(name: String = "tree"){
	print("Hello, My name is \(name)")
}

//반환 타입이 있는 함수
func sayHello(name: String) -> String{
	return "Hello, My name is \(name)"
}

+ 매개변수의 기본값이 있는 매개변수는 뒤로 보내야 한다.

 

입출력 매개변수 사용

  • 순서
    • 전달인자 값 복사
    • 함수 내부에서 값 변경
    • 반환 시점에 변경된 값을 매개변수에 할당
  • inout을 사용하면 값 타입도 참조 타입처럼 사용할 수 있다
  • B.U.T. 메모리 관점에서 안전하지 않기 때문에 사용을 지양한다. -> 이에 대해서도 이후에 작성할 것이다
//보통은 값을 복사하여 매개변수로 전달한다

var arr: [Int] = [1,2,3,4,5]

func abc(_ arr: [Int]){
	arr[0] = 100
}

abc(arr)
print(arr[0]) // 1

//입출력 매개변수 사용
func abc(_ arr: inout [Int]){
	arr[0] = 100
}

abc(&arr)
print(arr[0]) // 100

함수를 데이터 타입으로

- 앞서서 여러 차례 말했 듯이 함수는 일급 시민으로써 사용된다

var someFunction: (Int, Int) -> Int

func addInts(_ a: Int, _ b: Int) -> Int{
	return a+b
}

someFunction = addInts

print(someFunction(1,2)) // 3

someFunction이라는 변수에 addInts라는 함수를 지정하여 사용할 수 있다

B.U.T. 타입이 같아야한다.

 

중첩 함수

말 그대로 함수 내에 함수가 존재하는 것이다.

func function1(_ bool: Bool) -> (Int) -> Int{
	func plus(_ i: Int) -> Int{
    	return i+1
    }
    func minus(_ i: Int) -> Int{
    	return i-1
    }
    
    //삼항 연산자
    // ? 앞의 식이 true면 plus를 false면 minus를 return한다
    return bool ? plus: minus
}

var num: (Int) -> Int = func(true) //plus함수 할당
print(num(1)) // 2

 

이외에도 다양한 함수들이 존재한다

  • 종료되지 않는 함수
    • 반환 타입을 Never로 선언
  • 반환값을 무시 가능한 함수
    • @discardableResult 어노테이션 사용

스위프트의 데이터 타입

앞서 변수 선언 방법 

Swift : var value: Int = 100

Java : int value = 100

Kotlin : var value: Integer = 100

......

 

  • Int, UInt
    • 8, 16, 32, 64 비트 형태로 나타낼 수 있음
      • ex) 32비트 형태는 Int32 로 표현
  • Bool
    • true/false를 나타내는 데이터 타입
    • 내부에 toggle()이라는 API를 통해 값을 반전할 수 있음
  • Float / Double
    • Float : 32비트
    • Double : 64비트
  • Character
  • String
    • hasPrefix : 접두어 확인
    • hasSuffix : 접미어 확인
    • uppercased : 대문자로 변경
    • 등의 다양한 API가 존재함
    • 여러 줄의 String 형태를 변수에 담고 싶을 때는
var str: String = """
abcd
efgh
ijkl
"""

처럼 사용할 수 있다.

 

또한 Swift에는 Any, AnyObject, nil 타입이 존재한다.

  • Any
    • 모든 데이터 타입을 담을 수 있는 데이터 타입
  • AnyObject
    • 모든 Class 타입을 담을 수 있는 데이터 타입
  • nil
    • 알고 있는 null과 같다

 


데이터 타입 고급

  • 타입 확인
    • 컴파일 시점에 확인한다.
      • 타입이 옳지 않는 코드가 존재하면 컴파일러가 알려준다.
        • Application 내에서 오류가 발생하지 않게 방지한다 -> 안전성!!
  • 타입 별칭
    • Typealias
      • 이후에 나오겠지만, 간략하게 내가 타입을 만드는 것이다.
        • ex) 나는 MyInt 라는 Int 타입을 만들고 싶어!
typealias MyInt: Int

라고 선언하면 MyInt라는 데이터 타입을 Int와 같이 사용 가능 하다

 

  • 튜플
    • 지정된 Data의 묶음
    • 선언 : var person: (String, Int, Double) = ("tree", 84, 183.0) 로 선언이 가능하다
      • 하지만 위 방식대로 선언시 String, Int, Double이 의미하는 것이 뭔지 알 수가 없다.
    • 선언-2 : var person: (name: String, weight: Int, height: Double) 로 선언도 가능하다

컬렉션

  • 배열(Array)
    • C언어의 배열처럼 Buffer
    • 크기가 자동조절된다.
    • 별도의 Stack, Queue가 존재하지 않아 Array를 사용하여 구현이 가능
    • 선언 : var names: [String] = ["tree", "randy"]
    • 선언-2 : var names: Array<String> = ["tree", "randy"]
    • first, last, append(), insert(), remove() 등 다양한 API를 통해 조작할 수 있다.
  • 딕셔너리(Dictionary)
    • 키, 값 으로 이뤄진 자료구조
    • 키는 중복이 불가능하다.
    • 다른 언어들의 Map과 유사하다
    • 선언: var dic: [String, Int] = ["weight": 84]
  • 세트(Set)
    • 순서없이 저장된다
    • 값 중복 불가
    • 해시 가능한 값을 ItemType으로 가질 수 있음
    • 선언 : var set: Set<Int> = [84]
    • 다른 언어들의 Set와 유사하다
  • 열거형(Enum)
    • 제한된 선택지를 줄 때 사용 가능
    • 정해진 값 외에는 입력받고 싶지 않을 때 주로 사용
    • 예상된 입력값들이 한정되어 있을 때 사용
    • 항목별로 값(rawValue)을 가질 수도 있다
    • 연관값(AssociatedValue)를 가질 수 있음
    • 주로 switch 블럭과 사용한다.
    • + Optional 또한 enum타입이다.
enum Optional<Wrapped> {
  case none // nil
  case some(Wrapped) // optional value
}

기본 열거형

enum School{
	case elementary
    case middle
    case high
    case university
}

var mySchool: School = School.middle
var yourSchool: School = .high

원시값(rawValue)

enum School: String{
	case elementary = "초등"
    case middle = "중등"
    case high = "고등"
    case university = "대"
}

var mySchool = .middle
print("\(mySchool.rawValue)") // "중등"

연관값

enum MainDish{
	case pasta(taste: String)
    case pizza(taste: String, dough: String)
    case rice
}

var dinner: MainDish = MainDish.pasta(taste: "로제")
dinner = .pizza(taste: "너무 맛나", dough: "쌀가루")

열거형 - 항목 순회

- 모든 case를 알아야할 때 사용 

- ex) for문을 돌 때도 사용 가능

- B.U.T. 연관값을 갖는 다면 사용 불가

enum School: CaseIterable{
	//CaseIterable 프로토콜을 채택
    case elementary
    case middle
    //....
}

let allCases: [School] = School.allCases

순환 열거형

- 열거형 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용

- indirect 키워드를 사용

 


흐름 제어

조건문

  • if
    • 항상 true/false만 가능하다 -> 다른 언어에서의 0, 1, -1 등의 값을 넣을 수 없다
    • else 생략 가능
  • switch
    • break는 선택 사항
    • 이어서 실행하려면 fallthrough를 사용하면 된다
      • case가 0부터 5까지 있을 때, 다른 언어에서는 0 수행 블럭 끝에 break를 적지 않으면 1이 수행되었다
      • Swift는 반대라고 생각하면 된다. 적지 않아도 자동으로 break 동작을 한다
      • B.U.T. case 0을 수행하고 1을 수행하고 싶다면 0 블럭 끝에 fallthrough를 적어주면 된다.
    • default: 도 존재한다
    • switch에서 다양한 Swift 패턴들을 사용할 수 있다
    •  
// 1번째 예제
switch tuple{
	case ("tree", 183):
    	print(...)
    case (_, 183): // 와일드카드 패턴
    	print(...)
    case ("tree", let height): //값 바인딩 패턴
    	print(height) // height 사용 가능
}

// 2번째 예제

let 직급 = "사원"
let 연차 = 1
let 인턴 = false

switch 직급{
	case "사원" where 인턴 == true: //where는 뒤쪽에서 포스팅 하겠다
    // 직급이 사원이며, 인턴을 했다면
    // ....
    case "사원" where 연차 < 2 && 인턴 == false:
    // ....
}



반복문

  • for-in
for i in 0...10{
	print(i)
}

for char in str{
	print(char)
}
for(i in 0..10){
	println(i)
}

for(char in str){
	println(char)
}

위는 스위프트 아래는 코틀린 문법이다. 같은 동작을 한다

 

  • while
    • Boolean 타입을 사용해야 한다
  • repeat-while
    • 다른 언어에서의 do-while 문과 같다

 

야곰님의 Swift Programming을 공부하며 혼자 끄적였던 것들을 옮긴다.

Swift를 공부하는 다른 개발자들에게도 0.1만큼의 도움이 되었으면 좋을 것 같다는 마음으로 시작!

 

Swift

- 고차원적 언어

- ARC(자동 참조 카운팅)으로 메모리를 관리한다

  - 자바 기반의 GC와 비슷한 역할을 하지만, 전혀 다르다고 말할 수 있다. 이에 대해서는 이후에 작성할 것임

- Object-C의 동적 객체 모델과 매개변수를 도입했다

 

특징

  • 안전성
    • guard, 옵셔널, 오류처리 등으로 보다 안전하다
    • 타입에 대해 엄격하다 -> 개발자에게는 오히려 불편하지만, 프로그램 관점으로 본다면 안전하다
  • 신속성
  • FP(함수형 프로그래밍) 패러다임을 적용
  • POP(프로토콜 지향 프로그래밍)

함수형

  • 대규모 병렬처리에 유용하다
  • 상태의 변화없이 데이터를 처리할 수 있다
    • 함수형이 아니라면 포인터, 레퍼런스 등이 변경되어 함수 내부 처리에 영향을 준다
      • 즉, 순수함수를 작성하기 힘들다.
  • 함수를 일급시민으로 다룬다.
    • 전달인자(매개변수)로 전달 가능
    • 동적 프로퍼티로 할당 가능
    • 변수나 데이터 구조 내에 할당 가능
    • 반환 값으로 반환 가능
func sum(first: Int) -> (Int) -> Int{
	return { second in
    	return first+second
    }
}

sum(first:10)(5)

해당 메소드처럼 하나의 매개변수만 담을 수 있게 하는 기법을 커링이라 한다.

 

커링이란?

- 여러개의 매개변수를 갖는 함수를 매개변수 하나를 갖는 함수의 나열로 표현

 

프로토콜 지향 프로그래밍 POP

- 뒤~에서 설명

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts