[4주차] iOS프로그래밍 실무
- 프로젝트 생성 시 만들어지는
//
// AppDelegate.swift
// ddd
//
// Created by 소프트웨어컴퓨터 on 2025/03/26.
//
import UIKit // UIKit 프레임워크를 임포트하여 UI 관련 기능을 사용 가능하게 함
@main // 프로그램의 시작점을 나타내는 어트리뷰트
class AppDelegate: UIResponder, UIApplicationDelegate { // AppDelegate 클래스 선언, UIResponder와 UIApplicationDelegate 프로토콜을 준수
// 애플리케이션이 시작할 때 호출되는 메서드
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 애플리케이션이 시작된 후 사용자 정의를 위한 오버라이드 포인트
return true // 애플리케이션이 성공적으로 시작되었음을 나타냄
}
// MARK: UISceneSession Lifecycle // UISceneSession 생명 주기 관련 메서드를 그룹화하기 위한 주석
// 새로운 장면 세션이 연결될 때 호출되는 메서드
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// 새로운 장면을 생성하기 위한 구성을 선택하는 메서드
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) // 기본 구성 반환
}
// 사용자가 장면 세션을 폐기할 때 호출되는 메서드
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// 애플리케이션이 실행 중이지 않을 때 세션이 폐기된 경우 이 메서드가 호출됨
// 폐기된 장면에 특정한 리소스를 해제하는 데 사용할 수 있음
}
}
//
// SceneDelegate.swift
// ddd
//
// Created by 소프트웨어컴퓨터 on 2025/03/26.
//
import UIKit // UIKit 프레임워크를 임포트하여 UI 관련 기능을 사용 가능하게 함
class SceneDelegate: UIResponder, UIWindowSceneDelegate { // SceneDelegate 클래스 선언, UIResponder와 UIWindowSceneDelegate 프로토콜을 준수
var window: UIWindow? // UIWindow 인스턴스를 저장할 변수 선언
// 장면이 세션에 연결될 때 호출되는 메서드
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 제공된 UIWindowScene `scene`에 UIWindow `window`를 구성하고 연결하기 위한 선택적 메서드
// 스토리보드를 사용하는 경우 `window` 속성이 자동으로 초기화되고 장면에 연결됨
// 이 델리게이트는 연결된 장면이나 세션이 새로운 것임을 의미하지 않음
guard let _ = (scene as? UIWindowScene) else { return } // scene이 UIWindowScene인지 확인
}
// 장면이 시스템에 의해 해제될 때 호출되는 메서드
func sceneDidDisconnect(_ scene: UIScene) {
// 장면이 백그라운드로 들어가거나 세션이 폐기된 후에 호출됨
// 이 장면과 관련된 리소스를 해제하는 데 사용할 수 있음
// 장면은 나중에 다시 연결될 수 있음
}
// 장면이 비활성 상태에서 활성 상태로 이동할 때 호출되는 메서드
func sceneDidBecomeActive(_ scene: UIScene) {
// 장면이 활성 상태로 전환될 때 일시 중지된 작업을 재시작하는 데 사용
}
// 장면이 활성 상태에서 비활성 상태로 이동할 때 호출되는 메서드
func sceneWillResignActive(_ scene: UIScene) {
// 일시적인 중단으로 인해 호출될 수 있음 (예: 전화 통화 수신)
}
// 장면이 백그라운드에서 포그라운드로 전환될 때 호출되는 메서드
func sceneWillEnterForeground(_ scene: UIScene) {
// 백그라운드에서 들어올 때 변경 사항을 원래대로 되돌리는 데 사용
}
// 장면이 포그라운드에서 백그라운드로 전환될 때 호출되는 메서드
func sceneDidEnterBackground(_ scene: UIScene) {
// 데이터를 저장하고 공유 리소스를 해제하며 장면의 현재 상태를 복원할 수 있도록 상태 정보를 저장하는 데 사용
}
}
//
// ViewController.swift
// ddd
//
// Created by 소프트웨어컴퓨터 on 2025/03/26.
//
import UIKit // UIKit 프레임워크를 임포트하여 UI 관련 기능을 사용 가능하게 함
class ViewController: UIViewController { // ViewController 클래스 선언, UIViewController를 상속받음
override func viewDidLoad() { // 뷰가 메모리에 로드된 후 호출되는 메서드
super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 동작 수행
// 뷰가 로드된 후 추가적인 설정을 수행할 수 있음
}
}
- 소스 설명
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
1. **메서드의 역할**:
- 이 메서드는 새로운 장면(session)이 연결될 때 호출됩니다. 주로 앱이 멀티태스킹을 지원하는 경우, 새로운 장면을 생성하고 초기화하기 위한 설정을 반환하는 데 사용됩니다.
2. **매개변수**:
- `application`: 현재 실행 중인 애플리케이션의 인스턴스를 나타냅니다.
- `connectingSceneSession`: 새로 연결될 장면의 세션을 나타내는 객체입니다. 이 객체는 장면에 대한 정보를 담고 있습니다.
- `options`: 장면 연결과 관련된 추가 옵션을 포함하는 객체입니다. 예를 들어, 사용자가 특정 작업을 통해 장면을 생성했는지에 대한 정보가 포함될 수 있습니다.
3. **반환값**:
- `UISceneConfiguration`: 새 장면을 구성하는 데 필요한 설정을 포함하는 객체입니다. 이 설정은 장면의 이름과 역할을 정의합니다.
### 코드의 흐름
- 메서드가 호출되면, 새로운 장면 세션을 생성하기 위해 적절한 `UISceneConfiguration` 객체를 반환해야 합니다.
- 일반적으로 기존에 정의된 설정(예: "Default Configuration")을 반환하거나, 특정 요구 사항에 맞는 새로운 설정을 생성할 수 있습니다.
- 위의 코드에서는 "Default Configuration"이라는 이름의 기본 구성 객체를 반환하고, 연결된 장면의 역할(sessionRole)을 설정합니다. 이로 인해 새로운 장면이 해당 설정에 따라 초기화됩니다.
- 언더바를 쓴 이유는?
언더바(`_`)는 Swift에서 매개변수의 레이블을 생략하기 위해 사용됩니다. 언더바를 사용함으로써 다음과 같은 효과를 얻을 수 있습니다.
### 매개변수 레이블 생략
1. **코드 간결성**:
- 매개변수 앞의 레이블을 생략하면 메서드 호출 시 더 간결한 형태로 작성할 수 있습니다. 예를 들어, 다음과 같은 호출이 가능합니다:
```swift
application(application, configurationForConnecting: connectingSceneSession, options: connectionOptions)
```
- 여기서 첫 번째 매개변수인 `application`의 레이블을 생략하기 위해 언더바를 사용합니다.
2. **의미 명확화**:
- 메서드의 첫 번째 매개변수는 `application`으로, 메서드의 호출 문맥에서 그 의미가 분명하므로 레이블을 생략해도 코드의 가독성을 해치지 않습니다.
- 아래 코드 두개를 다 쓴 이유?
configurationForConnecting connectingSceneSession:
'configurationForConnecting'과 'connectingSceneSession'을 모두 사용하는 이유는 Swift 메서드의 매개변수를 정의할 때 역할과 의미를 명확하게 하기 위해서입니다. 각각의 요소가 어떤 의미를 가지는지 아래에서 설명하겠습니다.
1. 매개변수 레이블: 'configurationForConnecting'
- **목적**: 이 레이블은 메서드 호출 시 매개변수의 역할을 설명합니다. 즉, 이 매개변수가 "새로운 장면을 연결할 때 사용할 구성"이라는 것을 나타냅니다.
- **예시**: 메서드 호출 시 다음과 같이 사용됩니다.
application(application, configurationForConnecting: connectingSceneSession, options: connectionOptions)
여기서 `configurationForConnecting`은 호출하는 쪽에서 이 매개변수가 어떤 목적을 가지고 있는지를 명확히 알려줍니다.
### 2. 매개변수 이름: `connectingSceneSession`
- **목적**: 이 이름은 메서드 내에서 해당 매개변수를 참조할 때 사용됩니다. 실제로 연결될 장면의 세션을 나타내며, 장면의 상태와 관련된 정보를 포함합니다.
- **예시**: 메서드 내부에서 다음과 같이 사용할 수 있습니다.
let sceneRole = connectingSceneSession.role
여기서 `connectingSceneSession`을 통해 해당 세션의 역할(role)을 가져올 수 있습니다.
### 요약
- **구성 요소의 역할**:
- **`configurationForConnecting`**: 매개변수의 목적을 설명하는 레이블.
- **`connectingSceneSession`**: 메서드 내부에서 사용할 수 있는 실제 매개변수 이름.
이 두 가지 요소를 함께 사용함으로써 코드의 가독성을 높이고, 메서드의 동작을 이해하기 쉽게 만듭니다. 즉, 매개변수의 역할과 내용을 명확히 구분하여 코드의 의도를 보다 쉽게 파악할 수 있도록 돕습니다.
func application(_ application: UIApplication, configurationForConnecting/*외부*/ connectingSceneSession/*내부*/: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
- argument vs parameter
import UIKit
func sayHello() -> Void { //매개변수가 없는 경우, 빈 괄호를 쓴다.
print("Hello") //리턴값이 없는 경우, -> Void는 생략 가능
}
sayHello()
↓
- 자료형 출력 (type(of : ))
func add(x:Int, y:Int) -> Int {
return x+y
}
print(add(x: 10, y: 20)) //30
var x = 10
print(type(of: x)) //Int 출력. 타입추론
print(type(of: add)) // (Int, Int) -> Int
- 내부 매개변수, 외부 매개변수
func add(xx x:Int, yy y:Int) -> Int {
return x+y
}
print(add(xx: 10, yy: 20)) //30
// xx yy : argument label, 외부에서 사용
// x y : parameter name, 내부에서 사용
print(type(of: add)) // (Int, Int) -> Int
func add(first x:Int, second y:Int) -> Int {
return x+y
}
print(add(first: 5, second: 10)) //15
func add(x:Int, y:Int) -> Int { //외부 매개변수명 생략하는 경우, 내부가 외부명까지 겸함
return x+y
}
print(add(x: 10, y: 20)) //30
//print(type(of: add)) // (Int, Int) -> Int
※ 함수명 주의
func add(first x:Int, second y:Int) -> Int {
print(#function) //함수명 출력, add(first:second:)
return x+y
}
print(add(first: 5, second: 10)) //15
func add(x:Int, y:Int) -> Int { //외부 매개변수명 생략하는 경우, 내부가 외부명까지 겸함
print(#function) //함수명 출력, add(x:y:)
return x+y
}
print(add(x: 10, y: 20)) //30
func add(_ x:Int, _ y:Int) -> Int { //내부, 외부명이 필요 없다는 것. 가장 안쓰는 방식임.
print(#function) //함수명 출력, add(_:_:)
return x+y
}
print(add(1, 2)) //3
func add(_ x:Int, yy y:Int) -> Int { //
print(#function) //함수명 출력, add(_:yy:)
return x+y
}
print(add(5, yy:3)) //8
//print(type(of: add)) // (Int, Int) -> Int
//위 4가지 모두 타입은 다 똑같음. but, 함수명은 다 다름! add아님!!
//add(first:second:) add(x:y:) add(_:_:) add(_:yy:)
func application(_/*외부*/ application/*내부*/: UIApplication, configurationForConnecting/*외부*/ connectingSceneSession/*내부*/: UISceneSession, options/*왜내부 겸*/: UIScene.ConnectionOptions) -> UISceneConfiguration {
이 함수의 함수명
-> application(_:configurationForConnecting:options:)
외부 매개변수명을 나열하면 됨.
- numberOfRowsInSection 함수란?
'numberOfRowsInSection'은 주로 'UITableViewDataSource' 프로토콜의 메서드 중 하나로, UITableView의 특정 섹션에 표시할 행(row)의 개수를 반환하는 데 사용됩니다. 이 메서드는 UITableView가 데이터를 표시하는 데 필요한 정보를 제공합니다.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
#### 매개변수
- **`tableView`**: 행의 개수를 요청하는 UITableView 인스턴스입니다. 이 매개변수는 메서드가 어떤 테이블 뷰에 대한 요청인지 구별하는 데 사용됩니다.
- **`section`**: 행의 개수를 반환할 섹션의 인덱스입니다. UITableView는 여러 섹션을 가질 수 있으며, 각 섹션마다 다른 행의 수를 가질 수 있습니다.
#### 반환값
- **`Int`**: 지정된 섹션에 표시할 행의 개수를 나타내는 정수값입니다.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 5 // 첫 번째 섹션에 5개의 행을 보여줌
} else {
return 3 // 두 번째 섹션에 3개의 행을 보여줌
}
}
### 요약
`numberOfRowsInSection` 메서드는 UITableView의 데이터 소스에서 각 섹션에 몇 개의 행을 표시할지를 결정하는 중요한 역할을 하며, UITableView가 데이터를 올바르게 표시할 수 있도록 돕습니다.
- 객체지향 용어
- property
1. 초기값이 있는 경우
class Man {
var age : Int = 0
}
2. init을 이용해서 초기화
3. 옵셔널 변수로 선언
- class 생성
class Man {
var age : Int = 23 //프로퍼티
var weight : Double = 3.5 //프로퍼티
//init(){}
func display(){ //인스턴스 메서드
print("나이=\(age), 몸무게=\(weight)") }
}
var River : Man = Man() // : Man 생략가능
//River -> 프로퍼티
River.display()
- designated initializer
Swift에서 designated initializer는 클래스의 주요 초기화 메서드로, 클래스의 모든 저장 프로퍼티를 초기화하고, 해당 클래스의 기본 구현을 설정합니다. designated initializer는 클래스에서 필수적으로 구현해야 하는 초기화 메서드
class Man { // Man이라는 이름의 클래스를 정의
var age: Int // 나이를 나타내는 저장 프로퍼티 선언
var weight: Double // 몸무게를 나타내는 저장 프로퍼티 선언
// Man 클래스의 정보를 출력하는 메서드
func display() {
print("나이=\(age), 몸무게=\(weight)") // age와 weight 프로퍼티의 값을 출력
}
// Designated initializer: 나이와 몸무게를 초기화하는 메서드
init(age: Int, weight: Double) {
self.age = age // 전달받은 age 값을 저장 프로퍼티에 할당
self.weight = weight // 전달받은 weight 값을 저장 프로퍼티에 할당
}
}
var River = Man(age: 23, weight: 3.5) // Man 클래스의 인스턴스를 생성하고, age와 weight를 초기화
// River는 Man 클래스의 인스턴스이며, 나이는 23, 몸무게는 3.5로 설정됨
River.display() // River 인스턴스의 display() 메서드를 호출하여 나이와 몸무게를 출력
- 상속
class Man{
var age : Int
var weight : Double
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double) {
self.age = age
self.weight = weight
} //designated initializer
}
class Student : Man { // 상속
}
var kim : Student = Student (age: 25, weight: 60.6)
kim.display()
var River = Man(age: 23, weight: 3.5)
//River -> 프로퍼티
River.display()
- super
class Man{
var age : Int
var weight : Double
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){
self.age = age
self.weight = weight
}
}
class Student : Man {
var name : String
func displayS() {
print("이름=\(name), 나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double, name : String){
self.name = name
super.init(age:age, weight:weight) //이 줄을 안쓰면?
}//error:'super.init' isn't called on all paths before returning from initializer
} //자식 클래스에서 designated initializer를 만들면 부모 init()상속 안됨
var lee : Student = Student(age:20,weight:65.2,name:"홍길동")
lee.displayS() //이름=홍길동, 나이=20, 몸무게=65.2
lee.display() //나이=20, 몸무게=65.2
- 오류 발생?
self.name = name
super.init(age:age, weight:weight)
//이부분이 순서가 바뀌면 오류가 발생함
Swift에서 초기화 과정은 매우 중요하며, 초기화 메서드에서 프로퍼티를 올바르게 초기화해야 합니다.
'self.name = name'과 'super.init(age:age, weight:weight)'의 순서를 바꾸면 오류가 발생하는 이유는 다음과 같습니다.
1. **초기화 순서**:
- Swift에서는 모든 저장 프로퍼티가 사용되기 전에 초기화되어야 합니다. 즉, 클래스의 designated initializer에서 `self`를 사용할 수 있는 상태가 되어야 합니다.
- `super.init(...)`을 호출하기 전에 `self`를 사용하여 프로퍼티를 초기화하는 것은 불가능합니다.
2. **오류 발생**:
- `self.name = name`을 `super.init(...)` 호출 이전에 위치시키면, `self`가 초기화되지 않은 상태에서 `name` 프로퍼티를 설정하려고 시도하게 됩니다. 이 경우, Swift는 `self`가 유효한 상태가 아니기 때문에 "super.init isn't called on all paths before returning from initializer"라는 오류 메시지를 발생시킵니다.
- Swift의 초기화 규칙에 따라 상위 클래스의 초기화가 완료되기 전에는 하위 클래스의 프로퍼티를 설정할 수 없습니다.
### 요약
- 'self'를 사용하기 전에 'super.init(...)'을 호출해야 하며, 이는 Swift의 초기화 규칙에 따른 것입니다.
- 초기화 순서를 바꾸면 'self'가 초기화되지 않은 상태에서 접근하려고 하기 때문에 오류가 발생합니다.
- 부모와 함수 명을 같게 하려면?
class Man{
var age : Int
var weight : Double
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double){
self.age = age
self.weight = weight
}
}
class Student : Man {
var name : String
override func display() { //부모함수에 있는 명과 똑같이 display로 이름이 같게하려면 override라고 하는걸 붙여줘야함.
print("이름=\(name), 나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight : Double, name : String){
self.name = name
super.init(age:age, weight:weight) //이 줄을 안쓰면?
}//error:'super.init' isn't called on all paths before returning from initializer
} //자식 클래스에서 designated initializer를 만들면 부모 init()상속 안됨
var lee : Student = Student(age:20,weight:65.2,name:"홍길동")
lee.displayS() //이름=홍길동, 나이=20, 몸무게=65.2
lee.display() //나이=20, 몸무게=65.2
- 코드 설명
class Man { // Man이라는 이름의 클래스를 정의
var age: Int // 나이를 나타내는 저장 프로퍼티 선언
var weight: Double // 몸무게를 나타내는 저장 프로퍼티 선언
// Man 클래스의 정보를 출력하는 메서드
func display() {
print("나이=\(age), 몸무게=\(weight)") // age와 weight 프로퍼티의 값을 출력
}
// Designated initializer: 나이와 몸무게를 초기화하는 메서드
init(age: Int, weight: Double) {
self.age = age // 전달받은 age 값을 저장 프로퍼티에 할당
self.weight = weight // 전달받은 weight 값을 저장 프로퍼티에 할당
}
}
class Student: Man { // Man 클래스를 상속받는 Student 클래스 정의
var name: String // 학생의 이름을 저장하는 프로퍼티 선언
// display 메서드를 오버라이드하여 학생 정보를 출력하는 메서드
override func display() {
print("이름=\(name), 나이=\(age), 몸무게=\(weight)") // name, age, weight를 출력
}
// Designated initializer: 나이, 몸무게, 이름을 초기화하는 메서드
init(age: Int, weight: Double, name: String) {
self.name = name // 전달받은 name 값을 저장 프로퍼티에 할당
super.init(age: age, weight: weight) // 상위 클래스의 designated initializer 호출
}
// 이 줄을 안쓰면 초기화 과정에서 상위 클래스의 프로퍼티가 초기화되지 않아 오류 발생
}
var lee: Student = Student(age: 20, weight: 65.2, name: "홍길동") // Student 클래스의 인스턴스를 생성
// lee는 나이 20, 몸무게 65.2, 이름 "홍길동"으로 초기화됨
lee.display() // lee 인스턴스의 display() 메서드를 호출하여 이름, 나이, 몸무게를 출력
- delegate
델리게이트(Delegate) 디자인 패턴은 객체 지향 프로그래밍에서 객체 간의 통신을 위한 패턴으로, 주로 한 객체가 다른 객체에 작업을 위임할 때 사용됩니다. 이 패턴은 이벤트나 행동을 처리하는 데 유용하며, Swift와 같은 언어에서 자주 사용됩니다.
### 델리게이트 패턴의 구성 요소
1. **델리게이트 프로토콜**: 델리게이트가 구현해야 하는 메서드들을 정의하는 프로토콜입니다.
2. **주체(Delegate)**: 이벤트를 발생시키는 객체로, 델리게이트 프로토콜을 사용하여 델리게이트에게 작업을 위임합니다.
3. **델리게이트 객체**: 주체의 델리게이트 프로토콜을 구현하고, 주체에서 발생하는 이벤트에 대한 반응을 정의합니다.
### 예시: Swift에서의 델리게이트 패턴
아래는 델리게이트 패턴을 사용하는 간단한 예시입니다. `MyButton` 클래스가 버튼 클릭 이벤트를 델리게이트에게 알리는 구조입니다.
// 1. 델리게이트 프로토콜 정의
protocol MyButtonDelegate: AnyObject {
func buttonWasClicked() // 버튼 클릭 시 호출될 메서드 정의
}
// 2. 주체 클래스 정의
class MyButton {
weak var delegate: MyButtonDelegate? // 델리게이트 프로퍼티 (weak 참조)
func click() { // 버튼 클릭 메서드
print("버튼이 클릭되었습니다.")
delegate?.buttonWasClicked() // 델리게이트에게 버튼 클릭 알림
}
}
// 3. 델리게이트 객체 구현
class ViewController: MyButtonDelegate {
func buttonWasClicked() {
print("델리게이트가 버튼 클릭을 처리했습니다.") // 델리게이트가 처리하는 로직
}
}
// 4. 사용 예시
let myButton = MyButton() // MyButton 인스턴스 생성
let viewController = ViewController() // ViewController 인스턴스 생성
myButton.delegate = viewController // 델리게이트 설정
myButton.click() // 버튼 클릭 시뮬레이션
### 설명
1. **델리게이트 프로토콜**: `MyButtonDelegate` 프로토콜을 정의하여 버튼 클릭 시 호출될 메서드를 선언합니다.
2. **주체 클래스**: `MyButton` 클래스에서는 클릭 메서드(`click()`)가 버튼 클릭을 처리하고, 델리게이트에게 알리도록 구현합니다. `delegate` 프로퍼티는 `weak`로 선언하여 순환 참조를 방지합니다.
3. **델리게이트 객체**: `ViewController` 클래스는 `MyButtonDelegate` 프로토콜을 준수하며, 버튼 클릭 이벤트를 처리하는 `buttonWasClicked()` 메서드를 구현합니다.
4. **사용 예시**: `MyButton`과 `ViewController` 인스턴스를 생성하고, `myButton`의 델리게이트를 `viewController`로 설정한 뒤 버튼 클릭을 시뮬레이션합니다.
### 요약
델리게이트 패턴은 객체 간의 느슨한 결합을 통해 코드의 재사용성을 높이고, 이벤트 처리를 간단하게 만들 수 있는 강력한 디자인 패턴입니다. Swift에서는 UI 이벤트 처리 및 다양한 상황에서 널리 사용됩니다. 추가적인 질문이 있으시면 말씀해 주세요!
- protocol(프로토콜)
이 에러 메시지 **"Property in protocol must have explicit { get } or { get set } specifier"**는 프로토콜에 정의된 프로퍼티가 읽기 또는 읽기/쓰기 가능성을 명시해야 한다는 의미입니다. 프로토콜에서 프로퍼티를 정의할 때는, 그 프로퍼티가 어떻게 사용될지를 명확히 하기 위해 `{ get }` 또는 `{ get set }`을 지정해야 합니다.
- **`{ get }`**: 해당 프로퍼티는 읽기 전용입니다. 이 프로퍼티는 값에 접근할 수 있지만 수정할 수는 없습니다.
- **`{ get set }`**: 해당 프로퍼티는 읽기 및 쓰기가 가능합니다. 이 프로퍼티의 값에 접근하고 수정할 수 있습니다.
### 수정된 코드
아래는 에러를 수정한 코드입니다. 프로퍼티가 읽기 전용인 경우 `{ get }`을 추가했습니다.
protocol Runnable {
var x: Int { get } // 읽기 전용 프로퍼티로 정의
}
또는, 읽기 및 쓰기가 가능한 프로퍼티로 정의하려면 `{ get set }`을 사용할 수 있습니다.
protocol Runnable {
var x: Int { get set } // 읽기 및 쓰기가 가능한 프로퍼티로 정의
}
### 요약
- 프로토콜에서 프로퍼티를 정의할 때는 반드시 접근 제어를 명시해야 하며, 이를 통해 해당 프로퍼티의 사용 방식이 명확해집니다.
- `{ get }`은 읽기 전용, `{ get set }`은 읽기 및 쓰기가 가능함을 나타냅니다.
프로토콜 Runnable에 적합하지 않는다는 오류 발생.
- 프로토콜 정의, 채택, 준수
protocol Runnable {
var x : Int { get set } //{ get set }이라서 읽기와 쓰기 가능한 프로퍼티
func run() //메서드는 선언만
}
class Man : Runnable { //채택, adopt - Runnable이 부모 상속인지 프로토콜 채택인지 알 수 없을 수 있음
var x: Int = 1 //준수, conform - 초기값 없으면 오류발생.
func run() { //준수, conform
print("Runnnnn")
}
}
var River : Man = Man()
River.run()
- 상속과 프로토콜을 동시에 사용하는 예제
// 1. 작업을 위한 프로토콜 정의
protocol Workable {
var name: String { get } // 이름 프로퍼티
func work() // 작업 메서드
}
// 2. 기본 클래스 정의: Employee
class Employee: Workable {
var name: String // 직원의 이름
var employeeID: Int // 직원 ID
// 초기화 메서드
init(name: String, employeeID: Int) {
self.name = name
self.employeeID = employeeID
}
// 프로토콜의 work() 메서드 구현
func work() {
print("\(name)님이 일을 하고 있습니다.")
}
}
// 3. 매니저 클래스: Employee를 상속
class Manager: Employee {
var department: String // 관리하는 부서
// 초기화 메서드
init(name: String, employeeID: Int, department: String) {
self.department = department
super.init(name: name, employeeID: employeeID) // 상위 클래스 초기화
}
// work() 메서드 오버라이드
override func work() {
print("\(name)님이 \(department) 부서를 관리하고 있습니다.")
}
}
// 4. 인턴 클래스: Employee를 상속
class Intern: Employee {
var mentor: String // 멘토의 이름
// 초기화 메서드
init(name: String, employeeID: Int, mentor: String) {
self.mentor = mentor
super.init(name: name, employeeID: employeeID) // 상위 클래스 초기화
}
// work() 메서드 오버라이드
override func work() {
print("\(name)님이 \(mentor)님의 지도 아래 인턴십을 하고 있습니다.")
}
}
// 5. 사용 예시
let manager = Manager(name: "김부장", employeeID: 101, department: "영업")
manager.work() // 매니저의 작업 메서드 호출
let intern = Intern(name: "홍길동", employeeID: 102, mentor: "이멘토")
intern.work() // 인턴의 작업 메서드 호출
출처 :
뤼튼 사용하러 가기 > https://agent.wrtn.ai/5xb91l
smile han > https://youtube.com/@smilehan8416?si=t6AyWf3K1BVyPd6S