iOS프로그래밍 실무 4주차

post-thumbnail

출처 : smileHan - iOS 프로그래밍기초

AppDelegate.swift

앱의 진입점이며, 앱의 생명 주기를 관리하는 역할을 합니다.

import UIKit  // UIKit 프레임워크를 가져와서 UI 관련 기능을 사용 가능하게 함

@main  // 앱의 진입점임을 나타내는 애노테이션
class AppDelegate: UIResponder, UIApplicationDelegate {  // UIApplicationDelegate 프로토콜을 구현하는 AppDelegate 클래스 정의

    // 앱이 시작될 때 호출되는 메서드
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 앱이 실행된 후 추가적인 설정을 할 수 있음
        return true  // 기본적으로 앱 실행을 정상적으로 완료했다고 반환
    }

    // MARK: UISceneSession Lifecycle

    // 새로운 씬 세션이 생성될 때 호출되는 메서드
    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>) {
        // 사용자가 실행 중이던 씬을 닫았을 때 해당 리소스를 정리할 수 있음
    }
}
func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
) -> UISceneConfiguration

//1. 메서드의 역할
//UIApplicationDelegate 프로토콜의 필수 메서드 중 하나입니다.
//새로운 씬(Scene)을 설정하기 위한 UISceneConfiguration 객체를 반환합니다.
//앱이 멀티 윈도우(멀티 씬)를 지원할 경우 각 씬에 맞는 설정을 적용하는 역할을 합니다.

//_는 왜 사용했을까?
//Swift에서 _는 파라미터 이름 생략을 의미합니다. 이 메서드에서 application 앞에 _가 //붙은 이유는 다음과 같습니다:

//2. 외부 파라미터 이름 생략

//Swift 함수는 기본적으로 파라미터에 외부 이름(external name)과 내부 이름(internal //name)을 가질 수 있습니다.
//외부 이름은 함수를 호출할 때 사용되고, 내부 이름은 함수 내부에서 사용됩니다.
//_를 사용하면 외부에서 이 파라미터 이름을 생략할 수 있습니다.


configurationForConnecting connectingSceneSession: 

//여기서 configurationForConnecting connectingSceneSession: //UISceneSession은 하나의 파라미터를 정의하는 구문입니다. 이걸 분리해서 이해하면:
//configurationForConnecting: 외부 파라미터 이름 (External Parameter Name)
//connectingSceneSession: 내부 파라미터 이름 (Internal Parameter Name)
//: UISceneSession: 파라미터의 타입

SceneDelegate.swift

씬(화면)의 상태를 관리하는 역할을 합니다.

import UIKit  // UIKit 프레임워크를 가져와서 UI 관련 기능을 사용 가능하게 함

class SceneDelegate: UIResponder, UIWindowSceneDelegate {  // UIWindowSceneDelegate 프로토콜을 구현하는 SceneDelegate 클래스 정의

    var window: UIWindow?  // 앱의 UI를 담당하는 UIWindow 인스턴스 선언

    // 새로운 씬이 연결될 때 호출되는 메서드
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // 스토리보드를 사용하는 경우 자동으로 window가 설정됨
        guard let _ = (scene as? UIWindowScene) else { return }  // UIWindowScene이 존재하는지 확인 후 실행
    }

    // 씬이 해제될 때 호출되는 메서드
    func sceneDidDisconnect(_ scene: UIScene) {
        // 시스템이 씬을 정리할 때 호출됨 (백그라운드에서 제거될 때 등)
    }

    // 씬이 활성 상태(Active)로 전환될 때 호출되는 메서드
    func sceneDidBecomeActive(_ scene: UIScene) {
        // 일시 정지되었던 작업을 재개하거나 필요한 설정을 초기화할 때 사용됨
    }

    // 씬이 비활성화(Inactive) 상태로 전환될 때 호출되는 메서드
    func sceneWillResignActive(_ scene: UIScene) {
        // 전화 수신 등의 이유로 앱이 일시적으로 비활성화될 때 수행할 작업
    }

    // 씬이 포그라운드(화면)로 전환될 때 호출되는 메서드
    func sceneWillEnterForeground(_ scene: UIScene) {
        // 백그라운드에서 다시 앱으로 돌아왔을 때 수행할 작업
    }

    // 씬이 백그라운드로 전환될 때 호출되는 메서드
    func sceneDidEnterBackground(_ scene: UIScene) {
        // 데이터를 저장하고 리소스를 해제하는 등의 작업 수행
    }
}

