swift 기본문법 - 옵셔널(Optional)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


Optional

값이 ‘있을 수도, 없을 수도 있음’

let optionalConstant: Int? = nil  //혹은
let optionalConstant: Optional<Int> = nil

let someConstant: Int = nil  // 컴파일 에러 발생

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

Optional은 왜 필요한가?

nil의 가능성을 명시적으로 표현해주는 것

  • nil의 가능성을 문서화하지 안아도 코드만으로 충분히 표현 가능
    • 문서/주석 작성 시간을 절약
  • 전달받은 값이 옵셔널이 아니라면 nil체크를 하지 않더라도 안심하고 사용 가능
    • 효율적인 코딩이 가능해짐
    • 예외 상황을 최소화하는 안정된 코딩 가능
func someFunction(someOptionalParam: Int?) {
  // ...
}

func someFunction(someOptionalParam: Int) {
  // ...
}

someFunction(someOptionalParam: nil)
someFunction(someOptionalParam: nil)  // 컴파일 에러 발생

컴파일 단계에서도 안전하게 Optional값을 처리 가능하다.

Optional의 표현방식 > ?, !

Implicitly Unwrapped Optional, 암시적 추출 옵셔널 > !

타입 뒤에 !를 표현하는 것

옵셔널 타입 자체는 열거형이기 때문에 switch구문으로 표현이 가능하다.

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값을 할당했기 때문
optionalValue = optionalValue + 1

일반적인 옵셔널 > ?

var optionalValue: Int? = 100

switch optionalValue {
case .none:  // 값이 없다
  print("This Optional variable is nil!!")

case .some(let value):  // 값이 들어왔다
  print("Value is \(value)")
}

// nil 할당 가능
optionalValue = nil

// 기존 변수처럼 사용불가 - 옵셔널과 일반 값은 다른 타입이기 때문에 연산불가
optionalValue = optionalValue + 1

Optional Unwrapping, 옵셔널 값 추출

옵셔널 값을 어떻게 꺼내서 쓰고 활용하는가?

  • 옵셔널 바인딩(Optional Binding) > if let
  • 강제 추출(Force Unwrapping) > !

옵셔널 바인딩(Optional Binding)

옵셔널 값을 꺼내오는 방법 중 하나로 nil체크 + 안전한 값 추출하다는 특징을 가진다.

값이 있는지 확인해보고 값이 있다면 꺼내오고 없다면 지나간다.

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

var myName: String? = nil

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

전달되는 값의 타입이 다를때마다 컴파일 오류가 발생하기 때문에 if-let을 통해 안전하게 옵셔널 값을 추출하도록 한다.

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(myName)

그리고 여러 옵셔널 변수들을 바인딩할 수 있다. 그러나 해당 변수들에 모두 값이 들어가있어야 한다.

var myName: String? = "zehye"
var youtName: String? = nil

if let name = myName, let friend = youtName {
  print("\(name) and \(friend)")  // youtName이 nil이기 때문에 실행이 안됨
}

yourName = "hana"
if let name = myName, let friend = youtName {
  print("\(name) and \(friend)")  // 실행됨
}

강제 추출(Force Unwrapping)

옵셔널의 값을 강제로 추출

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

var myName: String? = "zehye"
printName(myName!)  // zehye !를 사용함으로써 값을 강제로 추출

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

var yourName: String! = nil  // 처음 선언시 사용 가능
printName(yourName)  // 여기서 ! 사용하지 않았어도 강제추출 가능

swift 기본문법 - 조건문과 반복문

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


조건문

if-else

if condition {
  statements
} else if condition {
  statements
} else {
  statements
}

예시는 아래와 같다.

if someInteger < 100 {
  print("100 미만")
} else if someInteger > 100 {
  print("100 이상")
} else {
  print("100이다!")
}

스위프트의 조건에는 항상 bool타입이 들어와야 한다. someInteger는 bool타입이 아닌 Int 타입이기 때문에 컴파일 오류가 발생한다.

Switch

if는 bool 값으로 조건을 확인하기 위해, 양자 택일의 형태밖에 처리하지 못한다.

값을 확인하고 다수의 분기를 할 수 있도록 하는것이 switch문이다. 체크하는 값을 준비하고 그 값에 따라 처리하는 부분에 점프를 한다.

  • swift 이후에 체크하는 값을 작성한다. » 숫자, 텍스트, 논리값 다 쓸 수 있다.
  • case: 값을 작성한다.
  • default: 마지막에는 default를 준비해야한다.
switch value {
case pattern:
  code
default:
  code
}

스위프트에서는 범위연산자 라는게 등장하는데 이를 활용하면 더욱 쉽고 유용하게 사용가능하다.

