Jihyeonnn
송지현
Jihyeonnn
전체 방문자
오늘
어제
  • Jihyeon Song (29)
    • C++ (0)
    • C (0)
    • Linux (0)
    • iOS (23)
    • JAVA (0)
    • Python (1)
    • GitHub (0)
    • 개인 프로젝트 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • array
  • swift
  • iOS 개발
  • 기초
  • 파이썬 #python #개발 #기초
  • optionalchaining
  • generic
  • 코딩
  • ios
  • Mac
  • 프로그래밍
  • 개발
  • iOS개발
  • 스위프트
  • Mac사용법
  • 개발자
  • IT
  • UI

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jihyeonnn

송지현

Swift - Optional Chaining, Throwing function, Generic, Array
iOS

Swift - Optional Chaining, Throwing function, Generic, Array

2025. 4. 9. 16:33

옵셔널 체이닝이란?

  옵셔널 값이 nil이 아니면 안전하게 해당 값에 접근하고, nil일 경우에는 아무것도 하지 않고 nil을 반환하는 것을 말한다.

var x : String? = "Hi"  // 옵셔널 타입 선언: 'String?'은 nil을 가질 수 있는 문자열 타입을 의미
                         // x는 초기값 "Hi"를 가지는 옵셔널 변수로 선언됨
                         
print(x, x!)  // 'x'는 옵셔널 타입이므로 출력 시 Optional("Hi") 형식으로 출력됨
              // 'x!'는 강제 언래핑(Forced Unwrapping) 연산자로, x가 nil이 아니면 그 값을 안전하게 추출
              // 'x!'를 사용하면, 옵셔널이 nil일 경우 런타임 오류가 발생할 수 있음

if let a = x {  // 옵셔널 바인딩(Optional Binding): x가 nil이 아니면 'a'에 안전하게 값을 바인딩
    // x가 nil이 아니면, x의 값을 변수 a에 할당하고 코드 블록을 실행
    print(a)  // 'a'는 'x'의 실제 값을 가지고 있음
}

let b = x! .count  // 강제 언래핑: 'x!'는 옵셔널을 강제로 풀어내고, 문자열의 'count' 속성에 접근
                   // 'count'는 문자열의 길이를 반환하는 프로퍼티
                   // 여기서 x는 "Hi"이므로, .count는 2를 반환함
print(type(of: b), b)  // 'type(of: b)'는 'b'의 타입을 출력, 'b'의 타입은 Int이고 값은 2임

let b1 = x?.count  // 옵셔널 체이닝(Optional Chaining): 'x'가 nil이 아닌 경우에만 'count'에 접근
                   // 'x'가 nil이라면, 'x?.count'는 nil을 반환하고, nil이 아니면 count를 실행하여 값을 반환
                   // 'x'가 "Hi"이므로 count는 2를 반환하고, b1은 Optional(2)가 됨
print(type(of: b1), b1, b1!)  // 'type(of: b1)'은 b1의 타입을 출력, 'b1'은 Optional(Int) 타입
                              // 'b1!'을 강제 언래핑하여 실제 값을 출력하면 2가 됨

let c = x ?? ""  // 널 병합 연산자(Null-Coalescing Operator): 'x'가 nil일 경우 기본값인 ""을 반환
                 // 'x'가 nil이 아니면 'x'의 값을 그대로 반환함. 여기서 'x'는 "Hi"이므로 'c'는 "Hi"가 됨
print(c)  // 'c'의 값은 "Hi"이고 출력됨

 

//실행 결과
Optional("Hi") Hi
Hi
Int 2
Optional<Int> Optional(2) 2
Hi

 

옵셔널 체이닝의 다른 예시이다.

class Person { // Person 클래스 정의
    var name: String // String 타입의 name 변수 선언
    var age: Int // Int 타입의 age 변수 선언
    
    init(name: String, age: Int) { // 초기화 메서드 정의, 매개변수로 name과 age를 받음
        self.name = name // self 키워드로 인스턴스의 name 변수에 매개변수 name을 할당
        self.age = age // self 키워드로 인스턴스의 age 변수에 매개변수 age를 할당
    }
}