ViewController.swift

앱의 기본 화면을 담당하는 뷰 컨트롤러입니다.

import UIKit  // UIKit 프레임워크를 가져와서 UI 관련 기능을 사용 가능하게 함

class ViewController: UIViewController {  // UIViewController를 상속받는 ViewController 클래스 정의

    override func viewDidLoad() {  // 뷰가 메모리에 로드된 후 호출되는 메서드
        super.viewDidLoad()  // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 설정 유지
        // 추가적인 초기 설정을 여기에 작성할 수 있음
    }
}

C언어 함수

#include <stdio.h>
void Fun( int param ) // parameter(매개변수, 인자), 형식 매개변수(formal parameter)
{
printf("%d",param);
}
int main()
{
Fun( 10 ); // 10은 argument(전달인자), 실 매개변수(actual parameter)
return 0;
}

파라미터(Parameter)란?

정의: 함수 정의에서 사용하는 형식 매개변수(formal parameter)로, 함수가 받을 값을 나타내는 변수입니다.

  • 역할

    함수 내부에서 외부에서 전달된 값을 처리할 때 사용됩니다. 즉, 함수의 "설계도"에 해당하는 부분입니다.
  • 특징

    함수 선언/정의 시 이름과 타입이 지정됩니다.
    함수가 호출될 때 실제 값(아규먼트)이 이 변수에 바인딩됩니다.
    코드에서: void Fun(int param)에서 param이 파라미터입니다.
    int param은 "이 함수는 정수형 값을 받겠다"는 뜻이고, param은 그 값을 함수 안에서 부르는 이름입니다.

아규먼트(Argument)란?

정의: 함수 호출 시 실제로 전달되는 값으로, 실 매개변수(actual parameter)라고도 합니다.

  • 역할

    함수가 실행될 때 파라미터에 "실제 데이터"를 제공합니다. 즉, 함수 호출의 "구체적인 입력값"입니다.
  • 특징

    함수 호출 시점에 제공되며, 값이나 변수, 표현식일 수 있습니다.
    파라미터와 타입/순서가 맞아야 합니다.
    코드에서: Fun(10)에서 10이 아규먼트입니다.
    10은 Fun 함수를 호출하면서 param에 전달되는 실제 값입니다.

Swift 함수

기초 함수 문법

func sayHello() -> Void {
    print("Hello, World!")
}
sayHello()
//함수명 : sayHello()
//반환형 : Void (생략 가능)

내부 매개변수와 외부 매개변수

함수의 다양한 정의 방법

