옵셔널 체이닝
주로 자바스크립트와 같은 프로그래밍 언어에서 사용되는 문법으로, 객체의 속성에 접근할 때 해당 속성이 null 또는 undefined인 경우에 에러를 발생시키지 않고 안전하게 접근할 수 있도록 해줍니다. 옵셔널 체이닝을 사용하면 코드가 간결해지고, 중첩된 객체의 속성에 접근할 때 발생할 수 있는 오류를 줄일 수 있습니다.
const user = {
profile: {
name: '홍길동',
age: 30
}
};
// 옵셔널 체이닝을 사용하지 않은 경우
const userName = user && user.profile && user.profile.name; // '홍길동'
// 옵셔널 체이닝을 사용한 경우
const userName = user?.profile?.name; // '홍길동'
위의 코드에서 user?.profile?.name은 user나 profile이 null 또는 undefined일 경우 undefined를 반환하고, 그렇지 않으면 name의 값을 반환합니다. 이렇게 함으로써 코드의 가독성을 높이고, 예외 처리를 간편하게 할 수 있습니다.
옵셔널을 언래핑하는 여러가지 방법
var x: String? = "Hi" // 옵셔널 문자열 변수 x를 "Hi"로 초기화
print(x, x!) // x의 값과 x의 강제 언래핑 결과를 출력 (x가 nil이 아닐 경우에만 안전)
if let a = x { // x가 nil이 아닐 경우에만 a에 값을 할당
print(a) // a의 값을 출력
}
let b = x!.count // x를 강제 언래핑하여 문자열의 길이를 b에 저장
print(type(of: b), b) // b의 타입과 값을 출력
let b1 = x?.count // x가 nil이 아닐 경우에만 count를 반환 (nil일 경우 nil)
print(type(of: b1), b1, b1!) // b1의 타입과 값을 출력, b1이 nil이 아닐 경우 강제 언래핑하여 출력
let c = x ?? "" // x가 nil일 경우 빈 문자열("")을 c에 저장
print(c) // c의 값을 출력
swift에서 물음표와 느낌표가 선언문과 실행문에서 쓰일 때 차이
기호 | 사용 용도 | 예시 | 코드 설명 |
? | 옵셔널 타입 선언 | var x: String? = "Hello" | x는 String 타입이지만, nil이 될 수 있는 옵셔널 타입으로 선언됨 |
옵셔널 체이닝 | let length = x?.count | x가 nil이 아닐 경우에만 count를 반환하고, nil이면 결과도 nil | |
! | 강제 언래핑 선언 | let y = x! | x의 값이 nil이 아닐 경우 그 값을 가져오지만, nil일 경우 런타임 오류가 발생함 |
강제 언래핑 실행 | print(x!.count) | x가 nil이 아닐 경우 count를 출력하지만, nil이면 프로그램이 중단됨 |
var x: String? = "Hello" // 물음표로 옵셔널 타입으로 선언
// 옵셔널 체이닝
if let length = x?.count { // x가 nil이 아닐 경우에만 length에 값 할당
print("Length: \(length)")
} else {
print("x is nil")
}
// 강제 언래핑
let y = x! // x의 값을 강제 언래핑 (nil일 경우 런타임 오류 발생)
print("Value: \(y)")
// 강제 언래핑 실행
print("Count: \(x!.count)") // x의 count를 출력 (nil일 경우 런타임 오류 발생)
int? / int! 차이점과 공통점
타입 | 설명 | 사용 예시 |
int? | 옵셔널: nil을 가질 수 있는 정수형 변수. 안전하게 값을 다룰 수 있으며, 값을 사용하기 전에 반드시 체크해야 합니다. |
var x: Int? = nil <br> if let value = x { ... } |
int! | 강제 언래핑 옵셔널: 기본적으로 nil을 허용하지만, 사용 시 강제로 값을 가져오는 타입. nil일 경우 런타임 오류가 발생합니다. |
var x: Int! = 10 <br> let value = x (x가 nil일 경우 오류 발생) |
var x: Int? = nil // x는 nil이 될 수 있는 옵셔널 정수
if let value = x { // 안전하게 값을 체크
print("Value: \(value)")
} else {
print("x is nil")
}
var y: Int! = 10 // y는 강제 언래핑 옵셔널
print("Value: \(y)") // y는 nil이 아닐 경우 정상 출력
// print(y!) // 강제 언래핑 사용 (nil일 경우 오류 발생)
swift에서 ?와 !의 차이
1. ? (옵셔널)
정의: 변수나 상수가 nil이 될 수 있음을 나타냅니다. 값이 존재하지 않을 수도 있음을 나타냅니다.
var optionalString: String? = "Hello" // 옵셔널 문자열
print(optionalString) // Optional("Hello")
if let unwrappedString = optionalString { // 옵셔널 바인딩
print(unwrappedString) // "Hello" 출력
} else {
print("optionalString is nil")
}
2. ! (강제 언래핑 옵셔널)
정의: 옵셔널 변수를 강제로 언래핑하여 값을 가져옵니다. 만약 값이 nil이라면 런타임 오류가 발생합니다.
var forcedString: String! = "World" // 강제 언래핑 옵셔널
print(forcedString) // "World" 출력
let unwrappedString = forcedString! // 강제 언래핑
print(unwrappedString) // "World" 출력
// 강제 언래핑 시도 (nil일 경우 오류 발생)
forcedString = nil
// print(forcedString!) // 이 줄은 런타임 오류를 발생시킵니다.
Optional Chaining 예
class Person { // Person이라는 클래스를 정의
var name: String // 이름을 저장할 String 타입 변수
var age: Int // 나이를 저장할 Int 타입 변수
// 초기화 메서드
init(name: String, age: Int) { // 클래스 초기화 시 이름과 나이를 매개변수로 받음
self.name = name // 매개변수 name을 클래스 변수 name에 할당
self.age = age // 매개변수 age를 클래스 변수 age에 할당
}
}
let kim: Person = Person(name: "Kim", age: 24) // Person 클래스의 인스턴스를 생성하여 kim에 할당
print(kim.age) // kim의 나이를 출력 (24)
let han: Person? = Person(name: "Han", age: 20) // 옵셔널 타입으로 Person 인스턴스를 생성하여 han에 할당
print(han.age) // 오류 발생: han은 옵셔널이므로 직접 접근할 수 없음
// print(han!.age) // 강제 언래핑을 사용하여 han의 age를 출력 (han이 nil일 경우 오류 발생)
print(han?.age) // 옵셔널 체이닝을 사용하여 han의 age를 안전하게 출력 (han이 nil일 경우 nil 반환)
print((han?.age)!) // 강제 언래핑: han이 nil이 아닐 경우 age를 출력 (nil일 경우 오류 발생)
if let hanAge = han?.age { // 옵셔널 바인딩: han의 age가 nil이 아닐 경우 hanAge에 할당
print(hanAge) // hanAge를 출력 (20)
} else {
print("nil") // han이 nil이거나 age가 nil일 경우 "nil" 출력
}
throwing function
swift에서 throws 키워드가 있는 메소드 사용하는 방법
1. throws 메소드 정의하기
enum DivisionError: Error {
case divideByZero // 0으로 나누는 경우
}
func divide(_ a: Int, _ b: Int) throws -> Int {
if b == 0 {
throw DivisionError.divideByZero // 오류 발생
}
return a / b // 정상적으로 나눈 결과 반환
}
2. try 키워드로 메소드 호출하기
do {
let result = try divide(10, 2) // 정상 호출
print("Result: \(result)") // "Result: 5" 출력
} catch {
print("An error occurred: \(error)") // 오류가 발생할 경우 처리
}
3. 오류 처리하기
do {
let result = try divide(10, 0) // 오류 발생
print("Result: \(result)") // 이 줄은 실행되지 않음
} catch DivisionError.divideByZero {
print("Error: Cannot divide by zero!") // 특정 오류 처리
} catch {
print("An unexpected error occurred: \(error)") // 다른 오류 처리
}
swift에서 throwing 함수
1. 나누기 함수
enum DivisionError: Error {
case divideByZero
}
func divide(_ a: Int, _ b: Int) throws -> Int {
if b == 0 {
throw DivisionError.divideByZero
}
return a / b
}
2. 문자열 정수 변환
func stringToInt(_ str: String) throws -> Int {
guard let number = Int(str) else {
throw NSError(domain: "InvalidInput", code: 1, userInfo: nil)
}
return number
}
3. 배열 인덱스 접근
func elementAtIndex<T>(_ array: [T], index: Int) throws -> T {
guard index >= 0 && index < array.count else {
throw NSError(domain: "IndexOutOfBounds", code: 2, userInfo: nil)
}
return array[index]
}
4. 파일 읽기
func readFile(atPath path: String) throws -> String {
let content = try String(contentsOfFile: path)
return content
}
5. JSON 파싱
func parseJSON(_ data: Data) throws -> [String: Any] {
let json = try JSONSerialization.jsonObject(with: data, options: [])
guard let dictionary = json as? [String: Any] else {
throw NSError(domain: "InvalidJSON", code: 3, userInfo: nil)
}
return dictionary
}
6. 사용자 인증
enum AuthError: Error {
case invalidCredentials
}
func authenticate(username: String, password: String) throws {
guard username == "admin" && password == "password" else {
throw AuthError.invalidCredentials
}
}
7. 날짜 변환
func dateFromString(_ dateString: String) throws -> Date {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
guard let date = formatter.date(from: dateString) else {
throw NSError(domain: "InvalidDateFormat", code: 4, userInfo: nil)
}
return date
}
8. URL 검증
func validateURL(_ urlString: String) throws {
guard let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) else {
throw NSError(domain: "InvalidURL", code: 5, userInfo: nil)
}
}
9. 비밀번호 검증
func validatePassword(_ password: String) throws {
guard password.count >= 8 else {
throw NSError(domain: "WeakPassword", code: 6, userInfo: nil)
}
}
10. 숫자 범위 체크
func checkRange(_ number: Int) throws {
guard number >= 1 && number <= 100 else {
throw NSError(domain: "OutOfRange", code: 7, userInfo: nil)
}
}
프로그래밍 언어에서 (), [], {}, <> 용도
기호 | 용도 | 설명 예시 |
() | 함수 호출 및 매개변수: 함수의 매개변수를 전달하거나, 수학적 표현에서 연산자의 우선 순위를 지정할 때 사용. |
func add(a: Int, b: Int) -> Int { return a + b } <br> let sum = add(3, 5) |
[] | 배열 및 리스트: 배열 또는 리스트와 같은 순차적 데이터 구조를 정의하고 접근할 때 사용. |
let numbers = [1, 2, 3] <br> print(numbers[0]) |
{} | 블록 및 객체 리터럴: 코드 블록이나 객체, 클래스, 딕셔너리 등을 정의할 때 사용. |
if condition { print("True") } <br> let dict = ["key": "value"] |
<> | 제네릭 및 타입 매개변수: 제네릭 프로그래밍에서 타입 매개변수를 정의할 때 사용. |
func printValue<T>(_ value: T) { print(value) } <br> printValue("Hello") |
일반 class vs. generic class
class Box<T> { // 제네릭 클래스를 정의, T는 타입 매개변수
var item: T // 제네릭 타입 T를 사용하는 속성 item을 선언
init(item: T) { // 초기화 메서드, 매개변수로 제네릭 타입 T를 받음
self.item = item // 매개변수 item을 클래스 속성 item에 할당
}
func getItem() -> T { // 제네릭 타입 T를 반환하는 메서드 정의
return item // item을 반환
}
} // 일반 클래스 정의 종료
let intBox = Box<Int>(item: 12) // Box<Int> 타입의 인스턴스를 생성하고, item에 12 할당
// Box<Int>(item: 123)처럼 타입을 명시적으로 지정할 수 있지만,
print(intBox.getItem()) // intBox의 getItem() 메서드를 호출하여 12 출력
let stringBox = Box(item: "Hello") // Box<String> 타입의 인스턴스를 생성, 타입 추론으로 <String> 생략
print(stringBox.getItem()) // stringBox의 getItem() 메서드를 호출하여 "Hello" 출력
swift에서 generic 구조체
// 제네릭 구조체 정의
struct Pair<T, U> {
var first: T // 첫 번째 요소, 타입 T
var second: U // 두 번째 요소, 타입 U
// 초기화 메서드
init(first: T, second: U) {
self.first = first // 첫 번째 요소 초기화
self.second = second // 두 번째 요소 초기화
}
// 메서드: 두 요소를 튜플로 반환
func getValues() -> (T, U) {
return (first, second) // 두 요소를 튜플로 반환
}
}
// 사용 예시
let intStringPair = Pair(first: 1, second: "One") // Int와 String 타입의 Pair 생성
print(intStringPair.getValues()) // (1, "One") 출력
let doubleBoolPair = Pair(first: 3.14, second: true) // Double과 Bool 타입의 Pair 생성
print(doubleBoolPair.getValues()) // (3.14, true) 출력
프로그래밍언어에서 collection Type 예를 들어 설명해줘
swift의 Array도 generic 구조체
빈 배열(empty array) 주의 사항
first와 last 프로퍼티
첫번째와 마지막 데이터 ? 값 가져오는 방법 > optional(1), optional(4) 출력
첨자(subscript)로 항목 접근
var num = [1, 2, 3, 4] // 배열 num을 정의
print(num[0], num[3]) // 1 4 출력
// 결과: 1과 4를 공백으로 구분하여 출력
print(num.first!) // 1 출력
// 결과: 배열의 첫 번째 요소인 1을 출력
for i in 0...num.count-1 { // 0부터 num.count-1까지 반복
print(num[i]) // 각 요소를 출력
}
// 결과:
// 1
// 2
// 3
// 4
// 각 요소가 개별적으로 출력됨
print(num[1...2]) // [2, 3] 출력
// 결과: 인덱스 1부터 2까지의 요소인 2와 3을 포함하는 배열을 출력
num[0...2] = [10, 20, 30] // 인덱스 0부터 2까지의 요소를 10, 20, 30으로 변경
print(num) // [10, 20, 30, 4] 출력
// 결과: 배열의 첫 세 요소가 10, 20, 30으로 변경되고, 마지막 요소는 그대로인 배열을 출력
Array 요소의 최댓값 최솟값 :max(), min()
var num = [1, 2, 3, 10, 20] // 배열 num을 정의
print(num) // [1, 2, 3, 10, 20] 출력
// 결과: 정의된 배열을 그대로 출력
print(num.min()) // Optional(1) 출력
// 결과: 배열의 최소값인 1을 Optional로 감싸서 출력
print(num.max()) // Optional(20) 출력
// 결과: 배열의 최대값인 20을 Optional로 감싸서 출력
print(num.min()!) // 1 출력
// 결과: Optional에서 값을 추출하여 1을 출력
print(num.max()!) // 20 출력
// 결과: Optional에서 값을 추출하여 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(num) //[1, 5, 4, 3, 2]
print(num.sorted(by:>)) //내림차순 정렬 결과를 리턴하고, 원본은 그대로
//[5, 4, 3, 2, 1]
print(num)//[1, 5, 4, 3, 2]
4주차
프로그래밍언어에서 access modifier
1. Public
public class PublicClass {
public var property: Int = 0 // 외부에서 접근 가능
}
2. Internal
class InternalClass {
var property: Int = 0 // 동일 모듈 내에서만 접근 가능
}
3. Fileprivate
class FilePrivateClass {
fileprivate var property: Int = 0 // 같은 파일 내에서만 접근 가능
}
4. Private
class PrivateClass {
private var property: Int = 0 // 같은 클래스 내에서만 접근 가능
func accessProperty() -> Int {
return property // 클래스 내부에서만 접근 가능
}
}
swift 에서 access modifier
접근제어자 | 모듈 외부 접근 | 상속 및 재정의 가능 | 사용 범위 |
open | 가능 | 가능 | 모듈 외부에서 접근 및 상속/재정의 가능 |
public | 가능 | 불가능 | 모듈 외부에서 접근 가능 |
internal | 불가능 | 불가능 | 같은 모듈 내에서만 접근 가능 |
fileprivate | 불가능 | 불가능 | 같은 파일 내에서만 접근 가능 |
private | 불가능 | 불가능 | 같은 타입 및 스코프 내에서만 접근 가능 |
package | 제한적 | 제한적 | 같은 패키지 내에서만 접근 가능 (Swift 5.9 추가) |
swift의 access control
public class MyClass{
// 모듈의 모든 소스 파일 내에서 접근+정의한 모듈을 가져오는 다른 모듈의 소스파일에서도 접근 가능
fileprivate var name : String = "Kim"
//현재 소스 파일 내에서만 사용 가능
private func play() {}
//현재 블럭 내에서만 사용 가능
func display(){} //internal은 디폴트 속성으로 생략됨
// internal 접근은 해당 모듈의 모든 소스 파일 내에서 사용
}
접근 제어
접근 제어 예제
// Public Class
public class Library {
private var books: [Book] = [] // Private: 외부에서 접근 불가
public var name: String // Public: 외부에서 접근 가능
public init(name: String) {
self.name = name
}
// Public 메서드: 책 추가
public func addBook(_ book: Book) {
books.append(book)
}
// Internal 메서드: 모든 책 목록 출력 (기본값)
func listBooks() {
for book in books {
print(book.title)
}
}
}
// Public Class
public class Book {
public var title: String // Public: 외부에서 접근 가능
private var author: String // Private: 외부에서 접근 불가
public init(title: String, author: String) {
self.title = title
self.author = author
}
// Public 메서드: 저자 이름 반환
public func getAuthor() -> String {
return author // 같은 클래스 내에서만 접근 가능
}
}
// Internal Class
class Member {
var name: String // Internal: 같은 모듈 내에서만 접근 가능
private var borrowedBooks: [Book] = [] // Private: 외부에서 접근 불가
init(name: String) {
self.name = name
}
// Internal 메서드: 책 대출
func borrowBook(_ book: Book, from library: Library) {
library.addBook(book) // Library의 public 메서드 사용
borrowedBooks.append(book)
}
}
extension
iOS 강의 자료 참고했습니다
'iOS프로그래밍 실무' 카테고리의 다른 글
[iOS프로그래밍 실무] 11주차 (0) | 2025.05.15 |
---|---|
[iOS프로그래밍 실무] 10주차 (1) | 2025.05.08 |
[iOS프로그래밍 실무] 9주차 (1) | 2025.05.07 |
[iOS프로그래밍 실무] 7주차 (0) | 2025.04.17 |
[iOS프로그래밍 실무] 5주차 (0) | 2025.04.03 |
[iOS프로그래밍 실무] 4주차 (0) | 2025.03.27 |
[iOS프로그래밍 실무] 3주차 (0) | 2025.03.20 |
[iOS프로그래밍 실무] 2주차 (0) | 2025.03.19 |