복습
저번 주 소스
// ViewController.swift 파일입니다. FoodCMU 애플리케이션의 일부입니다.
import UIKit // UIKit 프레임워크를 가져옵니다. iOS UI 구성 요소를 사용하기 위해 필요합니다.
// 이미지 파일 이름, 영화 제목, 순위, 개봉 날짜를 각각 배열로 정의합니다.
let image = ["1.png","2.png","3.png","4.png","5.png"]
let name = ["쿵푸팬더4","파묘","남은 인생 10년","댓글부대","오멘: 저주의 시작"]
let rank = [1,2,3,4,5]
let open = ["2024-04-10", "2024-02-22", "2023-05-24","2024-03-27", "2024-04-03"]
// ViewController 클래스를 정의합니다. UIViewController를 상속받고, UITableViewDelegate 및 UITableViewDataSource 프로토콜을 채택합니다.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView! // 스토리보드에서 연결한 테이블 뷰의 아웃렛입니다.
// 테이블 뷰의 섹션 개수를 정의합니다. 여기서는 5개의 섹션이 있다고 정의하였습니다.
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
// 각 섹션별 행의 개수를 정의합니다. 여기서는 각 섹션마다 5개의 행이 있다고 정의하였습니다.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
// 테이블 뷰 셀을 구성하는 메소드입니다.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// dequeueReusableCell 메소드를 사용하여 재사용 가능한 셀을 가져오거나 새로 생성합니다. "myCell" 식별자를 가진 셀을 가져옵니다.
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
// 셀 내 레이블에 영화 제목을, 개봉 날짜를 표시합니다.
cell.myLabel.text = name[indexPath.row]
cell.myOpen.text = open[indexPath.row]
return cell // 구성된 셀을 반환합니다.
}
// 테이블 뷰의 특정 행을 선택했을 때 실행되는 메소드입니다.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.description) // 선택된 행의 인덱스 경로를 콘솔에 출력합니다.
}
// 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메소드입니다.
override func viewDidLoad() {
super.viewDidLoad()
// 테이블 뷰의 delegate와 dataSource를 현재 뷰 컨트롤러로 설정합니다.
table.delegate = self
table.dataSource = self
}
}
이 코드는 iOS에서 테이블 뷰를 사용하여 영화 목록을 표시하는 간단한 예제입니다. UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택하여 테이블 뷰의 동작을 정의합니다. 각 셀에는 영화의 제목과 개봉 날짜가 표시되며, 행을 선택하면 해당 행의 인덱스가 콘솔에 출력됩니다. 이 예제는 스토리보드에서 UITableView와 연결된 아웃렛과 재사용 가능한 셀을 활용하는 방법을 보여줍니다.
//MyTableViewCell.swift
import UIKit
// 사용자 정의 UITableViewCell을 정의하는 클래스입니다.
class MyTableViewCell: UITableViewCell {
// 영화의 개봉일을 표시하는 레이블에 대한 아웃렛입니다.
@IBOutlet weak var myOpen: UILabel!
// 영화의 제목을 표시하는 레이블에 대한 아웃렛입니다.
@IBOutlet weak var myLabel: UILabel!
// 셀이 메모리에 로드될 때 호출되는 메서드입니다. 초기 설정 코드를 여기에 작성합니다.
override func awakeFromNib() {
super.awakeFromNib()
// 초기화 코드
}
// 셀의 선택 상태가 변경될 때 호출되는 메서드입니다. 셀이 선택되거나 선택 해제될 때의 사용자 인터페이스를 구성할 수 있습니다.
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// 선택된 상태의 뷰를 구성하는 코드입니다.
}
}
이 Swift 코드는 iOS 앱인 FoodCMU에 사용되는 사용자 정의 UITableViewCell의 클래스를 정의합니다. 이 클래스는 영화의 개봉일과 제목을 표시하기 위해 두 개의 UILabel을 IBOutlet으로 연결합니다.
이 클래스는 테이블 뷰 셀의 두 가지 주요 상태인 초기화(awakeFromNib)와 선택(setSelected)을 관리합니다. awakeFromNib 메서드는 셀이 인터페이스 빌더로부터 로드되고 초기화될 때 호출되며, 이곳에서 필요한 초기 설정을 수행할 수 있습니다. setSelected 메서드는 셀이 사용자에 의해 선택되거나 선택이 해제될 때 호출되며, 이 메서드에서는 셀의 선택 상태에 따른 UI 변경을 처리할 수 있습니다.
IBOutlet 변수 myOpen과 myLabel은 스토리보드에서 이 클래스의 인스턴스에 연결되어야 하며, 각각 영화의 개봉일과 제목을 표시하는 데 사용됩니다.
Optional unwrapping
forced unrwrapping
// 옵셔널 정수형 변수 x를 선언합니다. 옵셔널은 값이 있을 수도 있고, nil일 수도 있는 타입입니다.
var x : Int?
// 일반 정수형 변수 y를 선언하고 0으로 초기화합니다.
var y : Int = 0
// x에 10을 할당합니다. 이제 x는 nil이 아니라 10이라는 값을 갖게 됩니다.
x = 10 // 이 줄을 주석 처리하면 x는 초기값인 nil을 유지합니다.
// x를 출력합니다. x는 옵셔널이므로 Optional(10)이라고 출력됩니다.
print(x) // Optional(10)
// x의 값을 강제 언래핑하여 출력합니다. 강제 언래핑은 옵셔널의 값을 강제로 추출하는 것인데, 값이 nil인 경우 프로그램이 크래시될 수 있습니다.
// 여기서는 x가 10이므로 10이 출력됩니다.
print(x!) // forced unwrapping해서 10이 나옴
// y의 값을 출력합니다. y는 0으로 초기화되었으므로 0이 출력됩니다.
print(y)
// x = x+2 // 이 코드는 실행되지 않습니다. 왜냐하면 x는 옵셔널이므로 직접적인 연산이 불가능합니다. 옵셔널 바인딩이나 강제 언래핑을 통해 값을 추출한 후 연산해야 합니다.
// y = x // 이 코드도 실행되지 않습니다. x는 옵셔널 Int 타입이고, y는 일반 Int 타입이므로, 타입이 일치하지 않습니다. x의 값을 안전하게 추출한 후에 y에 할당해야 합니다.
- 선언문에서 자료형 다음에 ?를 쓰면 Optional 변수를 선언하는 것
- Optional 변수는 값을 초기화하지 않으면 nil이 된다.
- nil은 값이 없다는 의미이다.
- Optional 값은 값을 넣어도 Optional()로 감싸져있다. (wrapping)
- 실행문에서 Optinal 변수 뒤에 !를 쓰면 Optional을 unwrapping한다 (강제로 풀기 때문에 forced unwrapping)
- 포장되더 있던 값을 풀어주는 동작은 Unwrapping이라고 한다.
nil을 푸는 경우 crash가 발생하기 때문에 다음과 같은 소스를 사용하면 좋다.
var x : Int? // 'Int?'는 'x'가 정수(Integer)를 담을 수도 있고, nil을 담을 수도 있음을 의미합니다. 즉, x는 옵셔널 정수입니다.
x = 10 // x에 10을 할당합니다. 이제 x는 nil이 아니라 값 10을 가지고 있습니다.
if x != nil { // x가 nil이 아닌지 확인합니다. 즉, x에 값이 할당되어 있는지 확인합니다.
print(x!) // x가 nil이 아니라면, 강제 언래핑(`!`)을 사용하여 x의 값을 출력합니다. 여기서 x는 10이므로 "Optional(10)"이 아닌 "10"이 출력됩니다.
}
else {
print("nil") // x가 nil인 경우, "nil"을 출력합니다.
}
var x1 : Int? // 'x1'도 옵셔널 정수로 선언되지만, 여기서는 어떤 값도 할당되지 않습니다. 즉, 초기값은 nil입니다.
if x1 != nil { // x1이 nil이 아닌지 확인합니다. 이 경우 x1은 nil이므로, 이 조건은 거짓이 됩니다.
print(x1!) // x1이 nil이 아니라면, x1의 값을 강제 언래핑하여 출력합니다. 하지만 이 줄은 실행되지 않습니다.
}
else {
print("nil") // x1이 nil인 경우, 즉 값이 없는 경우 "nil"을 출력합니다. 여기서 x1은 nil이므로 "nil"이 출력됩니다.
}
이 코드는 옵셔널 변수가 nil인지 아닌지를 확인하고, nil이 아닌 경우에만 값을 강제 언래핑하여 출력하는 방법을 보여줍니다. 강제 언래핑(!)은 옵셔널에 값이 확실히 있을 때만 사용해야 합니다. 값이 없는 옵셔널을 강제 언래핑하려고 하면 런타임 오류가 발생하므로 주의해야 합니다. 안전한 방법으로는 옵셔널 바인딩(if let 또는 guard let)을 사용하는 것이 좋습니다.
Optional binding
var x : Int? // Int 타입의 옵셔널 변수 x를 선언합니다. 이 변수는 정수값을 가질 수도 있고 nil을 가질 수도 있습니다.
x = 10 // 옵셔널 변수 x에 정수 10을 할당합니다. 이 시점에서 x는 nil이 아닌 값(10)을 가지고 있습니다.
if let x { //short form of if-let to the Optional Binding (Swift 5.7이후부터 가능)
// 옵셔널 바인딩을 사용하여 x가 nil이 아닌 값을 가지고 있는지 확인합니다.
// x가 nil이 아니라면, x의 값을 언래핑해서 새로운 상수 x에 대입하고 if문 내의 코드를 실행합니다.
// 여기서 주의할 점은, if let 구문 내부에서 사용되는 x는 새로운 상수로, 옵셔널이 아닌 일반 Int 타입의 값입니다.
print(x) // x의 값(10)을 출력합니다. 여기서 x는 옵셔널이 아니기 때문에 강제 언래핑을 할 필요가 없습니다.
}
else {
print("nil") // x가 nil인 경우 "nil"을 출력합니다. 하지만 이 예제에서는 x에 값이 할당되어 있으므로 이 라인은 실행되지 않습니다.
}
이 코드는 옵셔널 바인딩을 사용하여 옵셔널이 nil인지 아닌지를 안전하게 확인하고, nil이 아닌 경우 그 값을 사용하는 방법을 보여줍니다.
var x : String? = "Hi" // x는 옵셔널 String 타입 변수로, 초기값은 "Hi"입니다.
// = "Hi" 지우고도 실습
print(x, x!) // x와 x를 강제 언래핑한 값을 출력합니다.
if let a = x { // 옵셔널 바인딩을 사용하여 x가 nil이 아니면 a에 값을 할당하고 출력합니다.
print(a)
}
let c = x ?? "" // x가 nil이면 빈 문자열("")을, 아니면 x의 값을 c에 할당합니다.
print(c)
let b = x!.count // x를 강제 언래핑하고, 그 문자열의 길이를 b에 할당합니다.
print(type(of:b),b) // b의 타입(Int)과 값을 출력합니다.
let b1 = x?.count // 옵셔널 체이닝을 사용하여 x가 nil이 아니면 그 문자열의 길이를 b1에 할당합니다.
print(type(of:b1),b1, b1!) // b1의 타입(Optional<Int>), 값을 옵셔널로 그리고 강제 언래핑한 값을 출력합니다.
이 Swift 코드는 옵셔널 변수의 다양한 사용 방법을 설명합니다. 여기서 x
는 옵셔널 String
타입으로 선언되어 있으며, 초기값으로 "Hi"
를 가집니다. 그러나 주석에서 언급된 것처럼 = "Hi"
를 제거하면 x
의 값은 nil
이 됩니다.
print(x, x!)
에서,x
를 그대로 출력하면 옵셔널(Optional("Hi")
)로 출력되고,x!
로 강제 언래핑하면"Hi"
가 출력됩니다.x
가nil
일 경우x!
는 런타임 오류를 일으킵니다.if let a = x
구문은x
가nil
이 아닐 경우에만 내부의print(a)
를 실행합니다. 여기서a
는x
의 언래핑된 값입니다.x ?? ""
는x
가nil
이면 빈 문자열을, 아니면x
의 값을 반환합니다.x!.count
는x
를 강제 언래핑하여 그 문자열의 길이를 구합니다. 이때x
가nil
이면 런타임 오류가 발생합니다.x?.count
는 옵셔널 체이닝을 사용하여x
가nil
이 아닐 경우에만 문자열의 길이를 구하고,nil
일 경우b1
은nil
이 됩니다.b1
의 타입은Optional<Int>
입니다.print(type(of:b1),b1, b1!)
에서b1
을 강제 언래핑하여 사용하는 것은x
의 값이nil
일 때 런타임 오류를 일으킬 수 있으므로 주의해야 합니다.
이 코드는 옵셔널의 안전한 사용 방법(옵셔널 바인딩, 옵셔널 체이닝)과 주의해야 할 점(강제 언래핑)을 잘 보여줍니다.
이런 자료를 참고했어요. [1] velog - [iOS] 옵셔널? Optional? (https://velog.io/@maddie/iOS-%EC%98%B5%EC%85%94%EB%84%90-Optional) [2] 티스토리 - Swift) Optional - 커다란 Scuti UY - 티스토리 (https://koggy.tistory.com/18)
뤼튼 사용하러 가기 > https://agent.wrtn.ai/5xb91l
Swift에서 ?와!
Swift에서 ?
와 !
의 사용법을 선언문과 실행문으로 구분하여 아래 표에 정리하였습니다.
구분 | ? 사용법 |
! 사용법 |
---|---|---|
선언문 | var x: Int? 옵셔널 변수 x 를 선언하여 Int 타입의 값을 가지거나 nil 을 가질 수 있음을 나타냄. |
var y: Int! 암시적으로 언래핑된 옵셔널 변수 y 를 선언하여, 사용 시 자동으로 언래핑됨. 단, 변수가 nil 인 상태에서 사용될 경우 런타임 에러 발생. |
실행문 | if let z = x { print(z) } 옵셔널 바인딩을 사용하여 x 의 값이 nil 이 아닐 경우, 그 값을 상수 z 에 대입하고 print(z) 를 실행함.x?.someMethod() 옵셔널 체이닝을 사용하여 x 가 nil 이 아닐 경우에만 someMethod() 를 호출함. |
print(x!) 옵셔널 변수 x 를 강제로 언래핑하여 사용. x 가 nil 일 경우 런타임 에러 발생. |
이 표는 Swift에서 옵셔널(?
)과 강제 언래핑(!
)을 사용하는 기본적인 방법을 설명합니다. 옵셔널은 값이 있을 수도 있고, 없을 수도 있는 변수를 선언할 때 사용됩니다. 반면, 강제 언래핑은 옵셔널이 값을 반드시 가지고 있음을 확신할 때 사용되지만, 사용에 주의가 필요합니다. 옵셔널 바인딩(if let
/guard let
)이나 옵셔널 체이닝(?.
)은 옵셔널을 보다 안전하게 다루는 방법으로 권장됩니다.
guard
// if-let (Optional binding) 예제
// 'multiplyByTen' 함수 정의. 옵셔널 정수 타입의 'value'를 매개변수로 받음.
func multiplyByTen(value: Int?) {
// 'if let' 구문을 사용하여 'value'가 'nil'이 아닌 경우,
// 'value'의 값을 'number'에 할당하고 이를 10배 곱한 값을 출력함.
if let number = value {
print(number * 10)
}
// 'value'가 'nil'인 경우 "nil" 문자열을 출력함.
else {
print ("nil")
}
}
// 'multiplyByTen' 함수를 호출하여 매개변수로 정수 3을 전달함.
// 'value'는 옵셔널 타입이므로 3이 'number'로 바인딩되고, 30이 출력됨.
multiplyByTen(value: 3)
// 'multiplyByTen' 함수를 호출하여 매개변수로 'nil'을 전달함.
// 이 경우 'value'가 'nil'이므로 "nil" 문자열이 출력됨.
multiplyByTen(value: nil)
func multiplyByTen(value: Int?) {
// `guard let`을 사용하여 `value`가 `nil`이 아닌지 확인합니다.
// `value`가 `nil`이면, `else` 블록이 실행되어 함수에서 즉시 반환(return)됩니다.
guard let number = value else { return }
// `value`가 `nil`이 아니라면, `number` 변수에 그 값을 할당하고 10배 곱한 값을 출력합니다.
print(number * 10)
}
- guard let number = value else { return }에서 guard 키워드는 조건이 참일 때 코드의 실행을 계속 진행시키고, 거짓일 경우 else 블록을 실행하여 함수의 실행을 조기에 종료합니다. 이 경우 value가 nil이면 else 블록이 실행되어 함수에서 나옵니다.
gurad~else
guard let
과 if let
은 Swift에서 옵셔널 값을 안전하게 다루기 위해 사용되며, guard let
은 특히 코드의 가독성과 안전성을 높이고, 에러 처리를 명확히 하며, 언랩된 값의 사용 범위를 넓혀줌으로써 함수의 시작 부분에서 입력 값의 유효성을 더 효과적으로 검증할 때 유용합니다.
다만 gurad-let은 함수 안에서 Optional 변수를 풀 때 사용한다.
일반적으로 Optional 변수를 풀 때는 return을 사용할 수 없기 때문에 if-let을 사용해야한다.
guard~else는 보통 nil이 나오면 return을 써서 함수를 조기에 빠져나오는 조기 출구 용도로 사용된다.
failable initializer
self
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){ //designated initializer 모든 프로퍼티(age, weight)를 다 초기화시키는 생성자
self.age = age
self.weight = weight
}
}
var kim : Man = Man(age:10, weight:20.5)
kim.display()
failable initializer
init 다음에 ?가 있으면 실패 가능한 failable initializer이다.
class Man{
var age : Int
var weight : Double
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init?(age: Int, weight : Double){
if age <= 0 || weight <= 0.0 {
return nil
}
else {
self.age = age
}
self.weight = weight
}
}
var kim : Man? = Man(age:1, weight:3.5) //1-1.옵셔널 형으로 선언
if let kim1 = kim { //1-2.옵셔널 바인딩
kim1.display()
}
//2.인스턴스 생성과 동시에 옵셔널 바인딩
if let kim2 = Man(age:2, weight:5.5) {
kim2.display()
}
//3.인스턴스 생성하면서 바로 강제 언래핑
var kim3 : Man = Man(age:3, weight:7.5)!
kim3.display()
//4.옵셔널 인스턴스를 사용시 강제 언래핑
var kim4 : Man? = Man(age:4, weight:10.5)
kim4!.display()
class Man{
var age: Int
var weight: Double
// 나이와 몸무게 정보를 출력하는 메서드
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
// 나이와 몸무게가 양수일 때만 인스턴스를 생성하는 실패 가능한 생성자
init?(age: Int, weight: Double){
if age <= 0 || weight <= 0.0 {
return nil // 나이 또는 몸무게가 유효하지 않으면 nil 반환
} else {
self.age = age
self.weight = weight
}
}
}
// 올바른 값을 가진 인스턴스 생성과 강제 언래핑을 통한 메서드 호출
var kim: Man? = Man(age: 1, weight: 3.5) // 옵셔널 형태의 Man 인스턴스 생성
kim!.display() // 강제 언래핑(!)을 사용하여 display 메서드 호출
// 잘못된 값을 가진 인스턴스 생성과 옵셔널 바인딩을 통한 메서드 호출 시도
var kim1: Man? = Man(age: -1, weight: 3.5) // 나이가 유효하지 않으므로 nil이 반환됨
if let kim1 = kim1 {
kim1.display() // 이 블록은 실행되지 않음. kim1이 nil이기 때문에 조건이 실패함
}
출처
- Smile Han의 iOS 프로그래밍 실무, 한성현(출판 예정), PPT로 제공
- Do it! 스위프트로 아이폰 앱 만들기 입문(개정 7판)(이지스퍼블리싱,송호정, 이범근, 2023.1)
- 핵심만 골라 배우는 SwiftUI 기반의 iOS 프로그래밍(제이펍, 닐 스미스, 2023.12)
- https://www.techotopia.com/index.php/IOS_iPhone_iPad_eBooks
- 스위프트 프로그래밍(Swift 5) 3판(한빛미디어, 야곰, 2019.10)
- 꼼꼼한 재은 씨의 스위프트 기본편 (루비페이퍼, 이재은, 2018.05)
- 꼼꼼한 재은 씨의 스위프트 실전편 (Swift) (루비페이퍼, 이재은, 2018.08)
- 꼼꼼한 재은 씨의 Swift 문법편 (루비페이퍼, 이재은, 2017.12)