2025. 3. 26. 13:05ㆍiOS프로그래밍실무
이 글은 한성현 교수님의 ‘iOS 프로그램 실무’ 강의를 수강한 후, 복습을 위해 작성된 글입니다.
주로 실습 위주의 내용을 다루고 있습니다.
🔷 함수
- ViewController.swift
//
// ViewController.swift
// ddd
//
// Created by choseongeun on 3/26/25.
//
import UIKit // UIKit 프레임워크를 가져옵니다. UI 요소를 만들고 관리하는 데 필요합니다.
class ViewController: UIViewController { // ViewController 클래스는 UIViewController를 상속받습니다.
override func viewDidLoad() { // 뷰가 메모리에 로드된 후 호출되는 메서드입니다.
super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 동작을 수행합니다.
// Do any additional setup after loading the view. // 뷰가 로드된 후 추가 설정을 할 수 있는 곳입니다.
}
}
- ScenceDelegate.swift
//
// SceneDelegate.swift
// ddd
//
// Created by choseongeun on 3/26/25.
//
import UIKit // UIKit 프레임워크를 가져옵니다.
class SceneDelegate: UIResponder, UIWindowSceneDelegate { // SceneDelegate 클래스는 UIResponder와 UIWindowSceneDelegate를 상속받습니다.
var window: UIWindow? // UIWindow 객체를 저장할 변수입니다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 새로운 씬이 연결될 때 호출되는 메서드입니다.
guard let _ = (scene as? UIWindowScene) else { return } // 씬이 UIWindowScene인지 확인합니다. 아니면 메서드를 종료합니다.
}
func sceneDidDisconnect(_ scene: UIScene) {
// 씬이 시스템에 의해 해제될 때 호출되는 메서드입니다.
// 이 메서드에서 리소스를 해제할 수 있습니다.
}
func sceneDidBecomeActive(_ scene: UIScene) {
// 씬이 비활성 상태에서 활성 상태로 전환될 때 호출되는 메서드입니다.
// 이곳에서 일시 중지된 작업을 재시작할 수 있습니다.
}
func sceneWillResignActive(_ scene: UIScene) {
// 씬이 활성 상태에서 비활성 상태로 전환될 때 호출되는 메서드입니다.
// 이곳에서 일시 중지할 작업을 처리할 수 있습니다.
}
func sceneWillEnterForeground(_ scene: UIScene) {
// 씬이 백그라운드에서 포그라운드로 전환될 때 호출되는 메서드입니다.
// 이곳에서 백그라운드에서 변경된 내용을 되돌릴 수 있습니다.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// 씬이 포그라운드에서 백그라운드로 전환될 때 호출되는 메서드입니다.
// 이곳에서 데이터를 저장하고 공유 리소스를 해제할 수 있습니다.
}
}
- AppDelegate.swift
//
// AppDelegate.swift
// ddd
//
// Created by choseongeun on 3/26/25.
//
import UIKit // UIKit 프레임워크를 가져옵니다. UI 요소를 만들고 관리하는 데 필요합니다.
@main // 이 클래스가 애플리케이션의 진입점임을 나타냅니다.
class AppDelegate: UIResponder, UIApplicationDelegate { // AppDelegate 클래스는 UIResponder와 UIApplicationDelegate를 상속받습니다.
// 애플리케이션이 시작될 때 호출되는 메서드입니다.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // 애플리케이션이 시작된 후 사용자 정의를 위한 지점입니다.
return true // 애플리케이션이 성공적으로 시작되었음을 나타냅니다.
}
// MARK: UISceneSession Lifecycle // UISceneSession 생명주기 관련 메서드 섹션입니다.
// 새로운 씬 세션이 생성될 때 호출되는 메서드입니다.
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Use this method to select a configuration to create the new scene with. // 새로운 씬을 생성하기 위한 구성을 선택하는 데 사용됩니다.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) // 기본 구성으로 새로운 씬을 생성합니다.
}
// 사용자가 씬 세션을 버릴 때 호출되는 메서드입니다.
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // 애플리케이션이 실행되지 않을 때 세션이 버려지면 이 메서드가 호출됩니다.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return. // 버려진 씬에 특정한 리소스를 해제하는 데 사용됩니다.
}
}
Swift 함수에서 매개변수 레이블과 이름의 차이
Swift에서는 매개변수 레이블(Argument Label) 과 매개변수 이름(Parameter Name) 을 구분하여 사용합니다. 이를 통해 코드 가독성을 높이고 함수 내부에서 변수를 명확하게 다룰 수 있습니다.
✅ configurationForConnecting connectingSceneSession 의 구조
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration
1. 매개변수 레이블 (configurationForConnecting)
• 함수 호출 시 사용됩니다.
• appDelegate.application(myApp, configurationForConnecting: session, options: options) 처럼 사용됩니다.
• 역할을 명확히 하여 가독성을 높여줍니다.
2. 매개변수 이름 (connectingSceneSession)
• 함수 내부에서 변수처럼 사용됩니다.
• connectingSceneSession.role처럼 값을 참조할 때 사용됩니다.
✅ _ (언더바)의 역할
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
• _ 를 사용하면 첫 번째 매개변수의 레이블을 생략할 수 있습니다.
• application:을 명시할 필요 없이 appDelegate.application(myApp, configurationForConnecting: session, options: options) 처럼 간결하게 호출할 수 있습니다.
• iOS 시스템에서 자동 호출되는 함수이므로 첫 번째 매개변수의 레이블이 불필요하기 때문에 생략하는 것이 일반적입니다.
✅ 결론
• 매개변수 레이블은 함수 호출 시 사용되어 가독성을 높입니다.
• 매개변수 이름은 함수 내부에서 값에 접근할 때 사용됩니다.
• _를 사용하면 첫 번째 매개변수 레이블을 생략하여 코드가 간결해집니다.
이러한 Swift의 설계 방식은 가독성과 직관성을 높이기 위한 원칙입니다. 😊
🔶 Argument vs. Parameter
구분 | 역할 | 사용 위치 | 예시 |
Argument (인자) | 함수 호출 시 전달하는 값 | 함수 외부 | greet(to: "성은") |
Parameter (매개변수) | 함수 내부에서 값을 받는 변수 | 함수 내부 | func greet(to name: String) |
📌 Argument는 함수 호출 시 사용하고, Parameter는 함수 내부에서 값을 다룰 때 사용합니다.
🔶 Void의 의미와 생략 가능성
import UIKit
func sayHello() -> Void {
// return값이 없는 경우 '-> Void' 생략 가능
print("Hello")
}
sayHello()
Void는 반환값이 없음을 나타내지만, Swift에서는 이를 생략해도 무방합니다. 🚀
🔶 Swift에서 함수와 타입 확인
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
📌 오류 발생 주의
print(add(10,20)) // ❌ 오류 발생!
오류 원인:
Swift에서는 함수 호출 시 매개변수 레이블을 반드시 명시해야 합니다.
👉 add(10, 20)가 아니라 add(x: 10, y: 20)처럼 레이블을 포함해야 합니다.
✅ 올바른 코드:
print(add(x: 10, y: 20)) // ✅ 정상 작동
📌 정리
1. Swift에서는 함수 호출 시 매개변수 레이블을 명시해야 함
• add(10, 20) → ❌ 오류 발생
• add(x: 10, y: 20) → ✅ 정상 작동
2. 함수 타입 확인 가능
• print(type(of: add)) → 출력: (Int, Int) -> Int
📌 결론
Swift에서는 가독성을 위해 함수 호출 시 매개변수 레이블을 명시해야 한다는 점을 기억하세요! 🚀
🔶 매개변수 레이블과 타입 확인
// 첫 번째 함수: 매개변수 레이블 수정
func add(xx x: Int, yy y: Int) -> Int {
return x + y
}
print(add(x: 10, y: 20)) // ❌ 오류 발생
// 매개변수 레이블에 맞춰 호출
print(add(xx: 10, yy: 20)) // ✅ 정상 작동
print(type(of: add)) // 출력: (Int, Int) -> Int
✅ 오류 발생 원인
func add(xx x: Int, yy y: Int) -> Int {
return xx + yy
}
print(add(xx: 10, yy: 20)) // ✅ 정상 작동
print(type(of: add)) // 출력: (Int, Int) -> Int
📌 오류 발생 주의
• func add(xx x: Int, yy y: Int)에서 xx와 yy는 매개변수 레이블입니다.
• 레이블은 함수 호출 시 사용되며, 내부에서 사용되는 변수 이름과는 구분됩니다.
주요 사항:
1. xx와 yy는 함수 내부에서 사용하는 변수 이름입니다.
2. x와 y는 매개변수 레이블이며, 함수 호출 시 필수로 사용해야 합니다.
📌 정리:
1. 매개변수 레이블과 변수 이름은 다르다
• 함수 내부에서 xx와 yy는 내부 변수명으로 사용되지만, 함수 호출 시에는 레이블 xx와 yy를 사용해야 합니다.
2. 오류 발생 이유:
print(add(x: 10, y: 20)) // ❌ 오류 발생
위 코드에서는 매개변수 레이블을 잘못 사용한 것입니다. xx와 yy가 매개변수 레이블이므로 호출할 때도 그에 맞는 레이블을 사용해야 합니다.
✅ 결론:
• 매개변수 레이블을 정확하게 사용하지 않으면 오류가 발생합니다.
• 함수 내부에서 사용하는 변수 이름(xx, yy)과 호출 시 사용하는 매개변수 레이블(x, y)은 다르게 처리되기 때문에 주의해야 합니다. 🚀
🔶 외부 매개변수명과 내부 매개변수명
// 함수 정의
func add(x: Int, y: Int) -> Int {
return x + y
}
// 함수 호출
add(x: 10, y: 20) // ✅ 정상 작동
• 외부 매개변수명은 함수 호출 시 사용되는 레이블입니다.
• 내부 매개변수명은 함수 내부에서 사용되는 변수명입니다.
기본적으로, Swift에서 매개변수의 외부 레이블은 매개변수 이름과 동일하게 사용됩니다. 위 코드에서 x와 y는 외부 매개변수명과 내부 매개변수명이 동일합니다.
✅ 외부 매개변수명 생략하기
외부 매개변수명을 생략하면, 내부 매개변수명이 외부 매개변수명까지 겸하게 됩니다. 예를 들어:
// 매개변수 레이블 생략
func add(_ x: Int, _ y: Int) -> Int {
return x + y
}
// 함수 호출
add(10, 20) // ✅ 정상 작동
• 이 경우, add(_ x: Int, _ y: Int)처럼 매개변수 레이블 앞에 **언더바(_)**를 사용하여 외부 매개변수명을 생략할 수 있습니다.
• 함수 호출 시 add(10, 20)처럼 레벨을 생략한 채 직접 전달할 수 있습니다.
✅ 정리
1. 외부 매개변수명과 내부 매개변수명:
• 기본적으로 외부 매개변수명은 내부 매개변수명과 동일합니다.
• 함수 호출 시 외부 매개변수명(x, y)을 사용해야 합니다.
2. 외부 매개변수명 생략:
• 매개변수 앞에 **언더바(_)**를 사용하여 외부 매개변수명을 생략하면, 함수 호출 시 외부 매개변수명 없이 직접 값만 전달할 수 있습니다.
✅ 예시:
1. 외부 매개변수명과 내부 매개변수명이 동일:
func add(x: Int, y: Int) -> Int {
return x + y
}
add(x: 10, y: 20)
2. 외부 매개변수명 생략:
func add(_ x: Int, _ y: Int) -> Int {
return x + y
}
add(10, 20)
이렇게 외부 매개변수명을 생략하면 코드가 더 간결해지고, 함수 호출 시 불필요한 레이블을 제거할 수 있습니다. 😊
✅ 매개변수 레이블 (Argument Label) 활용 예시
// 1. 매개변수 레이블을 명시적으로 지정
func add(first x: Int, second y: Int) -> Int {
return x + y
}
print(add(first: 5, second: 10)) // 5 + 10 = 15
// 2. 기본 매개변수 레이블을 사용하는 경우
func add(x: Int, y: Int) -> Int {
return x + y
}
print(add(x: 10, y: 20)) // 10 + 20 = 30
// 3. 외부 매개변수명 생략 (언더바(_) 사용)
func add(_ x: Int, _ y: Int) -> Int {
return x + y
}
print(add(1, 2)) // 1 + 2 = 3
// 4. 특정 매개변수에만 다른 레이블 지정
func add(_ x: Int, with y: Int) -> Int {
return x + y
}
print(add(5, with: 3)) // 5 + 3 = 8
✅ 매개변수 레이블 정리
1. 매개변수 레이블을 명시적으로 지정:
함수 선언에서 first와 second와 같은 레이블을 사용하여 함수 호출 시 각 매개변수의 의미를 명확히 전달할 수 있습니다.
func add(first x: Int, second y: Int) -> Int { ... }
print(add(first: 5, second: 10))
2. 기본 매개변수 레이블 사용:
매개변수 레이블을 별도로 지정하지 않으면, 매개변수 이름을 그대로 레이블로 사용합니다.
func add(x: Int, y: Int) -> Int { ... }
print(add(x: 10, y: 20))
3. 외부 매개변수명 생략:
_를 사용하여 외부 매개변수명을 생략할 수 있습니다. 이 경우 함수 호출 시 매개변수명 없이 값만 전달합니다.
func add(_ x: Int, _ y: Int) -> Int { ... }
print(add(1, 2))
4. 특정 매개변수에 다른 레이블 사용:
외부 매개변수명에 특별한 이름을 부여할 수도 있습니다. 예를 들어 with 같은 단어를 사용하여 두 번째 매개변수를 구별할 수 있습니다.
func add(_ x: Int, with y: Int) -> Int { ... }
print(add(5, with: 3))
✅ 결론
• 매개변수 레이블은 함수 호출 시 인자의 의미를 명확하게 전달하는 데 도움을 줍니다.
• 외부 매개변수명 생략은 코드가 더 간결해지지만, 가독성에 영향을 줄 수 있으므로 상황에 맞게 사용하는 것이 중요합니다.
🔶 #function을 사용하여 함수명 출력하기
Swift에서는 외부 매개변수명을 다르게 지정하여 이름이 동일한 함수 여러 개를 정의할 수 있습니다. 이를 통해 매개변수 레이블에 따라 함수의 기능을 구분할 수 있으며, 함수 오버로딩을 지원합니다.
// 1. 매개변수 레이블을 명시적으로 지정한 함수
func add(first x: Int, second y: Int) -> Int {
print(#function) // 함수명이 출력됨
return x + y
}
print(add(first: 5, second: 10)) // 함수 호출
// 출력: add(first:second:)
// 2. 기본 매개변수 레이블 사용한 함수
func add(x: Int, y: Int) -> Int {
print(#function) // 함수명이 출력됨
return x + y
}
print(add(x: 10, y: 20)) // 함수 호출
// 출력: add(x:y:)
// 3. 외부 매개변수명 생략한 함수
func add(_ x: Int, _ y: Int) -> Int {
print(#function) // 함수명이 출력됨
return x + y
}
print(add(1, 2)) // 함수 호출
// 출력: add(_:_:)
// 4. 특정 매개변수에 다른 레이블 지정한 함수
func add(_ x: Int, with y: Int) -> Int {
print(#function) // 함수명이 출력됨
return x + y
}
print(add(5, with: 3)) // 함수 호출
// 출력: add(_:with:)
✅ 외부 매개변수명으로 함수 오버로딩 가능
• 외부 매개변수명이 다르면 같은 함수명이라도 다른 함수로 간주됩니다.
• Swift에서는 매개변수 레이블이 다르면 동일한 함수 이름을 여러 번 정의할 수 있기 때문에, 매개변수의 의미나 용도에 따라 함수 이름을 쉽게 구분할 수 있습니다.
✅ 출력 결과 예시
1. add(first: second:) - 첫 번째 함수가 호출됨.
2. add(x: y:) - 두 번째 함수가 호출됨.
3. add(_: _:) - 외부 매개변수명을 생략한 함수가 호출됨.
4. add(_: with:) - with 레이블이 추가된 함수가 호출됨.
✅ numberOfRowsInSection 함수
• @MainActor: 이 속성은 메서드가 UI 작업을 다룬다는 것을 나타냅니다. 즉, 이 메서드는 메인 스레드에서 실행되어야 하며 UI 업데이트와 관련된 작업이므로, UI 관련 코드에서 안전하게 호출될 수 있도록 보장합니다.
• tableView: UITableView: UITableView 객체 자체를 전달받습니다. 이 객체는 어떤 테이블 뷰에서 호출되었는지 알려줍니다.
• numberOfRowsInSection section: Int: 이 매개변수는 테이블 뷰의 섹션 인덱스를 나타냅니다. 이 값을 통해 특정 섹션에 몇 개의 행이 표시될지 알 수 있습니다.
• -> Int: 이 함수는 Int 타입을 반환합니다. 반환값은 주어진 섹션에 표시할 행의 개수입니다.
🔷 클래스(class)
✅ 클래스에서 프로퍼티 초기화 방법
클래스의 프로퍼티는 반드시 초기화를 해줘야 사용이 가능합니다. 만약 초기화되지 않은 프로퍼티를 사용하려 하면 컴파일 에러가 발생합니다. 프로퍼티 초기화 방법은 크게 3가지로 나눌 수 있습니다.
1. 초기값을 직접 지정하기
가장 간단한 방법은 프로퍼티 선언 시 초기값을 설정하는 것입니다.
class Man {
var age: Int = 0
}
위 코드에서는 age라는 프로퍼티가 **0**으로 초기화됩니다. 이렇게 초기값을 직접 설정하면 별도로 init 메서드를 정의하지 않아도 됩니다.
2. 옵셔널 변수로 선언하기
초기값을 지정할 필요가 없다면, 옵셔널 변수로 선언할 수 있습니다. 옵셔널 변수는 기본적으로 **nil**로 초기화됩니다.
class Man {
var age: Int?
}
옵셔널 변수는 값이 없을 때 **nil**을 기본값으로 가지므로, 꼭 초기값을 지정하지 않아도 됩니다. 이 방법은 값이 유동적인 경우에 유용합니다.
3. init을 이용해 초기화하기
클래스 내에서 init 메서드를 이용하여 프로퍼티를 초기화할 수도 있습니다. 이 방법은 클래스 인스턴스를 만들 때 값을 설정할 수 있는 방법입니다.
class Man {
var age: Int
init(age: Int) {
self.age = age
}
}
이 예제에서는 init(age:) 초기화 메서드를 통해 Man 객체를 생성할 때 나이를 설정할 수 있습니다.
에러 발생 예시
초기값이 없는 프로퍼티를 그냥 선언만 하고 사용하려 하면 컴파일 에러가 발생합니다. 예를 들어, 아래와 같이 age 프로퍼티에 초기값을 설정하지 않으면 에러가 발생합니다.
class Man {
var age: Int
}
let man = Man() // 에러 발생: 'age' 프로퍼티는 초기화되지 않았습니다.
이렇게 초기값을 주지 않으면 반드시 init 메서드로 초기화하거나 옵셔널로 선언해야 합니다.
✅ 정리
클래스의 프로퍼티는 초기화되지 않으면 사용할 수 없다는 점을 기억하고, 위에서 소개한 3가지 방법으로 초기화해주면 됩니다! 😊
• 초기값 지정: 프로퍼티 선언 시 기본값 설정.
• 옵셔널 변수 사용: 기본값 없이 nil로 초기화.
• init 메서드 사용: 객체 생성 시 값을 설정.
이 방법들을 활용하면 에러 없이 클래스 프로퍼티를 잘 초기화할 수 있습니다!
🔶 클래스와 초기화 메서드 (Initializer)
클래스를 정의할 때, 인스턴스를 생성할 수 있는 초기화 메서드(init)를 제공할 수 있습니다. 이 초기화 메서드는 인스턴스를 생성하면서 클래스의 프로퍼티에 값을 설정해줍니다. init 메서드는 클래스의 프로퍼티를 초기화하는 중요한 역할을 합니다.
1. 기본 프로퍼티 초기화
class Man {
var age: Int = 1
var weight: Double = 3.5
func display() { // 인스턴스 메서드
print("나이=\(age), 몸무게=\(weight)")
}
}
var cho = Man() // 초기화된 값으로 인스턴스 생성
cho.display() // 나이=1, 몸무게=3.5
위 코드에서는 클래스 Man이 선언되고, age와 weight 프로퍼티에 초기값이 설정되어 있습니다. 이때, init 메서드를 따로 정의하지 않더라도 자동으로 기본 초기값으로 초기화됩니다.
2. 커스텀 초기화 메서드 사용
init 메서드를 사용하여 커스텀 초기화를 할 수 있습니다. init 메서드를 통해 클래스의 프로퍼티를 인스턴스를 생성하면서 설정할 수 있습니다.
class Man {
var age: Int = 1
var weight: Double = 3.5
func display() {
print("나이=\(age), 몸무게=\(weight)")
}
init(yourAge: Int, yourWeight: Double) {
age = yourAge
weight = yourWeight
}
}
var cho = Man(yourAge: 20, yourWeight: 50.5) // 초기값 전달
cho.display() // 나이=20, 몸무게=50.5
위 예제에서는 init(yourAge:yourWeight:) 초기화 메서드를 정의하여, 인스턴스를 생성할 때 나이와 몸무게를 매개변수로 전달받아 설정할 수 있습니다. 이를 통해 초기값을 자유롭게 지정할 수 있습니다.
3. self 키워드로 초기화
init 메서드에서 매개변수 이름과 프로퍼티 이름이 같을 때, self 키워드를 사용하여 매개변수와 프로퍼티를 명확하게 구분할 수 있습니다. 이를 통해 혼동을 피하고, 클래스의 프로퍼티에 값을 할당할 수 있습니다.
class Man {
var age: Int = 1
var weight: Double = 3.5
func display() {
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight: Double) {
self.age = age // 매개변수와 프로퍼티 이름이 같을 때는 'self'로 구분
self.weight = weight
} //designated initializer
}
var cho = Man(age: 20, weight: 50.5) // 초기값 전달
cho.display() // 나이=20, 몸무게=50.5
• self는 클래스의 프로퍼티를 나타내며, 매개변수와 프로퍼티 이름이 같을 때 이를 구분하는 데 사용됩니다.
• Designated Initializer는 클래스의 주요 초기화 메서드로, 클래스의 모든 프로퍼티를 초기화하는 역할을 합니다. self를 사용해 모든 프로퍼티를 설정할 수 있습니다.
✅ 정리
• 클래스의 프로퍼티는 초기화되지 않으면 사용할 수 없습니다.
• 초기화 메서드(init)를 사용하여 인스턴스를 생성할 때 프로퍼티에 값을 설정할 수 있습니다.
• **self**를 사용하면, 매개변수와 클래스 프로퍼티의 이름이 동일할 때 구분할 수 있습니다.
• 기본적으로 제공되는 초기화 외에도 커스텀 초기화를 통해 클래스를 더 유연하게 활용할 수 있습니다.
이렇게 클래스에서 초기화 메서드를 잘 활용하면, 객체를 보다 세밀하게 관리하고 다양한 형태의 인스턴스를 만들 수 있습니다!
🔶 상속 (Inheritance)
상속은 자식 클래스가 부모 클래스의 속성과 메서드를 상속받아 재사용하거나 확장하는 개념입니다. Swift에서는 class를 사용하여 상속을 구현할 수 있습니다.
class Man {
var age: Int = 1
var weight: Double = 3.5
func display() {
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight: Double) {
self.age = age
self.weight = weight
}
}
class Student: Man {
// Student 클래스는 Man 클래스를 상속받습니다.
}
var kim: Student = Student(age: 25, weight: 55.5)
kim.display() // 나이=25, 몸무게=55.5
var cho = Man(age: 20, weight: 50.5)
cho.display() // 나이=20, 몸무게=50.5
• Student 클래스는 Man 클래스를 상속받았습니다. 이로 인해 Student는 Man 클래스의 age, weight, display() 메서드를 그대로 사용할 수 있습니다.
• 상속된 클래스는 부모 클래스의 초기화 메서드도 사용할 수 있습니다. 위 코드에서 Student는 Man 클래스의 초기화 메서드를 호출하여 객체를 생성하고 있습니다.
🔶 super 키워드와 초기화
super는 자식 클래스에서 부모 클래스의 메서드나 초기화 메서드를 호출할 때 사용하는 키워드입니다. 부모 클래스의 초기화 메서드를 명시적으로 호출해야만 자식 클래스의 객체가 제대로 초기화됩니다.
1. super를 사용하여 부모 클래스 초기화 호출
class Man {
var age: Int = 1
var weight: Double = 3.5
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) // 부모 클래스의 초기화 메서드 호출
}
}
var lee: Student = Student(age: 20, weight: 65.2, name: "홍길동")
lee.displayS() // 이름=홍길동, 나이=20, 몸무게=65.2
lee.display() // 나이=20, 몸무게=65.2
• super.init(age: weight:): 자식 클래스인 Student에서 부모 클래스인 Man의 초기화 메서드를 super.init을 사용하여 호출하고 있습니다.
• 자식 클래스에서 지정된 초기화 메서드를 구현할 때는 반드시 부모 클래스의 초기화 메서드를 호출해야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다.
2. super.init 호출 누락으로 인한 에러 발생
class Man {
var age: Int = 1
var weight: Double = 3.5
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 호출 누락
} // 에러 발생: 'super.init' isn't called on all paths before returning from initializer
}
var lee: Student = Student(age: 20, weight: 65.2, name: "홍길동")
lee.displayS() // 에러 발생
lee.display()
“super.init isn’t called on all paths before returning from initializer”
이 에러는 자식 클래스에서 부모 클래스의 초기화 메서드를 호출하지 않아서 발생합니다. 자식 클래스의 초기화 메서드 내에서 반드시 부모 클래스의 초기화 메서드를 호출해야 합니다.
🔶 override 에러 발생
override는 자식 클래스에서 부모 클래스의 메서드를 재정의할 때 사용하는 키워드입니다. 만약 부모 클래스에서 정의된 메서드를 자식 클래스에서 **override**하지 않고 이름만 같은 메서드를 만들면 에러가 발생할 수 있습니다.
super.init 호출 누락으로 인한 오류
class Man {
var age: Int = 1
var weight: Double = 3.5
func display() {
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight: Double) {
self.age = age
self.weight = weight
}
}
class Student: Man {
var name: String
// 부모 클래스의 display() 메서드를 재정의하려고 할 때 에러 발생
func display() {
print("이름=\(name), 나이=\(age), 몸무게=\(weight)")
}
init(age: Int, weight: Double, name: String) {
self.name = name
// 부모 클래스의 init 호출을 하지 않으면 에러 발생
super.init(age: age, weight: weight) // 반드시 super.init을 호출해야 합니다
}
}
var lee: Student = Student(age: 20, weight: 65.2, name: "홍길동")
lee.display() // 이름=홍길동, 나이=20, 몸무게=65.2
자식 클래스에서 부모 클래스의 초기화 메서드를 호출하지 않으면 컴파일 오류가 발생합니다. 부모 클래스의 초기화 메서드를 반드시 호출해야 객체가 제대로 초기화됩니다.
✅ 핵심 정리:
• 상속을 통해 자식 클래스는 부모 클래스의 속성과 메서드를 물려받아 재사용할 수 있습니다.
• 자식 클래스에서 super.init을 호출하여 부모 클래스의 초기화 메서드를 명시적으로 호출해야만 객체가 제대로 초기화됩니다.
• 자식 클래스에서 부모 클래스의 **메서드를 override**하여 재정의할 때는 반드시 override 키워드를 사용해야 하며, super를 사용해 부모 클래스 메서드를 호출할 수 있습니다.
🔷 프로토콜(Protocol)
✅ 프로토콜 정의하기
protocol Runnable {
var x: Int { get set } // 속성 요구사항
func run() // 메서드 요구사항
}
프로토콜은 메서드와 속성의 요구사항을 정의합니다. 이 프로토콜을 채택한 타입은 반드시 요구된 속성과 메서드를 구현해야 합니다.
✅ 프로토콜 채택 및 구현
class Man: Runnable {
var x: Int = 1 // 프로토콜에서 요구한 속성 구현
func run() {
print("Runnnn~~~") // 프로토콜에서 요구한 메서드 구현
}
}
var kim = Man()
kim.run() // "Runnnn~~~" 출력
위 코드에서 Man 클래스는 Runnable 프로토콜을 채택합니다. 이 클래스는 x라는 속성과 run() 메서드를 구현해야 하며, 이를 통해 Runnable 프로토콜을 준수합니다.
✅ 에러 발생 예시
protocol Runnable {
var x: Int { get set }
func run()
}
class Man: Runnable {
// 에러 발생: "Type 'Man' does not conform to protocol 'Runnable'"
// 'x' 속성 및 'run()' 메서드를 구현해야 합니다.
}
위와 같이 Man 클래스에서 x 속성과 run() 메서드를 구현하지 않으면, 프로토콜을 준수하지 않아 컴파일 에러가 발생합니다.
✅ 프로토콜을 준수한 클래스
protocol Runnable {
var x: Int { get set }
func run()
}
class Man: Runnable {
var x: Int = 1
func run() {
print("Runnnn~~~")
}
}
var kim = Man()
kim.run() // "Runnnn~~~" 출력
이제 Man 클래스는 Runnable 프로토콜을 준수하므로 정상적으로 동작합니다.
📌 정리
• 프로토콜은 메서드와 속성 요구사항을 정의합니다.
• 프로토콜을 채택한 클래스는 반드시 해당 요구사항을 구현해야 하며, 그렇지 않으면 컴파일 에러가 발생합니다.
• 에러 해결을 위해서는 요구된 속성과 메서드를 반드시 구현해야 합니다.
프로토콜을 사용하면 코드의 일관성과 재사용성을 높일 수 있습니다. 🎉
🔶 상속과 프로토콜을 동시에 사용하는 예제
우리는 동물이라는 기본 클래스와 직원이라는 프로토콜을 정의하고, 그 후 강아지와 고양이 클래스가 상속을 받고, 강아지와 고양이가 모두 직원 프로토콜을 채택하여 각자의 역할을 다하는 예제를 만들겠습니다.
✅ 프로토콜 정의
먼저, 직원이라는 프로토콜을 정의해보겠습니다. 이 프로토콜은 이름과 직책이라는 속성을 요구하고, 일하다()라는 메서드를 요구합니다.
protocol Employee {
var name: String { get set }
var position: String { get set }
func work()
}
• name: 직원의 이름
• position: 직원의 직책
• work(): 직원이 하는 일에 대한 메서드
✅ 상속을 위한 동물 클래스 정의
그다음, Animal이라는 기본 클래스를 만들겠습니다. Animal 클래스는 name과 sound라는 속성, 그리고 makeSound()라는 메서드를 가집니다.
class Animal {
var name: String
var sound: String
init(name: String, sound: String) {
self.name = name
self.sound = sound
}
func makeSound() {
print("\(name) makes \(sound) sound.")
}
}
• name: 동물의 이름
• sound: 동물이 내는 소리
• makeSound(): 동물이 소리를 내는 메서드
✅ 강아지와 고양이 클래스 정의 (상속과 프로토콜 채택)
이제 Animal 클래스를 상속받고, Employee 프로토콜을 채택한 Dog와 Cat 클래스를 정의하겠습니다.
class Dog: Animal, Employee {
var position: String
init(name: String, sound: String, position: String) {
self.position = position
super.init(name: name, sound: sound)
}
func work() {
print("\(name) is guarding the house.")
}
}
class Cat: Animal, Employee {
var position: String
init(name: String, sound: String, position: String) {
self.position = position
super.init(name: name, sound: sound)
}
func work() {
print("\(name) is catching mice.")
}
}
• Dog 클래스와 Cat 클래스는 모두 Animal 클래스를 상속받고, Employee 프로토콜을 채택하여 work() 메서드를 구현합니다.
• Dog는 집을 지키는 역할을 하고, Cat은 쥐를 잡는 역할을 합니다.
✅ 사용 예시
이제 이 클래스를 사용하여 각 동물들의 동작을 출력해보겠습니다.
let dog = Dog(name: "Buddy", sound: "bark", position: "Guard Dog")
dog.makeSound() // "Buddy makes bark sound."
dog.work() // "Buddy is guarding the house."
let cat = Cat(name: "Whiskers", sound: "meow", position: "Mouse Catcher")
cat.makeSound() // "Whiskers makes meow sound."
cat.work() // "Whiskers is catching mice."
📌 설명
1. 상속 (Inheritance):
• Dog와 Cat 클래스는 Animal 클래스를 상속받아 name, sound 등의 속성 및 makeSound() 메서드를 물려받습니다. 이로 인해 Dog와 Cat은 Animal의 기능을 재사용하면서, 각각 고유한 특성을 추가할 수 있습니다.
2. 프로토콜 (Protocol):
• Dog와 Cat 클래스는 Employee 프로토콜을 채택하여 position과 work() 메서드를 구현해야 합니다. 이를 통해 Dog와 Cat은 각각 직원으로서 역할을 다하고, work() 메서드를 통해 각자의 일을 수행합니다.
📌 결과
• Dog와 Cat은 공통된 Animal 클래스에서 상속받은 기능을 사용하면서, Employee 프로토콜을 통해 자신의 역할(일)을 정의하게 됩니다.
• 이 방식으로 상속과 프로토콜을 동시에 사용하여 코드의 재사용성과 확장성을 높일 수 있습니다.
이 예제처럼, 상속은 기본적인 속성 및 기능을 물려받고, 프로토콜은 클래스가 반드시 구현해야 할 요구사항을 정의하는 데 유용합니다. 이 두 가지를 결합하여, 더욱 유연하고 구조적인 코드를 만들 수 있습니다!