Swift 기본 문법 #2

원본 링크 : https://dawn-horse-972.notion.site/Week-2-982b50c01da04d2faebb13f39228784f

학습목표

  1. 기본 컬렌션 타입을 활용해 데이터 메모리에 저장할 수 있다
  2. 반복문 활용해 문제 해결
  3. 다양한 형태의 함수 이용, 응용
  4. 논리 연산자의 종류를 알고 코드에 활용
  5. 열거형을 통해 프로그래머에게 한정된 선택지 제공해보기
  6. 조건문을 활용해 상황에 따른 동작 제어
  7. Optional을 이해하고 안전하게 사용하기

 01. 컬렉션 타입

  • Array : 순서(인덱스)가 있는 리스트 컬렉션
  • Dictionary : 키와 값의 쌍으로 이루어진 컬렌션 (like HaspMap)
  • Set : 순서가 없고 멤버가 유일한 컬렉션 (like 집합)

Array

선언과 축약 표현

//Int 타입의 빈 Array 생성
var integers: Array<Int> = Array<Int>()

//Array<Int> == [Int]
var integers: Array<Int> = [Int]()

var integers: [Int] = [Int]()

//[]는 새로운 빈 Array
var integers: [Int] = []
  • 같은 표현

    var integers: Array<Int>  = Array<Int>()
    
    var integers: Array<Int> = [Int]()
    var integers: Array<Int> = []
    var integers: [Int] = []
    var integers = [Int]()

let을 사용해 Array를 선언하면 불변 Array가 생성된다

let immutableArray = [1, 2, 3]

//append나 remove같은 메소드 사용 불가

Array 관련 메소드

.append(1)
.contains(100)
.remove(at: 0)
.removeLast()
.removeAll()
.count

//없는 인덱스에 접근하려고 하면 프로그램 강제 종료
//integers[0], integers[99] = 99 

Dictionary

딕셔너리는 순서나 정렬 없이 그저 키 - 값의 쌍으로 요소가 존재할 뿐

선언과 축약표현

//Key - String타입, Value - Any타입인 빈 Dictionary 생성
var anyDictionary: Dictionary<String, Any> = [String: Any]()

//[:]는 빈 딕셔너리를 나타낸다
var emptyDictionary: [String: Any] = [:]