switch someInteger {
case 0:
  print("zero")
case 1..<100:
  print("1~99")
case 100:
  print("100")
case 101...Int.max:
  print("over 100")
default:
  print("unknown")
}

그리고 정수 외의 대부분의 기본 타입을 사용할 수 있다.
문자열을 가지고 비교값에 넣어주고 case구문을 작성할 수 있다.

switch "zehye" {
case "jake", "mina":
  print("jake")
case "zehye":
  print("zehye")
default:
  print("unknown")
}

그런데 스위프트의 switch 구문에서는 명확히 case가 다 명시되지 않는 한 default 구문을 반드시 작성해주어야 한다. 그리고 case구문 다음에 break이 없는데, 명시적으로 break를 하지 않아도 break가 걸린다.

혹은 break가 걸리지 않게 하고 싶다면 fallthrough를 사용해주면 된다.

switch "zehye" {
case "jake":
  print("jake")
  fallthrough
case "mina":
  print("mina")
case "zehye":
  print("zehye")
  fallthrough
default:
  print("unknown")
}

>>>zehye
   unknown

반복문

반복문은 컬렉션 타입과 많이 사용되므로 미리 변수와 상수를 만들어놓도록 한다.

var integers = [1,2,3]
let people = ["zehye": 10, "eric": 15, "mike": 12]

for-in

for item in items {
  code
}

예시는 아래와 같다.

for integer in integers {
  print(integer)  // [1,2,3]
}

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

while

while condition {
  code
}

예시는 아래와 같다.
조건문과 같이 bool값이 들어와야 한다.

while integers.count > 1 {
  integers.removeLast()
}

repeat-while

다른 언어의 do~while과 같다.
while문과 비슷하지만 반복문 내의 코드를 우선 한번은 실행 한 뒤 조건문을 테스트하는 점이 차이점이다.

repeat {
  code
} while condition

예시는 아래와 같다.

repeat {
  integers.removeLast()
} while integers.count > 0

swift 기본문법 - 함수 고급

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


매개변수 기본값

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

func 함수이름(매개변수1 이름: 매개변수1 타입, 매개변수2 이름: 매개변수2 타입 = 매개변수 기본값 ...) -> 반환타입 {
  함수 구현부
  return 반환값
}

예시는 아래와 같다.

func greeting(friend: String, me: String = "zehye") {
  print("hello \(friend)! I'm \(me)")
}

// 뿐만 아니라 매개변수 기본값을 가지는 매개변수는 생략이 가능하고 수정도 가능하다.
greeting(friend: "hana")
greeting(friend: "John", me: "eric")

전달인자 레이블

전달인자 레이블은 함수를 호출할 때 매개변수의 역할을 좀 더 명확하게 하거나 함수 사용자의 입장에서 표현하고자 할 때 사용한다.

func 함수이름(전달인자 레이블 매개변수1 이름: 매개변수1 타입, 전달인자 레이블 매개변수2 이름: 매개변수2 타입 ...) -> 반환타입 {
  함수 구현부
  return 반환값
}

예시는 아래와 같다.

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

위와 같이 함수의 중복 정의 또한 손쉽게 할 수 있다.
이미 greeting이라는 함수가 있었음에도 불구하고 to, from이라는 레이블을 이용해 함수 이름이 바뀐 효과를 갖는다.

함수의 외부에서는 to 라는 전달인자 레이블을 통해 함수를 호출하게 되고, 함수 내부에서는 friend 라는 매개변수 이름을 통해 매개변수를 사용하게 된다.
이렇듯 함수외부에서는 꼭 전달인자 레이블을 사용해야하며, 함수 내부에서는 매개변수 이름을 사용해주어야 한다.

greeting(to: "hana", from: "zehye")

가변 매개변수

매개변수를 통해 전달되어올 값들의 개수를 알기 어려울때 사용한다. 가변 매개변수는 함수당 하나만 가질 수 있다.

func 함수이름(매개변수1 이름: 매개변수1 타입, 전달인자 레이블 매개변수2 이름: 매개변수2 타입...) -> 반환타입 {
  함수 구현부
  return
}

예시는 아래와 같다.

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

이때 전달인자가 없거나 nil을 넣어주게 되면 오류가 발생한다.

print(sayHelloToFriends(me: "zehye", friend: ))
print(sayHelloToFriends(me: "zehye", friend: nil))

만약 가변 인자에 아무값도 넘겨주고 싶지 않다면 전달인자 레이블을 생략하면 된다.

print(sayHelloToFriends(me: "zehye"))

데이터 타입으로서의 함수

  • 스위프트는 함수형 프로그래밍 패러다임을 포함하는 다중 패러다임언어이다.
  • 스위프트의 함수는 일급객체이므로 변수, 상수 등에 저장이 가능하고 매개변수를 통해 전달 또한 가능하다.