let kim: Person = Person(name: "Kim", age: 20) // kim이라는 상수에 Person 인스턴스 생성
print(kim.age) // kim의 age를 출력

let han: Person? = Person(name: "Han", age: 25) // han이라는 옵셔널 상수에 Person 인스턴스 생성
print((han?.age)!) // 옵셔널 체이닝을 사용하여 han의 age를 강제로 언래핑하여 출력
if let hanAge = han?.age { // 옵셔널 바인딩: han의 age가 nil이 아닐 경우 hanAge 상수에 할당
    print(hanAge) // han의 나이를 출력
} else {
    print("nil") // han이 nil인 경우 "nil" 출력
}
//실행 결과
20
Optional(25)
25
25

 

옵셔널 체이닝 예시 3

class Company {
    var ceo: Person?
}
class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

let apple = Company()
apple.ceo = Person(name: "Kim")
//print(apple.ceo.name) //오류
// 옵셔널 체이닝 없이면:
if let ceo = apple.ceo {
    print(ceo.name) //kim
}
print(apple.ceo!.name) //kim
print(apple.ceo?.name) //Optional("Kim")
// 옵셔널 체이닝
if let name = apple.ceo?.name {
    print(name) //kim
}
print(apple.ceo?.name ?? "CEO가 없습니다") // kim
//실행 결과
Kim
Kim
Optional("Kim")
Kim
Kim

 

try

let result = try? throwingFunction() //함수가 오류를 발생시키면 nil을 반환
//안전하게 사용 가능하며 간단한 오류 처리 가능
if let value = result {
    print("성공: \(value)")
} else {
    print("실패")
}

 

let result = try! throwingFunction() //함수가 오류를 발생시키면 런타임 오류가 발생하여 프로그램이 크래시
//호출하는 시점에 오류가 발생하지 않는다는 확신이 있다면 사용
print("결과: \(result)")

 

throwing function

매개변수 괄호 다음 throws라는 키워드가 있다면 error handing을 해 줘야 한다. 아래의 코드가 그 예시이다.

 

do {
    audioPlayer = try AVAudioPlayer(contentsOf: audioFile)
} catch let error as NSError {
    print("Error-initPlay : \(error)")
}

 

throwing fucntion을 쓸 때는 do ~ try ~ catch의 형태로 사용한다.

 

Generic <>

따로 자료형을 선언하지 않는다.

제네릭은 다양한 데이터 타입을 안전하게 처리할 수 있게 해 주고, 자료형이 다른 똑같은 기능의 함수를 따로 만들지 않아도 된다!

func myPrint <T> (a: T, b: T) { //템플릿의 약자인 T를 많이 사용한다.
    print(b,a)
}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5) 
myPrint(a: "Hi", b: "Hello")

//실행 결과
2 1
3.5 2.5
Hello Hi
class Box {
    var item: Int
    init(item: Int) {
        self.item = item
    }
    func getItem() -> Int {
        return item
    }
} //일반 클래스
let intBox = Box(item: 12)
// Box<Int>(item: 123), generic class는 이렇게 쓰지만 타입 추론으로 <Int> 생략 가능
print(intBox.getItem()) // 12
//let stringBox = Box(item: "Hello") // Box<String>(item: "Hello")
//print(stringBox.getItem()) // Hello

 

제네릭으로 클래스도 선언할 수 있다.

class Box<T> {
    var item: T
    init(item: T) {
        self.item = item
    }
    func getItem() -> T {
        return item
    }
}
let intBox = Box(item: 12)
// Box<Int>(item: 123), generic class는 이렇게 쓰지만 타입 추론으로 <Int> 생략 가능
print(intBox.getItem()) // 12
let stringBox = Box(item: "Hello") // Box<String>(item: "Hello")
print(stringBox.getItem()) // Hello

 

Array

// 타입 추론을 사용하여 정수 배열을 정의
let number = [1, 2, 3, 4] // 타입 추론: number는 Array<Int>로 추론됨