anyDictionary["somekKey"] = "value" //딕셔너리에 "someKey": "value" 추가
anyDictionary["anotherKey" = 100    //딕셔너리에 "anotherKey": 100 추가
  • 같은 표현

    var anyDictionary: Dictionary<String, Any> = Dictionary<String, Any>()
    
    var anyDictionary: Dictionary<String, Any> = [:]
    var anyDictionary: [String: Any] = [String: Any]()
    var anyDictionary: [String: Any] = [:]
    var anyDictionary = [String: Any]()

let으로 선언하면 불변 Dictionary가 생성된다.

let initalizedDictionary: [String: String] = ["name": "sujilee", 
"height": "169.2"] 

Dictionary 관련 메소드

.removeValue(forkey: "anotherKey")

과제?! → 해보니까 옵셔널과 관련있는 듯

//Error!
let someValue: String = initializedDictionary["name"]

//이건 왜 안될까? someValue에 "sujilee"가 담겨야하는 것 아닌가?
//"name" 키에 해당하는 값이 없을 수도 있으므로 String 타입의 값이 나올거라 보장 못함
//nil이 나오면 어쩔건데? 컴파일 오류 발생

Set

순서가 없고, 멤버가 유일한 것을 보장하는 컬렉션 타입

선언. (Set는 축약 표현 X)

var integers: Set<Int> = Set<Int>()

Set 관련 메소드

integers.insert()
integers.contains()
integers.remove()
integers.removeFirst()

integers.count

Set의 응용

import Foundation

var anyDictionary: Dictionary<String, Any> = [String: Any]()

let setA: Set<Int> = [1, 2, 3, 4, 5]
let setB: Set<Int> = [3, 4, 5, 6, 7]

//union() 메소드로 합집합 구하기
let union: Set<Int> = setA.union(setB)

//sorted() 메소드로 집합 정렬시켜 오름차순 String 배열로 만들기
let sortedUnion: [Int] = union.sorted()

//intersection() 메소드로 교집합 구하기
let intersection: Set<Int> = setA.intersection(setB)

//subtracting() 메소드로 차집합 구하기 (setA - setB)
let subtracting: Set<Int> = setA.subtracting(setB)

생각해보기

  • 각각 어떤 컬렉션 타입을, 상수/변수 중 어떤 것을 사용하면 유용할지
  1. 영어 알파벳 소문자를 모아두는 컬렉션 → 상수 Array
  2. 책의 제목과 저자 정리를 위한 컬렉션 → 상수 Dictionary
  3. 우리반 학생 명부 작성을 위한 컬렉션 → 상수 Set
💡 Swift Language - Collection Types : [https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html](https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html)

 02. 반복문

  • for-in
  • while
  • repeat-while

컬렉션 타입과 잘 사용된다

for-in

import Foundation

// for item in items {
//      code
// }

var integers = [1, 2, 3]
let people = ["sujilee": 1206, "kchoi": 1127, "pol": 1101]

for integer in integers {
    print(integer);
}

//Dictionary의 item은 key와 value로 구성된 튜플 타입
for (name, age) in people {
    print("\(name): \(age)")
}

while

condition에는 꼭 bool값이 들어와야한다 !

import Foundation

//while condition {
//  code
//}

var integers = [1, 2, 3]
let people = ["sujilee": 1206, "kchoi": 1127, "pol": 1101]

//한개 남기고 뒤에서부터 반복 삭제
while integers.count > 1 {
    integers.removeLast()
}
print (integers)

repeat - while

기존의 do while과 흡사하다

import Foundation

//repeat {
//  code
//} while condition

var integers = [1, 2, 3]
let people = ["sujilee": 1206, "kchoi": 1127, "pol": 1101]

//일단 한 번 맨 뒤 요소를 제거하고, 전부 사라질때까지 반복하라.
repeat {
    integers.removeLast()
} while integers.count > 0

print(integers)

(do란 키워드가 오류처리에서 쓰여서 do while 안쓰는거임)

💡 Swift Language - Control Flow : [https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html](https://docs.swift.org/swift-book/LanguageGuide/ControlFlow.html)

 03. 함수 고급

매개변수 기본값

import Foundation

//기본값을 갖는 매개변수는 매개변수 목록 중 뒤쪽에 위치하는 것이 좋음

//매개변수 me를 기본값으로 갖는 함수
func greeting (friend: String, me: String = "sujilee") {
    print("Hello \(friend) it's me, \(me)!")
}

//함수 호출 시 매개변수 생략 가능
greeting(friend: "kchoi")
//물론 원하는 인자도 넘길 수 있다
greeting(friend: "kchoi", me: "suji")

전달인자 레이블

함수 호출 시 매개변수의 역할을 더 명확하게 하거나, 함수 사용자의 입장에서 표현하고자 할때 사용

import Foundation

//매개변수 이름 앞에 전달인자 레이블을 표시한다
//함수 구현 시, 함수 내부에서는 매개변수 이름을 사용
func greeting(to friend: String, from me: String) {
    print("Hello \(friend) it's me, \(me)!")
}

//함수 호출 시, 함수 외부에서는 전달인자 레이블 사용
greeting(to: "sean", from: "suji")

위 매개변수 기본값에서 쓰인 greeting 함수와 함수명이 같으나, 전달인자 레이블을 씀으로써 중복방어? 가 되어 다르게 취급된다 !

가변 매개변수

전달받을 매개변수의 개수를 알기 어려울 때 사용 - (5.4미만은 함수 당 하나만 가질 수 있다.)

import Foundation

func sayHelloToFriends(me: String, friends: String...) -> String {
    return "Hello \(friends) it's \(me)!"
}

print(sayHelloToFriends(me: "suji", friends: "kcho", "sean", "joockim"))
//OUTPUT : Hello ["kcho", "sean", "joockim"] it's suji!

데이터 타입으로서의 함수

Swift는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임 언어

함수 : 일급 객체 → 변수, 상수 등에 저장 가능. 매개변수를 통해 전달 가능

스위프트의 함수는 하나의 데이터 타입으로도 표현, 사용 될 수 있음

import Foundation

func greeting(to friend: String, from me: String) {
    print("Hello \(friend) it's me, \(me)!")
}

//변수 someFunction에, (Sting, Sting)을 매개변수로 가지고 Void를 반환하는 함수 할당
//조건에 일치하는 greeting 함수를 할당할 수 있다.
var someFunction: (String, String) -> Void = greeting(to:from:)

//매개변수의 타입이 같아야함
someFunction = greeting(to:from:)
someFunction("sujilee", "jseo")
//OUTPUT : Hello suji it's me, jseo!

//함수의 매개변수로, (String, String)을 매개변수로 가지고 Void를 반환하는 함수를 받음
func runAnother(function: (String, String) -> Void) {
    function("suji", "lea")
}

//runAnother 함수의 매개변수로 조건에 부합하는 greeting함수 넘김.
//greeting함수의 인자는 이미 runAnother 함수 내부에서 지정해놓음
runAnother(function: greeting(to:from:))
//OUTPUT : Hello suji it's me, lea!
runAnother(function: someFunction)
//OUTPUT : Hello suji it's me, lea!
💡 Swift Language - Functions : [https://docs.swift.org/swift-book/LanguageGuide/Functions.html](https://docs.swift.org/swift-book/LanguageGuide/Functions.html)

 04. 논리연산자

Bool → 참/거짓, 논리 연산

  • 비교연산자 → 비교한 결과로 Bool값(참/거짓)을 만든다

  • 논리 연산자
true && false == false
true && true == true
true || false == true
true || true == true
  • 비트연산자(Bitwise operator)

수학적 연산보다는 비트검출이나 옵션전달의 목적으로 사용

//옵션 선택지 전달 예시

import Foundation

let apple: Int = 1
let orange: Int = 2
let banana: Int = 4

func printFavoriteFruits(_ selectedFruits: Int) {
    print("사과를 좋아함 : \((selectedFruits & apple) != 0)")
    print("오렌지를 좋아함 : \((selectedFruits & orange) != 0)")
    print("바나나를 좋아함 : \((selectedFruits & banana) != 0)")
}

printFavoriteFruits(apple | orange)
//apple | orange == 0001 | 0010 == 0011

let 진동모드  = 1 // 0001
let 비행기모드 = 2 // 0010
let 와이파이 = 4 // 0100

let 현재모드 = 진동모드 | 비행기모드 // 0011

Swift에서는 OptionSet 이라는 프로토콜을 활용해 위와 같은 기능 구현

💡 OptionSet : [https://developer.apple.com/documentation/swift/optionset](https://developer.apple.com/documentation/swift/optionset)

 05. 열거형 (Enumeration)

Int, Double, String처럼 스위프트 표준 라이브러리에서 제공하는 타입과 다르게 사용자가 필요에 의해 만들어 사용하는 타입 → Type(타입)의 일종 : 대문자로 시작

  • (다른 프로그래머에게) 제한된 선택지를 주고 싶을 때
  • (함수 등에서 다른 프로그래머에게) 정해진 값 외에는 입력받고 싶지 않을 때
import Foundation

//Dish라는 타입으로 dish 매개변수의 타입을 제한
enum Dish {
    case rice, noodle, soup
}

//Dish에서 정의한 선택지 외에는 dish 매개변수의 인자로 전달할 수 없음
func choose(dish: Dish) {
    print("\(dish)를 골랐군요!")
}

choose(dish: Dish.rice)
//dish에 전달하는 인자는 Dish 타입이 분명하므로 타입 이름 생략, .뒤에 케이스만 적어도 ok
//이런걸 타입추론이라고 하는데, 초보자는 쓰지마라!
choose(dish: .rice)
//아래와 같은 표현 모두 가능

enum Dish {
	case rice
	case noodle
	case soup
}

enum Dish {
	case rice, noodle
	case soup
}

원시값(rawValue) 사용 예시

import Foundation

//원시값(rawValue) 사용 : 각 case별로 고유한 값 지정
enum Dish: String {
    case rice = "밥", noodle = "면", soup = "스프"
}

func choose(dish: Dish) {
    print("\(dish.rawValue)를 골랐군요!")
}

choose(dish: Dish.rice)
//OUTPUT : 밥을 골랐군요!

원시값(rawValue) 사용 예시 2

import Foundation

enum Bearings: String {
    case east = "동"
    case west = "서"
    case south = "남"
    case north = "북"
}

func navigateBearings(bearings: Bearings) {
    print("\(bearings.rawValue)쪽으로 가십시오")
}

navigateBearings(bearings: Bearings.east)

 06. 조건문

  • if-else
  • switch

Swift의 조건에는 항상 Bool 타입이 들어와야 함

if-else

let someInteger = 100

if someInteger < 100 {
    print("100 미만")
} else if someInteger > 100 {
    print("100 초과")
} else {
    print("100")
}
	

switch

import Foundation

//switch value {
//case pattern:
//    code
//default:
//    code
//}

let someInteger = 100

switch someInteger {
case 0:
    print("zero")
//1 이상 100 미만
case 1..<100:
    print("1~99")
case 100:
    print("100")
case 101...Int.max:
    print("over 100")
default:
    print("unknown")
}
switch "sujilee" {
case "kchoi", "sean":
		print("kchoi or sean")
case "joockim"
		print("joockim")
default:
	print("unknown")
}

switch "sujilee" {
case "kchoi":
		print("kchoi or sean")
		fallthrough
case "jseo"
		print("kchoi or sean")
case "joockim"
		print("joockim")
default:
	print("unknown")
}

 07. Optional

옵셔널의 이해

optional : 값이 있을 수도, 없을 수도

옵셔널이 아닌 상수에다 nil값을 할당하려고 하면 컴파일 에러 발생

옵셔널 : nil의 가능성을 명시 → 별도의 문서 작성, 닐 체크 필요없음, 예외사항 최소화

! (Implicity Unwrapped Optional) : 암시적 추출 옵셔널

  • 암시적 추출 옵셔널 형식 예시
import Foundation

var optionalValue: Int! = 100

switch optionalValue {
case .none:
    print("This Optional variable is nil")
case .some(let value):
    print("Value is \(value)")
}
//기존 변수처럼 사용 가능
optionalValue = optionalValue + 1

//nil 할당 가능
optionalValue = nil

//잘못된 접근으로 인한 런타임 오류 -> nil에 어떻게 + 1을 하냔 말이야
optionalValue = optionalValue + 1

? (Optional)

기존 변수처럼 사용 불가 - 여타 타입과는 다른 타입임

옵셔널 값 추출(Optional Unwrapping)

  • Optional Binding(옵셔널 바인딩)

    : 옵셔널의 값을 꺼내오는 방법 중 하나 → nil 체크 + 안전한 값 추출

  • Force Unwrapping(강제 추출)

    :

Optional Binding

상자에 똑똑 - 값이 있습니까?

값이 있으면 꺼내오고, 없으면 지나치고

옵셔널 타입은 엄연한 타입이기 때문에 다른 타입에 할당할 수 없다

func printName(_ name: String) {
		print(name)
}

var myName: String? = nil

//컴파일 에러 발생
printName(myName) //Error! : 전달되는 값의 타입이 다르기 때문에

그럼 어떻게 할까? if-let을 이용해 옵셔널 바인딩을 해줄 수 있다.

import Foundation

func printName(_ name: String) {
    print(name)
}

var myName: String! = nil

if let name: String = myName {
    printName(name)
} else {
    print("myName == nil")
}

// name 상수는 if-let 구문 내에서만 사용 가능

//컴파일 오류 발생 - 상수 사용범위를 벗어남
printName(name) //Error!
import Foundation

var myName: String? = "sujilee"
var yourName: String? = nil

//name과 friend 모두 값이 있어야(nil이 아니어야) print가 실행됨
if let name = myName, let friend = yourName {
    print("\(name) and \(friend)")
}
//yourName이 nil이라 실행되지 않음

yourName = "sean"

if let name = myName, let friend = yourName {
    print("\(name) and \(friend)")
}
//sujilee and sean

옵셔널 강제 추출

정중하게 값을 꺼내오는 옵셔널 바인딩과 다르게 그냥 옵셔널 보호막을 강제로 깨부수고 값을 가지고 나온다

import Foundation

func printName(_ name: String) {
    print(name)
}

var myName: String? = "suijlee"

//!를 붙이면 옵셔널타입인 myName의 값을 강제로 추출해낸 값이라는 뜻
printName(myName!) // 옵셔널 타입인 myName 안의 String 타입의 "sujilee"가 담김
//OUTPUT : sujilee

myName = nil

//런타임 오류 발생
print(myName!) //Error! : 강제추출 시 값이 없으므로 오류

//처음부터 강제추출되는 효과를 가짐 -> 위에 인자로 넘긴 myName!와 같은 의미
var yourName: String! = nil

//런타임 오류 발생
printName(yourName) //Error! : 강제추출 시 값이 없으므로 오류

옵셔널 강제 추출 방식은 그다지 추천되지 않음. 안전하게 옵셔널 바인딩 사용 권장

🐻 Swift Language - The Basicss : [https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html)

딕셔너리 타입의 느낌표

import Foundation

var someDictionary: [String: String] = [:]
//day(of: today)는 예시를 위해 있다고 가정하자
let day: Int = day(of: today)

if day == 1 || day == 30 {
    someDictionary["날씨"] = "맑음"
} else {
    someDictionary["풍향"] = "남동풍"
}

//이 시점에 "날씨" 키에 해당하는 값이 항상 존재한다고 장담할 수 있을까?

컴파일러는 “날씨”키에 해당하는 값이 항상 있을 수 없다고 생각함. 더불어 딕셔너리 타입은 언제든 키와 값을 수정할 수 있기 때문에 컴파일하는 시점에 딕셔너리 내부의 키-값 쌍이 어떻게 변화할지 모름 → 따라서 딕셔너리에서 값을 꺼내올 때에는 항상 값이 없을 수 있음에 대비해야한다.

좋은 웹페이지 즐겨찾기