swift에서 ?와 !가 선언문과 실행문에서 쓰일 때 차이
- ? (선언문): 옵셔널 타입 선언. 값이 없을 수도 있음을 명시합니다.
- ! (선언문): 암시적 언래핑 옵셔널 타입 선언. 자동으로 언래핑되지만, nil일 경우 런타임 에러 위험이 있습니다.
- ? (실행문 - Optional Chaining): 옵셔널 값이 nil이 아닐 때만 코드 실행. 안전하게 옵셔널 값에 접근할 수 있습니다.
- ! (실행문 - Forced Unwrapping): 옵셔널 값을 강제로 추출. nil일 경우 런타임 에러 발생. 최대한 사용을 피하고, 꼭 필요한 경우에만 사용해야 합니다.
옵셔널 체이닝을 쓰는 이유
- 옵셔널 타입으로 정의된 값이 프로퍼티나 메서드를 가지고 있을 때, 다중 if를 쓰지 않고 간결하게 코드를 작성하기 위해
- 옵셔널 타입의 데이터는 연산이 불가능
보통 Swift에서 throws 키워드를 사용하는 경우
- Swift에서 throws 키워드가 있는 메서드는 예외(오류)를 던질 수 있는 메서드입니다. 즉, 해당 메서드를 호출할 때 예외가 발생할 가능성을 염두에 두고, 적절한 방법으로 예외를 처리해야 합니다. throws 메서드를 사용하는 방법은 크게 두 가지입니다.
1. do-catch 블록 사용: 가장 일반적인 방법으로, do 블록 안에서 throws 메서드를 호출하고, catch 블록에서 발생할 수 있는 예외를 처리합니다.
enum MyError: Error {
case invalidInput
case networkError
}
func someThrowingFunction(input: Int) throws -> String {
if input < 0 {
throw MyError.invalidInput // 예외 던지기
}
// ... (정상적인 동작)
return "결과 문자열"
}
do {
let result = try someThrowingFunction(input: 10) // throws 메서드 호출
print("결과: \(result)") // 예외가 발생하지 않았을 때 실행
} catch MyError.invalidInput {
print("입력 값이 유효하지 않습니다.") // MyError.invalidInput 예외 처리
} catch MyError.networkError {
print("네트워크 오류가 발생했습니다.") // MyError.networkError 예외 처리
} catch {
print("알 수 없는 오류가 발생했습니다.") // 다른 모든 예외 처리
}
- try 키워드: throws 메서드를 호출할 때 반드시 try 키워드를 사용해야 합니다. try 키워드는 해당 메서드 호출이 예외를 던질 수 있다는 것을 명시적으로 나타냅니다.
- catch 블록: catch 블록은 do 블록에서 발생한 예외를 처리합니다. 각 catch 블록은 특정 예외 타입을 지정하여 해당 타입의 예외만 처리할 수 있습니다.
- catch 블록 순서: catch 블록은 특정 타입의 예외부터 일반적인 예외 순으로 작성하는 것이 좋습니다.
- catch { ... } (일반적인 catch 블록): 특정 예외 타입을 지정하지 않은 catch 블록은 모든 예외를 처리합니다. 마지막에 위치하는 것이 일반적입니다.
2. try? 또는 try! 사용:
- try? (Optional Try): 예외가 발생하면 nil을 반환하고, 예외가 발생하지 않으면 정상적인 반환 값을 옵셔널 타입으로 반환합니다. 예외 처리보다는 간단하게 오류를 무시하고 싶을 때 사용합니다.
let result = try? someThrowingFunction(input: -5) // 예외 발생 시 result는 nil
if let unwrappedResult = result {
print("결과: \(unwrappedResult)")
} else {
print("오류 발생")
}
// 예외가 발생하지 않을 것이라고 확신하는 경우 (매우 드물고 신중하게 사용해야 함)
// let result = try! someThrowingFunction(input: 10)
// print("결과: \(result)")
- try! (Forced-Try): 예외가 발생하지 않을 것이라고 확신할 때 사용합니다. 만약 예외가 발생하면 런타임 에러가 발생하여 프로그램이 강제 종료됩니다. 매우 위험하며, 사용을 최대한 자제해야 합니다.
언제 어떤 방법을 사용해야 할까요?
- do-catch 블록: 예외를 명시적으로 처리하고, 각 예외 타입에 따라 다른 동작을 수행해야 하는 경우에 사용합니다.
- try?: 예외를 무시하고, 결과가 있는지 없는지만 확인하고 싶을 때 사용합니다.
- try!: 예외가 발생하지 않을 것이라고 확신하는 경우에만 매우 신중하게 사용합니다. 일반적으로 사용을 피하는 것이 좋습니다.
이렇게 throwing function을 예외처리하는 것을 error handing이라고 한다.
프로그래밍 언어에서 소괄호, 중괄호, 대괄호, 꺽쇠괄호를 사용하는 방법
괄호 종류 | 이름 | 주요 용도 | 예시 |
() | 소괄호 | 1. 함수 호출/정의 시 매개변수 지정 2. 연산 우선순위 지정 3. 조건식 묶음 |
1. print("Hello") (Python) 2. (2 + 3) * 4 3. if (x > 0) (C) |
{} | 중괄호 | 1. 코드 블록 정의 (함수, 조건문, 반복문 등) 2. 객체/구조체 초기화 |
1. void func() { int x = 5; } (C++) 2. int arr[] = {1, 2, 3}; |
[] | 대괄호 | 1. 배열 인덱스 접근 2. 리스트/배열 요소 접근 3. 일부 언어에서 속성 지정 |
1. arr[0] = 10; (C) 2. list[1] (Python) 3. [Key] (C# 속성) |
<> | 꺽쇠괄호 | 1. 템플릿/제네릭 타입 정의 2. HTML/XML 태그 3. 비교 연산자 (별도 용도) |
1. vector<int> (C++) 2. <div> (HTML) 3. x < y (비교) |
Generic Class : 실행 시 자료형을 결정하게 함
- 아래 코드를 오류가 나지 않도록 수정하기
func myPrint(a: Int, b: Int) {
print(b,a)
}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5)
- 수정한 코드
func myPrint <T>(a: T, b: T) {
print(b,a)
}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5)
myPrint(a: "Hi", b: "1234")
Generic Class를 사용하는 또다른 코드 예시
Swift에서 빈 배열을 만드는 방법 3가지 (시험문제 출제 가능성)
- var x : [Int] = [] //빈 배열
- var y = [Int]()
- var z : Array<Int> = []
빈 배열(empty array) 주의 사항
let 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)
- 가변형(mutable)
- var animal = ["dog", "cat","cow"]
- 불변형 (immutable)
- 초기화 후 변경 불가
- let animal1 = ["dog", "cat","cow"]
아래 사진에서 P 는 property, M은 Method
배열에서 첫번째 데이터와 마지막 데이터를 가져오는 방법 : 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)로 항목 접근
Array : 추가 /제거
Array는 구조체이므로 값 타입
var num = [1,2,3]
var x = num //x는 num의 복사본, 별개의 배열
num[0]=100
print(num)
print(x)
Array 요소의 최댓값 최솟값 :max(), min()
Array 요소의 정렬
sort와 sorted
- sort : 정렬하면서 원본도 변경
- sorted : 정렬하지만 원본은 그대로
Java, C#, Swift 언어에서 사용되는 액세스 제어(Access Control)를 정리한 표
언어 | 주요 엑세스 제어 키워드 | 설명 |
Java | public, protected, private, (default/package) | public: 모든 곳에서 접근 가능 protected: 동일 패키지 또는 상속받은 클래스에서 접근 가능 private: 클래스 내부에서만 접근 가능 (default): 패키지 내에서만 접근 가능 |
C# | public, protected, private, internal, protected internal | public: 모든 곳에서 접근 가능 protected: 상속받은 클래스에서 접근 가능 private: 클래스 내부에서만 접근 가능 internal: 동일 어셈블리 내에서 접근 가능 protected internal: 동일 어셈블리 또는 상속받은 클래스에서 접근 가능 |
Swift | open, public, internal, fileprivate, private | open: 모듈 외부에서도 접근 및 상속 가능 public: 모듈 외부에서 접근 가능(상속 불가) internal: 동일 모듈 내에서 접근 가능(기본값) fileprivate: 동일 파일 내에서만 접근 가능 private: 선언된 범위(클래스/구조체 등) 내에서만 접근 가능 |
swift의 access control
swift의 access control을 잘 이해할 수 있는 예제
class Car {
// 누구나 접근하고 수정 가능 (open)
open var color: String = "Red"
// 외부에서 접근 가능, 수정 불가 (public)
public var model: String = "Sedan"
// 같은 모듈에서만 접근 가능 (internal)
internal var speed: Int = 100
// 같은 파일에서만 접근 가능 (fileprivate)
fileprivate var owner: String = "Alice"
// 클래스 내부에서만 접근 가능 (private)
private var fuel: Int = 50
// 외부에서 호출하고 오버라이드 가능
open func honk() {
print("Beep!")
}
// 클래스 내부에서만 사용
private func useFuel() {
fuel -= 10
print("Fuel left: \(fuel)")
}
}
// 같은 파일에 있는 다른 클래스
class Driver {
let car = Car()
func drive() {
// fileprivate 속성에 접근 가능 (같은 파일)
print("Owner: \(car.owner)")
}
}
// 다른 모듈에서 사용
class Guest {
let car = Car()
func checkCar() {
// open 속성 접근 가능
print("Color: \(car.color)")
car.honk()
// public 속성 접근 가능
print("Model: \(car.model)")
// internal, fileprivate, private 속성은 접근 불가
// car.speed // 오류
// car.owner // 오류
// car.fuel // 오류
}
}
실행 예시
let guest = Guest()
guest.checkCar()
// 출력:
// Color: Red
// Beep!
// Model: Sedan
let driver = Driver()
driver.drive()
// 출력:
// Owner: Alice
extension
- 클래스, 구조체, 열거형, protocol에 새로운 기능을 추가
- extension은 기존 클래스에 메서드, 생성자(initializer), 계산 프로퍼티 등의 기능을 추가하기 위하여 사용
- Swift built-in 클래스와 iOS 프레임워크에 내장된 클래스에 기능을 추가할 때 extension을 이용하면 매우 효과적임
- 클래스(구조체, 열거형, protocol)는 다음과 같은 형태로 extension 함
extension은 protocol 채택할 때 소스를 깔끔하게 분할하기 위해 많이 사용
참고자료 : 한성현 교수님 수업자료
'iOS프로그래밍실무 (3학년 1학기)' 카테고리의 다른 글
250403 iOS프로그래밍실무 5주차 (0) | 2025.04.03 |
---|---|
250327 iOS프로그래밍실무 4주차 (0) | 2025.04.02 |
250320 iOS프로그래밍실무 3주차 (0) | 2025.03.20 |
250313 iOS프로그래밍실무 2주차 (0) | 2025.03.19 |
250306 iOS프로그래밍실무 1주차 (1) | 2025.03.06 |