// 명시적으로 Int 타입의 배열을 정의
let odd: [Int] = [1, 3, 5] // odd는 Array<Int>로 선언

// 명시적으로 Array<Int> 타입을 선언
let even: Array<Int> = [2, 4, 6] // even은 Array<Int>로 선언

// number 배열의 타입 출력
print(type(of: number)) // Array<Int>
// number 배열의 내용 출력
print(number) // [1, 2, 3, 4]

// odd 배열의 타입 출력
print(type(of: odd)) // Array<Int>
// odd 배열의 내용 출력
print(odd) // [1, 3, 5]

// even 배열의 타입 출력
print(type(of: even)) // Array<Int>
// even 배열의 내용 출력
print(even) // [2, 4, 6]

// 타입 추론을 사용하여 문자열 배열을 정의
let animal = ["dog", "cat", "cow"] // 타입 추론: animal은 Array<String>으로 추론됨

// animal 배열의 타입 출력
print(type(of: animal)) // Array<String>
// animal 배열의 내용 출력
print(animal) // ["dog", "cat", "cow"]

 

Empty Array

var number : [Int] = [] //var number : [Int] = []
//빈 배열을 let으로 만들 수는 있지만 초기값에서 변경 불가이니 배열의 의미 없음
var odd = [Int]()
var even : Array<Int> = Array()
print(number) //[]
//print(number[0]) //오류, 빈 배열을 값을 넣은 다음에 접근
number.append(100) //let으로 선언한 불변형 배열이라 추가 불가능
//error: cannot use mutating member on immutable value: 'number' is a 'let' constant
print(number[0])
number.append(200)
print(number[0], number[1],number)

//실행 결과
[]
100
100 200 [100, 200]

 

초기값이 변경되지 않는 배열은 let으로 만드는 게 더 좋다. 하지만 빈 배열의 경우 계속 값을 추가해 줘야 하기 때문에 var로 선언해야 한다.

 

// 0으로 초기화된 5개의 요소를 가진 배열 x 생성
var x = [0, 0, 0, 0, 0]
print(x) // [0, 0, 0, 0, 0] 출력

// 0으로 초기화된 5개의 요소를 가진 배열 x1 생성
var x1 = Array(repeating: 0, count: 5)
print(x1) // [0, 0, 0, 0, 0] 출력

// 1로 초기화된 3개의 요소를 가진 Int 타입의 배열 x2 생성
var x2 = [Int](repeating: 1, count: 3)
print(x2) // [1, 1, 1] 출력

// "A"로 초기화된 4개의 요소를 가진 String 타입의 배열 x3 생성
var x3 = [String](repeating: "A", count: 4)
print(x3) // ["A", "A", "A", "A"] 출력

 

for ~ in 으로 배열의 항목 접근하기

let colors = ["red", "green", "blue"]
print(colors)
for color in colors {
    print(color)
}

//실행 결과
["red", "green", "blue"]
red
green
blue

 

count, isEmpty로 항목이 몇 개인지, 비어 있는지 알아내기

let num = [1, 2, 3, 4]
var x = [Int]()
print(num.isEmpty) //배열이 비어있나? false
print(x.isEmpty)
if num.isEmpty {
    print("비어 있습니다")
}
else {
    print(num.count) //배열 항목의 개수
}

//실행 결과
false
true
4

 

배열의 first와 last 프로퍼티는 옵셔널 값으로 출력된다. 비어있을 수도 있기 때문이다.

let num = [1, 2, 3, 4]
let num1 = [Int]()
print(num.first, num.last)//Optional(1) Optional(4)
print(num1.first, num1.last)//nil nil
if let f = num.first, let l = num.last {
    print(f,l) //1 4
}

 

첨자로 항목에 접근하는 법이다.

var num = [1, 2, 3, 4]
print(num[0], num[3]) //1 4
print(num.first!) //1
for i in 0...num.count-1{
    print(num[i]) // 1\n 2\n 3\n 4\n
}
print(num[1...2]) //[2, 3]
num[0...2] = [10,20,30]
print(num) //[10, 20, 30, 4]

 

항목을 추가 / 제거하는 방법이다.