func add(first x:Int, second y:Int) -> Int{
    print(#function)
    return x+y
}
print(add(first:110,second:201))

func add(x:Int, y:Int) -> Int{
    print(#function)
    return x+y
}
print(add(x:10,y:20))

func add(_ x:Int, _ y:Int) -> Int{
    print(#function)
    return x+y
}
print(add(120,230))

func add(_ x:Int, yy y:Int) -> Int{
    print(#function)
    return x+y
}
print(add(13,yy:24))

print(#function)을 사용하면 그 함수의 이름을 알 수 있습니다.
위에는 기능은 같지만 서로다른 이름을 가지고 있습니다.

함수 이름 확인

func application(
    _ application: UIApplication,
    configurationForConnecting connectingSceneSession: UISceneSession,
    options: UIScene.ConnectionOptions
) -> UISceneConfiguration

✅ 함수의 기본 이름(Base Name):
👉 application

✅ 전체 함수 시그니처(Full Name, 공식적으로는 "Function Name"):
👉 application(_:configurationForConnecting:options:)

즉, Swift에서 함수의 공식적인 이름은 기본 이름(Base Name) + 모든 매개변수 레이블을 포함한 형태입니다.

@MainActor
func tableView(
    _ tableView: UITableView,
    numberOfRowsInSection section: Int
) -> Int

📌 함수의 역할

  • UITableViewDataSource 프로토콜의 필수 메서드 중 하나입니다.
  • 특정 섹션(section)에 포함될 행(row)의 개수를 결정합니다.
  • return 값으로 해당 섹션에 표시할 셀의 개수(Int)를 반환합니다.

클래스(class)

class Man{
    var age : Int = 0
}

swift에서는 클래스안의 변수를 프로퍼티라고 한다.
그리고 그 프로퍼티의 초깃값이 존재해야한다.

저장 프로퍼티(stored property) 에 초기값을 주는 3가지 방법

  • 직접 지정
  • 옵셔널 사용
  • 생성자 사용
class Man{
    var age : Int = 1 //직접 지정
    var weight : Double = 3.5
}

class Man{
    var age : Int? //옵셔널 변수는 nil로 자동 초기화
    var weight : Double! //옵셔널 변수는 nil로 자동 초기화
}

class Man{
    var age : Int
    var weight : Double
    init(){ //initializer로 초기화
        age = 1
        weight = 3.5
    }
}

Designated Initializer란?

  • 정의

    Designated initializer는 클래스에서 모든 저장 속성(stored properties)을 초기화하는 책임을 지는 주요 생성자입니다.
  • 특징

    클래스의 모든 저장 속성을 반드시 초기화해야 합니다.
    부모 클래스가 있다면, 부모 클래스의 designated initializer를 호출해야 합니다.
    클래스마다 최소 하나 이상의 designated initializer가 있어야 합니다.
    비유: 집을 지을 때 "기초 공사"를 담당하는 팀이라고 생각하면 됩니다. 건물의 필수 구조(속성)를 완전히 세팅하는 역할이죠.
// 클래스를 정의합니다. 'Man'이라는 이름의 클래스를 생성.
class Man {
    // 'age'라는 정수형 저장 속성(property)을 선언하고 기본값 1로 초기화.
    var age: Int = 1
    
    // 'weight'라는 실수형(Double) 저장 속성을 선언하고 기본값 3.5로 초기화.
    var weight: Double = 3.5
    
    // 'display'라는 메서드를 정의. 객체의 나이와 몸무게를 출력하는 기능.
    func display() {
        // 문자열 보간법을 사용해 'age'와 'weight'를 출력.
        print("나이=\(age), 몸무게=\(weight)")
    }
    
    // Designated Initializer(지정 생성자)를 정의.
    // 이 생성자는 'age'와 'weight'를 파라미터로 받아 객체를 초기화.
    init(age: Int, weight: Double) {
        // 'self.age'는 클래스의 속성 'age'를 의미하며, 파라미터 'age' 값을 할당.
        self.age = age
        // 'self.weight'는 클래스의 속성 'weight'를 의미하며, 파라미터 'weight' 값을 할당.
        self.weight = weight
    } // 이 생성자는 모든 저장 속성을 초기화하므로 designated initializer임.
}

// 'kim'이라는 변수에 'Man' 클래스의 인스턴스를 생성하고 할당.
// 생성자에 'age: 25', 'weight: 60000'을 전달해 초기화.
var kim: Man = Man(age: 25, weight: 60000)

// 'kim' 객체의 'age' 속성을 출력. 결과는 25가 나옴.
print(kim.age)

오버라이드(override)

자식클래스에서 부모클래스의 메서드를 재정의 하고싶다면 오버라이드를 사용합니다.

// 'Man'이라는 부모 클래스를 정의합니다.
class Man {
    // 'age'라는 정수형 저장 속성(property)을 선언. 초기값 없음.
    var age: Int
    
    // 'weight'라는 실수형(Double) 저장 속성을 선언. 초기값 없음.
    var weight: Double
    
    // 'display' 메서드를 정의. 객체의 나이와 몸무게를 출력하는 기능.
    func display() {
        // 문자열 보간법을 사용해 'age'와 'weight'를 출력.
        print("나이=\(age), 몸무게=\(weight)")
    }
    
    // Designated Initializer(지정 생성자)를 정의.
    // 'age'와 'weight'를 파라미터로 받아 객체의 속성을 초기화.
    init(age: Int, weight: Double) {
        // 'self.age'는 클래스의 속성 'age'를 의미하며, 파라미터 'age' 값을 할당.
        self.age = age
        // 'self.weight'는 클래스의 속성 'weight'를 의미하며, 파라미터 'weight' 값을 할당.
        self.weight = weight
    }
}

// 'Student'라는 자식 클래스를 정의. 'Man' 클래스를 상속받음.
class Student: Man {
    // 'name'이라는 문자열형 저장 속성을 선언. 초기값 없음.
    var name: String
    
    // 부모 클래스 'Man'의 'display' 메서드를 오버라이드(재정의).
    override func display() {
        // 'name', 'age', 'weight'를 출력. 'age'와 'weight'는 부모 클래스에서 상속됨.
        print("이름=\(name), 나이=\(age), 몸무게=\(weight)")
    }
    
    // 'Student' 클래스의 Designated Initializer를 정의.
    // 'age', 'weight', 'name'을 파라미터로 받아 초기화.
    init(age: Int, weight: Double, name: String) {
        // 'self.name'은 'Student' 클래스의 속성 'name'을 의미하며, 파라미터 'name' 값을 할당.
        self.name = name
        // 부모 클래스 'Man'의 Designated Initializer를 호출해 'age'와 'weight'를 초기화.
        super.init(age: age, weight: weight) // 이 줄이 없으면 에러 발생.
    } // 주석: 이 줄이 없으면 "super.init이 호출되지 않았다"는 에러 발생.
      // 이유: 자식 클래스는 부모의 초기화를 반드시 호출해야 모든 속성이 초기화됨.
}

// 'Student' 클래스의 인스턴스를 생성하고 'lee' 변수에 할당.
// 'age: 20', 'weight: 65.2', 'name: "홍길동"'을 전달해 초기화.
var lee: Student = Student(age: 20, weight: 65.2, name: "홍길동")

// 'lee' 객체의 'display' 메서드를 호출.
// 'Student'에서 오버라이드된 메서드가 실행됨. 출력: "이름=홍길동, 나이=20, 몸무게=65.2"
lee.display()

// 'lee' 객체의 'display' 메서드를 다시 호출.
// 동일하게 "이름=홍길동, 나이=20, 몸무게=65.2" 출력.
lee.display()

Delegate Design Pattern

한 객체가 특정 작업이나 이벤트를 처리할 때, 그 작업을 직접 수행하지 않고 다른 객체(델리게이트)에게 "위임"하는 패턴

핵심 : 객체 A가 어떤 일을 해야 할 때, 그 일을 객체 B에게 맡기고, 객체 B가 대신 처리한 뒤 필요하면 결과를 객체 A에게 돌려줍니다.

주요 구성 요소

  • Delegator (위임자): 작업을 위임하는 객체. 보통 이벤트를 발생시키거나 특정 작업을 요청합니다.
  • Delegate (대리자): 위임받은 작업을 실제로 수행하는 객체. Delegator가 정의한 프로토콜(규약)을 따릅니다.
  • Protocol (프로토콜): Delegator와 Delegate 간의 소통 규칙을 정의한 인터페이스. 어떤 메서드를 구현해야 하는지 명시합니다.

동작 방식

  • 프로토콜 정의: Delegator가 "이런 일을 할 수 있어야 해"라는 메서드 집합(프로토콜)을 정의합니다.
  • 델리게이트 지정: Delegator가 Delegate 객체를 참조하도록 설정합니다(보통 속성으로 연결).
  • 위임 실행: Delegator가 특정 상황에서 Delegate의 메서드를 호출합니다.
  • 응답 처리: Delegate가 작업을 수행하고, 필요하면 Delegator에게 결과를 전달합니다.

프로토콜 에러[1]

  • 에러 의미: 프로토콜 속성에 { get } 또는 { get set }가 누락됨.
  • 원인: Swift는 프로토콜에서 속성의 읽기/쓰기 여부를 명시적으로 요구.
  • 해결: 속성 뒤에 { get }(읽기) 또는 { get set }(읽기/쓰기)를 추가.

프로토콜 에러[2]

protocol 정의, 채택, 준수

protocol Runnable { //대리하고 싶은 함수 목록 작성
    var x : Int {get set} //읽기와 쓰기 가능 프로퍼티,{get}은 읽기 전용
    //property in protocol must have explicit { get } or { get set } specifier
    func run() //메서드는 선언만 있음
}
class Man : Runnable { //채택, adopt
    var x : Int = 1 //준수, conform
    func run(){print("달린다~")} //준수, conform
}
let han = Man()

GPT : Swift에서 상속(Inheritance)프로토콜(Protocol)을 동시에 사용하는 쉬운 예제를 만들어 설명해드릴게요. 초보자도 이해하기 쉽게 간단한 상황을 설정하고, 코드와 함께 각 부분을 자세히 풀어볼게요.

상황: 동물원

  • 동물(부모 클래스): 모든 동물은 기본적으로 "이름"과 "소리를 내는 기능"이 있어요.
  • 강아지(자식 클래스): 동물을 상속받아 추가로 "짖는 소리"를 구체적으로 정의.
  • 프로토콜: 동물원에서 "전시 가능"한 동물은 특정 규칙(전시 방법)을 따라야 함.
// 프로토콜 정의: 전시 가능한 동물이 따라야 할 규칙
protocol Exhibitable {
    func showOff() // 전시 동작
}

// 부모 클래스: 동물
class Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func makeSound() {
        print("동물이 소리를 냅니다.")
    }
}

// 자식 클래스: 강아지 (상속 + 프로토콜 준수)
class Dog: Animal, Exhibitable {
    // 부모로부터 name 속성과 makeSound() 메서드를 상속받음
    
    // 부모의 메서드를 오버라이드
    override func makeSound() {
        print("\(name)가 짖습니다: 멍멍!")
    }
    
    // 프로토콜 요구사항 구현
    func showOff() {
        print("\(name)가 꼬리를 흔들며 전시됩니다!")
    }
}

// 사용
let myDog = Dog(name: "바둑이")
myDog.makeSound() // "바둑이가 짖습니다: 멍멍!" (상속받아 오버라이드)
myDog.showOff()   // "바둑이가 꼬리를 흔들며 전시됩니다!" (프로토콜 준수)

코드 설명

1. 프로토콜: Exhibitable

Exhibitable 프로토콜은 동물원에서 전시 가능한 동물이 따라야 할 규칙을 정의해요.
showOff() 메서드를 필수로 구현해야 함.
비유: 동물원에서 "전시 동작"을 어떻게 할지 규칙을 정한 것.

2. 부모 클래스: Animal

Animal 클래스는 모든 동물의 기본 속성과 동작을 정의.
name 속성과 makeSound() 메서드를 가짐.
비유: 모든 동물이 공통적으로 가지는 기본 특성(이름, 소리).

3. 자식 클래스: Dog

Dog는 Animal을 상속받아 name과 makeSound()를 물려받음.
makeSound()를 오버라이드해서 강아지 특유의 소리("멍멍")를 출력.
동시에 Exhibitable 프로토콜을 준수하며 showOff()를 구현.
비유: 강아지는 동물의 기본 특성을 물려받고, 추가로 전시 규칙을 따름.

4. 사용

Dog 객체(myDog)를 생성하고, 상속받은 메서드와 프로토콜 메서드를 호출.
상속: makeSound() → 부모로부터 물려받아 오버라이드.
프로토콜: showOff() → 전시 규칙을 따름.

상속과 프로토콜의 역할

  • 상속: Dog가 Animal의 기본 특성(name, makeSound)을 물려받아 재사용하고, 필요에 따라 수정(오버라이드).

  • 프로토콜: Dog가 동물원에서 전시 가능한 동물로 동작하기 위해 Exhibitable 규칙을 따름.
    비유로 이해

  • 상속: 강아지는 동물이라는 큰 카테고리에 속해서 기본 특성(이름, 소리)을 물려받음.
    프로토콜: 동물원에서 전시하려면 "전시 동작"이라는 규칙을 따라야 함. 강아지는 꼬리를 흔드는 방식으로 전시.

실행결과

바둑이가 짖습니다: 멍멍!
바둑이가 꼬리를 흔들며 전시됩니다!

요약

  • 상속: Dog가 Animal의 기본 기능을 물려받아 확장.
  • 프로토콜: Dog가 Exhibitable 규칙을 따라 전시 기능 추가.
  • 조합: 상속으로 기본 특성을 물려받고, 프로토콜로 추가 역할(전시)을 부여.
profile
The learning never stops. 😂

0개의 댓글