6주차 - iOS앱
- 옵셔널을 언래핑하는 여러가지 방법
var x: String? = "Hi" // x는 옵셔널 타입(String?)이며 초기값은 "Hi"입니다.
- `x`는 옵셔널 타입으로 선언되었으며, 초기값은 `"Hi"`입니다. 옵셔널 타입은 값이 있을 수도 있고 없을 수도 있는 상태를 나타냅니다.
print(x, x!) // x! 강제 언래핑
- `x`는 옵셔널 타입이므로 출력 시 `Optional("Hi")` 형태로 나타납니다.
- `x!`는 강제 언래핑을 통해 옵셔널 값을 꺼내 `"Hi"`를 출력합니다.
- **주의**: `x`가 `nil`일 경우 강제 언래핑(`x!`)은 런타임 에러를 발생시킵니다.
if let a = x {
print(a)
}
- 옵셔널 바인딩(`if let`)을 사용하여 `x`의 값을 안전하게 추출합니다.
- `x`가 `nil`이 아니라면, 값이 `a`에 바인딩되고 `"Hi"`가 출력됩니다.
- 만약 `x`가 `nil`이라면 이 블록은 실행되지 않습니다.
let b = x!.count // nil값일 수도 있기 때문에 크래시 위험이 있음
print(type(of: b), b)
- 강제 언래핑(`x!`)을 통해 옵셔널 값을 꺼내고, 문자열의 길이를 계산합니다.
- `"Hi"`의 길이는 2이므로 `b = 2`입니다.
- `type(of: b)`는 `Int`로 출력됩니다.
- **주의**: 만약 `x`가 `nil`이라면 강제 언래핑 시 런타임 에러가 발생합니다.
let b1 = x?.count // 크래시 위험때문에 ? 사용함
print(type(of: b1), b1, b1!)
- **옵셔널 체이닝**을 사용하여 안전하게 문자열 길이에 접근합니다.
- `x?.count`는 `x`가 nil일 경우 `nil`, 그렇지 않으면 문자열 길이를 반환합니다.
- 결과적으로, `b1 = Optional(2)`입니다.
- 출력:
- `type(of: b1)`는 옵셔널 타입(`Optional`)입니다.
- `b1!`는 강제 언래핑하여 값을 꺼내므로 2를 출력합니다.
let c = x ?? ""
print(c)
- **Nil 병합 연산자 (`??`)**를 사용하여 기본값을 설정합니다.
- 만약 `x`가 nil이라면 빈 문자열(`""`)을 반환합니다.
- 현재 `x = "Hi"`이므로, 결과적으로 `c = "Hi"`입니다.
---
1. **강제 언래핑 (`!`)**: 위험하지만 직접 값을 꺼낼 수 있음.
2. **옵셔널 바인딩 (`if let`)**: 안전하게 값을 추출하는 방법.
3. **옵셔널 체이닝 (`?.`)**: 중첩된 값에 안전하게 접근 가능.
4. **Nil 병합 연산자 (`??`)**: 기본값 설정으로 nil 처리.
- 두 문장의 공통점과 차이점
let b = x!.count
print(type(of:b),b)
let b1 = x?.count
print(type(of:b1),b1, b1!)
**공통점**
1. 옵셔널 값 처리
- 두 문장 모두 `x`라는 옵셔널 값을 기반으로 작업합니다.
- `x`가 `nil`일 가능성을 고려하여 값을 접근하거나 처리합니다.
2. 값의 길이 확인
- 두 문장 모두 `x`의 문자열 길이를 확인하려는 목적으로 작성되었습니다.
3. 언래핑 방식 사용
- 두 문장은 각각 다른 방식으로 언래핑을 시도하지만, 최종적으로 `count` 값을 추출하려고 합니다.
---
1. **강제 언래핑 ('let b = x!.count')
var x: String? = nil
let b = x!.count // 런타임 에러 발생 (Fatal Error: Unexpectedly found nil while unwrapping an Optional value)
2. **옵셔널 체이닝 ('let b1 = x?.count')
var x: String? = nil
let b1 = x?.count // b1은 Optional(nil)로 설정됨, 에러 없음
print(b1) // 출력: nil
---
### 결론
- 강제 언래핑은 값이 반드시 존재한다고 확신할 때 사용하는 반면, 옵셔널 체이닝은 값이 없을 가능성을 고려하여 안전하게 접근하는 방법입니다
▷ Optional Chaining
// Person 클래스 정의
class Person {
// 이름 속성 (String 타입)
var name: String
// 나이 속성 (Int 타입)
var age: Int
// 초기화 메서드 (initializer)
init(name: String, age: Int) {
// 전달받은 이름으로 Person 객체의 name 속성 초기화
self.name = name
// 전달받은 나이로 Person 객체의 age 속성 초기화
self.age = age
}
}
// Person 클래스의 인스턴스 생성 (Kim)
let kim: Person = Person(name: "Kim", age: 20)
// Kim 객체의 age 속성 출력 (옵셔널이 아님)
print(kim.age) // 출력: 20
// Person 클래스의 옵셔널 인스턴스 생성 (Han)
// Han은 nil일 수도 있는 Person 객체를 가리킬 수 있음
let han: Person? = Person(name: "Han", age: 25)
// 옵셔널 체이닝을 사용하여 han 객체의 age 속성에 접근
// han이 nil이 아니면 age 속성 값을 가져오고, nil이면 nil을 반환
//print(han?.age) //에러 발생: han이 nil일 수도 있으므로, age 속성도 옵셔널(Int?) 타입이 됨.
//print(han!.age) // Han이 nil일 경우 런타임 에러 발생 (강제 언래핑)
//print(han?.age) //Optional(25), 옵셔널 체이닝
// 옵셔널 체이닝과 강제 언래핑을 함께 사용 (위험)
// han이 nil이 아니면 age 속성 값을 가져와 강제로 언래핑
// han이 nil이면 런타임 에러 발생
print((han?.age)!) // 출력: 25
// 옵셔널 바인딩을 사용하여 안전하게 옵셔널 값 처리
// han?.age가 nil이 아니면 hanAge에 값을 할당하고 if 블록 실행
if let hanAge = han?.age {
// hanAge는 옵셔널이 아닌 Int 타입
print(hanAge) // 출력: 25
} else {
// han이 nil이거나 age 속성이 없는 경우
print("nil") // 출력되지 않음 (han
- 옵셔널 체이닝 : 점 앞에 ? 사용
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
- 오류 제어
▷ try?
: 에러를 간단히 무시하거나 실패 여부만 확인하고 싶을 때 사용한다.
let result = try? throwingFunction()
if let value = result {
print("성공: \(value)")
} else {
print("실패")
}
- try!
let result = try! throwingFunction()
print("결과: \(result)")
- 에러가 절대 발생하지 않은 것이라고 가정할 때 사용한다.
- 만약 에러가 발생한다면 프로그램이 크래시
- throwing function
iOS 개발에서 자주 사용되는 throwing function (오류를 던질 수 있는 함수) 10가지:
1. **`Data(contentsOf: URL)`:** URL에서 데이터를 읽을 때, URL 문제나 네트워크 오류 시 오류 발생.
2. **`JSONSerialization.jsonObject(with: Data, options: ...)`:** JSON 데이터를 Swift 객체로 변환할 때, JSON 형식이 잘못되면 오류 발생.
3. **`FileManager.createDirectory(at: URL, ...)`:** 파일 시스템에 디렉토리를 생성할 때, 권한 문제나 공간 부족 시 오류 발생.
4. **`FileManager.copyItem(at: URL, to: URL)`:** 파일을 복사할 때, 권한 문제나 파일 없음 등의 이유로 오류 발생.
5. **`FileManager.removeItem(at: URL)`:** 파일 또는 디렉토리를 삭제할 때, 권한 문제나 파일 없음 등의 이유로 오류 발생.
6. **`String(contentsOf: URL, encoding: ...)`:** URL에서 문자열을 읽을 때, URL 문제나 인코딩 오류 시 오류 발생.
7. **`PropertyListSerialization.propertyList(from: Data, ...)`:** Property List 데이터를 Swift 객체로 변환할 때, 형식 오류 시 오류 발생.
8. **Core Data `managedObjectContext.save()`:** Core Data 변경 사항을 저장할 때, 유효성 검사 실패나 저장소 문제 시 오류 발생.
9. **`NSRegularExpression(pattern: ..., options: ...)`:** 정규 표현식을 생성할 때, 패턴이 유효하지 않으면 오류 발생.
10. **UserDefault에 저장하는 방식:** 저장하려는 데이터가 Property list가 아닐경우 오류가 발생
func can() throws
매개변수 괄호 다음에 throws라는 키워드가 있는 함수는 그냥 사용할 수 없고 error handing을 해야한다.
그냥 AVAudioPlayer(contentsOf:audioFile) 이렇게는 호출 불가
do try catch로 error handing해야 함.
( 하지 않으면 Call can throw, but it is not marked with 'try' and the error is not handled" 오류가 발생 )
▷ Generic <>
func myPrint <T> (a: T, b: T) {
print(b,a)
}
//func myPrint(a: Double, b: Double) { //함수중첩(오버로딩)
// print(b,a)
//}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5)
myPrint(a: "Hi", b: "Hello")
- Generic class
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
- 빈 배열 (swift의 Array도 generic 구조체)
var x : [Int]=[] //빈 배열, empty array
var y = [Int]()
var z : Array<Int> = []
▷ Collection Type
프로그래밍 언어에서 컬렉션 타입(Collection Type)은 여러 개의 데이터 요소(element)를 하나의 단위로 묶어서 관리하고 조작할 수 있도록 제공하는 자료구조입니다. 컬렉션 타입은 데이터를 효율적으로 구성하고, 저장하고, 접근하고, 수정하고, 검색하는 데 사용됩니다.
- 컬렉션 타입의 주요 특징
* **데이터 저장 : 여러 개의 데이터를 저장할 수 있습니다.
* **데이터 관리 : 데이터를 추가, 삭제, 수정, 검색하는 기능을 제공합니다.
* **데이터 접근 : 특정 위치의 데이터에 접근할 수 있는 방법을 제공합니다.
* **다양한 종류 : 프로그래밍 언어는 다양한 컬렉션 타입을 제공하며, 각각 다른 특징과 성능을 가집니다.
**일반적인 컬렉션 타입의 종류:**
1. **배열 (Array/List):**
* 순서가 있는 데이터의 집합입니다.
* 각 요소는 인덱스(index)를 통해 접근할 수 있습니다.
* 대부분의 언어에서 배열의 크기는 고정되거나 동적으로 조절될 수 있습니다.
* 예: `[1, 2, 3, 4, 5]`, `["apple", "banana", "cherry"]`
2. **연결 리스트 (Linked List):**
* 각 요소가 다음 요소의 주소를 가지고 있는 데이터의 집합입니다.
* 배열과 달리 메모리에 연속적으로 저장되지 않습니다.
* 데이터의 삽입/삭제가 빈번하게 일어나는 경우 효율적입니다.
3. **집합 (Set):**
* 중복된 요소를 허용하지 않는 데이터의 집합입니다.
* 요소의 순서는 보장되지 않습니다.
* 특정 요소가 집합에 존재하는지 빠르게 확인할 수 있습니다.
* 예: `{1, 2, 3, 4, 5}`, `{"apple", "banana", "cherry"}`
4. **딕셔너리 (Dictionary/Map/Associative Array):**
* 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합입니다.
* 키를 사용하여 값에 접근할 수 있습니다.
* 키는 중복될 수 없습니다.
* 예: `{"name": "John", "age": 30, "city": "New York"}`
5. **큐 (Queue):**
* FIFO (First-In, First-Out) 원칙에 따라 데이터를 관리하는 자료구조입니다.
* 데이터는 큐의 뒤쪽(rear)에 추가되고, 앞쪽(front)에서 제거됩니다.
* 예: 프린터 큐, 메시지 큐
6. **스택 (Stack):**
* LIFO (Last-In, First-Out) 원칙에 따라 데이터를 관리하는 자료구조입니다.
* 데이터는 스택의 맨 위(top)에 추가되고, 맨 위에서 제거됩니다.
* 예: 함수 호출 스택, 되돌리기 기능
7. **트리 (Tree):**
* 계층적인 데이터 관계를 표현하는 자료구조입니다.
* 노드(node)와 간선(edge)으로 구성됩니다.
* 예: 파일 시스템, 조직도
8. **그래프 (Graph):**
* 노드(node)와 간선(edge)으로 이루어진 자료구조입니다.
* 노드 간의 관계를 표현하는 데 사용됩니다.
* 예: 소셜 네트워크, 도로망
다. 컬렉션 타입을 적절하게 선택하고 사용하는 것은 효율적인 프로그래밍에 매우 중요합니다.
▷ Array
- 프로퍼티
* **`count`**: 배열 요소 개수 확인 (배열 크기)
* **`isEmpty`**: 배열이 비어있는지 확인
- 메소드
* **`append(_:)`**: 배열 끝에 요소 추가
* **`remove(at:)`**: 특정 인덱스의 요소 삭제
* **`forEach(_:)`**: 배열의 각 요소에 대해 반복 작업 수행
* **`map(_:)`**: 배열의 각 요소를 변환하여 새로운 배열 생성
* **`filter(_:)`**: 특정 조건을 만족하는 요소만 골라 새로운 배열 생성
- 빈 배열(empty array) 주의 사항
let으로 선언된 변수는 불변(immutable)하기 때문이다. 즉, 한 번 값이 할당되면 변경할 수 없습니다.
//let number : [Int] = []
var number : [Int] = []
var odd = [Int] ()
var even : Array<Int> = Array()
print(number)
//print(number[0]) //오류, 빈 배열은 값을 넣은 다음에 접근해야 함.
number.append(100) //let으로 선언했다면 불변형 배열이라 추가 불가능
print(number[0])
number.append(200)
print(number[0], number[1], number)
let으로 선언한다면 불변형 배열이라 추가가 불가능하기 때문에 오류가 발생함.
- 초기값에서 변경되지 않는 애들은 굳이 var로 만들지 않고 let으로 만들어도 됨.
- 가변형(mutable)
var animal = ["dog", "cat","cow"]
- 불변형 (immutable)
초기화 후 변경 불가
let animal1 = ["dog", "cat","cow"]
var number : [Int] = []
//number[0]=1 //crash, 방을 만든 후 사용하라!, 첫번째 방을 만들고 1을 넣어라
number.append(1)
print(number)
number[0]=10
print(number)
꼭 append로 방 만들어서 사용해야 함.
- Array(repeating:count:)
var x = [0,0,0,0,0]
print(x) //[0, 0, 0, 0, 0]
var x1 = Array(repeating: 0, count: 5)
print(x1) //[0, 0, 0, 0, 0]
var x2 = [Int](repeating: 1, count: 3)
print(x2) //[1, 1, 1]
var x3 = [String](repeating: "A", count: 4)
print(x3) //["A", "A", "A", "A"]
- for~in으로 배열 항목 접근
let colors = ["red", "green", "blue"]
print(colors) //["red", "green", "blue"]
for x in colors {
print(x)
}
////red
//green
//blue
- 항목이 몇 개인지(count), 비어있는지(isEmpty) 알아내기
let num = [1, 2, 3, 4]
var x = [Int]()
print(num.isEmpty) //배열이 비어있나?, false
if num.isEmpty {
print("비어 있습니다")
}
else {
print(num.count) //배열 항목의 개수, 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
}
- 첨자(subscript)로 항목 접근
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]) //1n\ 2\n 3\n 4\n
}
print(num[1...2]) //[2, 3]
num[0...2] = [10,20,30] //0~2번째 것들이 이걸로 바뀜
print(num) //[10, 20, 30, 4]
- Array : 추가/제거
//let x = [1, 2, 3, 4] //불변 배열은 초깃값에서 변경 불가
//x.append(5)
var num = [1,2,3]
print(num)
num.append(4)
print(num)
num.append(contentsOf: [6, 7, 8])
print(num) //[1, 2, 3, 4, 6, 7, 8]
num.insert(5, at:4)
print(num) //[1, 2, 3, 4, 5, 6, 7, 8]
num.remove(at: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]
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)
print(x)
- 배열(Array) 요소의 최댓값 최솟값 : max(), min()
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
- Array 요소의 정렬 (시험에 잘 나옴!!)
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(x)
print(num) //[1, 5, 4, 3, 2]
print(num.sorted(by:>)) //내림차순 정렬 결과를 리턴하고, 원본은 그대로
//[5, 4, 3, 2, 1]
print(num)//[1, 5, 4, 3, 2]
▷ swift 접근제어
: 접근 속성(접근 수정자, 액세스 수정자, 액세스 지정자 )는 클래스, 메서드, 멤버의 접근 가능성을 설정하는 객체 지향 언어의 키워드
- internal 접근
- 해당 모듈의 모든 소스 파일 내에서 사용되지만, 해당 모듈 외부의 소스파일에서는 사용되지 않도록 함
- package 접근
- 패키지의 모든 소스 파일 내에서 사용할 수 있지만 패키지 외부의 모든 소스 파일에서는 사용할 수 없음
- fileprivate 접근
- 해당 소스 파일 내에서만 사용 가능
- private 접근
- 블록{}과 동일한 파일에 있는 해당 선언의 extension에서만 접근 가능
뤼튼 사용하러 가기 > https://agent.wrtn.ai/5xb91l