// let x = [1, 2, 3, 4] // 불변 배열은 초기값에서 변경 불가
// x.append(5) // 오류 발생: let으로 선언된 배열은 변경할 수 없음

var num = [1, 2, 3] // 가변 배열 num 생성
print(num) // [1, 2, 3] 출력

num.append(4) // 4 추가
print(num) // [1, 2, 3, 4] 출력

num.append(contentsOf: [6, 7, 8]) // [6, 7, 8] 추가
print(num) // [1, 2, 3, 4, 6, 7, 8] 출력

num.insert(5, at: 4) // 인덱스 4에 5 추가
print(num) // [1, 2, 3, 4, 5, 6, 7, 8] 출력

num.remove(at: 3) // 인덱스 3의 요소 제거
print(num) // [1, 2, 3, 5, 6, 7, 8] 출력

num.removeLast() // 마지막 요소 제거
print(num) // [1, 2, 3, 5, 6, 7] 출력

print(num.firstIndex(of: 2)) // Optional(1), 2가 처음으로 나오는 인덱스

if let i = num.firstIndex(of: 2) { // 2가 처음으로 저장된 방의 값을 20으로 변경
    num[i] = 20 // num[1] = 20
}
print(num) // [1, 20, 3, 5, 6, 7] 출력

num = num + num // 배열을 자신과 더하여 중복 생성
print(num) // [1, 20, 3, 5, 6, 7, 1, 20, 3, 5, 6, 7] 출력

num += [8, 9] // [8, 9] 추가
print(num) // [1, 20, 3, 5, 6, 7, 1, 20, 3, 5, 6, 7, 8, 9] 출력

num.removeAll() // 모든 요소 제거
print(num) // [] 출력

 

Array는 구조체이다.

var num = [1,2,3]
var x = num //x는 num의 복사본, 별개의 배열
num[0]=100
print(num) //[100, 2, 3]
print(x) //[1, 2, 3]

 

배열의 최댓값과 최솟값이다.

var num = [1,2,3,10,20]
print(num) //[1, 2, 3, 10, 20]
print(num.min()) //Optional(1)
print(num.max()) //Optional(20)
print(num.min()!) //1
print(num.max()!) //20

 

요소 정렬하기(sort와 sorted의 차이)

var num = [1,5,3,2,4]
num.sort() //오름차순 정렬하여 원본 변경
print(num) //[1, 2, 3, 4, 5]
num[0...4] = [2,3,4,5,1]
num.sort(by:>) //내림차순 정렬하여 원본 변경
print(num) //[5, 4, 3, 2, 1]
num[0...4] = [2,3,4,5,1]
num.reverse() //반대로 정렬하여 원본 변경
print(num) //[1, 5, 4, 3, 2]
print(num.sorted()) //오름차순 정렬 결과를 리턴하고, 원본은 그대로, var x = num.sorted()
//[1, 2, 3, 4, 5]
print(num) //[1, 5, 4, 3, 2]
print(num.sorted(by:>)) //내림차순 정렬 결과를 리턴하고, 원본은 그대로
//[5, 4, 3, 2, 1]
print(num)//[1, 5, 4, 3, 2]

 

sort는 원본을 변경해서 정렬하고, sorted는 정렬한 결과를 잠깐 리턴하는 것이기 때문에 원본은 그대로이다.

sorted 한 결과를 저장하려면 다른 변수를 선언하여 넣어 줘야 한다.

'iOS' 카테고리의 다른 글

Swift - Open API 기반 앱 만들기(미완성)  (0) 2025.05.07
Swift - enum, struct, extension  (1) 2025.04.16
Swift - TableView  (0) 2025.04.02
Swift - 프로토콜  (0) 2025.03.26
Swift의 주요 문법 복습  (0) 2025.03.26
    'iOS' 카테고리의 다른 글
    • Swift - Open API 기반 앱 만들기(미완성)
    • Swift - enum, struct, extension
    • Swift - TableView
    • Swift - 프로토콜
    Jihyeonnn
    Jihyeonnn

    티스토리툴바