그래서 스위프트의 함수는 하나의 데이터 타입으로서 표현이 될 수 있다.

(매개변수1 타입, 매개변수2 타입) -> 반환타입  // 반환타입은 생략 불가능
// 해당 변수에 함수가 들어올 것이라고 선언
var someFunction: (String, String) -> Void = greeting(to:from:)
someFunction("eric", "hana")

someFunction = greeting(friend:me:)
someFunction("eric", "hana")

// 타입이 다른 함수는 할당 불가능, 가변매개변수로 오는 함수
someFunction = sayHelloToFriends(me: friends:)

func runAnother(function: (String, String) -> Void) {
  function("jenny", "mike")
}

runAnother(function:greeting(friend:me:))
// 변수에 담겨있는 함수를 넘겨줄 수 있다
runAnother(function: someFunction)

swift 기본문법 - 함수 기본

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


함수의 선언

함수의 기본 형태

func 함수이름(매개변수1 이름: 매개변수1 타입, 매개변수2 이름: 매개변수2 타입 ...) -> 반환타입 {
  함수 구현
  return 반환값
}

간단하게 구현해보면 아래와 같다.

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

함수의 반환값이 없는 경우

함수의 반환값이 없는 경우 void를 사용한다
void는 없다 라는 표현의 타입 별칭이다.

func 함수이름(매개변수1 이름: 매개변수1 타입, 매개변수2 이름: 매개변수2 타입 ...) -> Void {
  함수 구현
  return
}

예시는 아래와 같다.

func printMyName(name: String) -> Void {
  print(name)
}

혹은 void는 생략도 가능하다.

func 함수이름(매개변수1 이름: 매개변수1 타입, 매개변수2 이름: 매개변수2 타입 ...) {
  함수 구현
  return
}

예시는 아래와 같다.

func printMyName(name: String)  {
  print(name)
}

함수의 매개변수가 없는 경우

func 함수이름() -> 반환타입 {
  함수 구현부
  return 반환값
}
func maximumIntegerValue() -> Int {
  return Int.max
}

함수의 매개변수와 반환값이 없는 경우

func 함수이름() -> {
  함수 구현부
}

혹은

func 함수이름() {
  함수 구현부
}
funcA() { print("bye") }

함수의 호출

sum(a:3, b:5)
printMyName(name: "zehye")
maximumIntegerValue()

swift 기본문법 - 컬렉션 타입(Array, Dictionary, Set)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


컬렉션 타입

여러 값들을 묶어 하나의 변수를 표현할 수 있도록 해준다.

Array

순서가 있는(인덱스가 있는) 리스트 컬렉션


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

integers.append(1)  // [1]
integers.appenr(100)  // [1,100]

integers.contains(100)  // true
integers.contains(99)  // false

// 0번 인덱스의 값을 삭제
integers.remove(at: 0)  // 1

// 마지막 인덱스 값을 삭제
integers.remove()  

// 모든 인덱스 값을 삭제
integers.removeAll()  

integers.count()

integers[0]

Array를 생성하는 다양한 방법

var doubles: Array<Double> = [Double]()

var strings: [String] = [String]()

var characters: [Character] = []

// let을 사용해 Array를 선언하면 불면 Array가 된다.
let immutableArray = [1,2,3]  

Dictionary

키와 값의 쌍으로 이루어진 컬렉션(key, value)

특별한 정렬 순서는 정해져있지 않다.

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

anyDictionary["someKey"] = "value"
anyDictionary["anotherKey"] = 100

// key에 해당하는 value를 삭제하고 싶을 때
anyDictionary.removeValue(forKey: "anotherKey")
anyDictionary["someKey"] = nil

// let으로 선언했기 때문에 Dictionary의 값을 변경할 수 없다
let emptyDictionary: [String: Stinrg] = [:]
let initalizedDictionary: [String: String] = ["name": "zehye", "gender": "female"]

Set

순서가 없고, 멤버가 유일한 컬렉션(중복된 값이 없다)

var integerSet: Set<Int> = Set<Int>()
integerSet.insert(1)
integerSet.insert(100)
integerSet.insert(99)
integerSet.insert(99)
integerSet.insert(99)

integerSet  // {100,99,1}

integerSet.contains(1)  // true
integerSet.contains(2)  // false

integerSet.remove(100)
integerSet.removeFirst()

integerSet.count  

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

// A와 B의 합집합
let union: Set<Int> = setA.union(setB)  // {2,4,5,6,1,3,7}

// 합집합을 정렬
let sortedUnion: [Int] = union.sorted()  // {1,2,3,4,5,6,7}

// 교집합
let intersection: Set<Int> = setA.intersection(setB)  // {5,3,4}

// 차집합
let subtracting: Set<Int> = setA.subtracting(setB)  // {2,1}