iOS프로그래밍 실무

[4주차] iOS프로그래밍 실무

리버윤 2025. 3. 26. 13:56
728x90

- 프로젝트 생성 시 만들어지는 

//
//  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

 

728x90