본문 바로가기

iOS 앱 개발자 프로젝트/Swift 문법 정복하기

[Swift] Inheritance, Initializer

2024년 3월 12일 화요일

클래스의 상속 개념과 객체를 만들기 위한 다양한 초기화 방법에 대해 공부해 봅니다.  


#1. inheritance (상속)

#2. initializer (초기화)


#1. inheritance (상속)

 

  • 클래스 간에 코드 및 속성을 공유하는 메커니즘을 제공: 기존 클래스에서 새로운 클래스를 만들고, 기존 클래스의 특성(속성과 메서드)을 재사용하면서 새로운 기능을 추가할 수 있도록 해준다.(서브 클래싱)
    1. 코드 재사용성: 기존 클래스의 특성을 재사용하여 중복을 피하고 유지보수성을 높일 수 있다.
    2. 계층 구조: 부모 클래스와 이를 상속받는 자식 클래스 간에 계층 구조를 형성하여 다양한 추상화와 분류 가능
  • override (재정의)
    • 부모 클래스에서 '상속받은' 메서드, 속성 또는 서브스크립트를 자식 클래스에서 다시 정의할 때 사용
    • 자식 클래스에서 부모 클래스의 메서드를 재정의하여 새로운 구현을 제공할 수 있다.
  • super (호출)
    • 자식 클래스에서 부모 클래스의 메서드, 속성 또는 초기화 메서드를 호출할 때 사용
    • 부모 클래스의 메서드를 호출하거나 부모 클래스의 초기화 메서드를 호출하는 데 사용
    • super.method() 또는 super.property와 같이 사용하여 부모 클래스의 기능을 호출
  • final
    • 클래스, 메서드, 속성 또는 서브스크립트를 표시하여 상속이 불가능하도록 만듭니다.
    • final 키워드가 클래스에 사용되면 해당 클래스는 상속될 수 없다.
    • 메서드, 속성, 서브스크립트에 사용될 경우, 해당 멤버들을 재정의(Override)할 수 없다.
// 부모 클래스(Person) 선언
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func greet() {
        print("Hello, my name is \(name).")
    }
}

// Person 클래스를 상속받는 자식 클래스(Student) 선언
class Student: Person {
    var studentID: Int

    init(name: String, age: Int, studentID: Int) {
        self.studentID = studentID
        super.init(name: name, age: age)
    }

    func study() {
        print("\(name) is studying.")
    }
}

// Student 클래스 인스턴스 생성 및 사용
let john = Student(name: "John", age: 20, studentID: 123)
john.greet() // 출력: Hello, my name is John.
john.study() // 출력: John is studying.



// override, super 키워드 예시

class Animal {
    func makeSound() {
        print("Some generic sound")
    }
}

class Dog: Animal {
    override func makeSound() {
        super.makeSound() // 부모 클래스의 메서드 호출
        print("Bark!")
    }
}

let dog = Dog()
dog.makeSound()


// final 키워드 예시

final class Vehicle {
    final var wheels: Int = 0
    
    final func makeSound() {
        print("Some generic sound")
    }
}

// Error: 'SubVehicle' cannot inherit from final class 'Vehicle'
class SubVehicle: Vehicle {
    // Error: 'wheels' cannot override 'final' var from superclass
    // override var wheels: Int = 4
    
    // Error: 'makeSound()' cannot override a final method
    // override func makeSound() {
    //     print("Custom sound")
    // }
}

 

 

 

#2. initializer (초기화)

 

    • 초기화와 생성자
      • 초기화는 클래스, 구조체, 또는 열거형의 인스턴스를 생성하고 속성을 초기화하여 사용할 수 있도록 하는 과정으로, 이 기능이 제공되는 것을 생성자(Constructor)라고 한다. 즉, 객체를 생성할 때 항상 실행되며, 맨 처음 실행되는 메소드를 의미한다. 

                →  객체가 메모리에 할당되고 속성이 적절히 설정되어 안정적으로 사용될 수 있도록 하는 중요한 단계

 

           ※  인스턴스란?

                   클래스, 구조체 또는 열거형과 같은 타입의 실제 예시

                    →  클래스나 구조체 등의 템플릿(틀)을 기반으로 생성된 실제 데이터 

                         ( = 클래스나 구조체를 기반으로 만들어진 객체 )

 

// Person 클래스 정의
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func greet() {
        print("Hello, my name is \\(name) and I'm \\(age) years old.")
    }
}

// Person 클래스의 인스턴스 생성
let john = Person(name: "John", age: 30)
let emily = Person(name: "Emily", age: 25)

// 생성된 인스턴스 사용
john.greet() // 출력: Hello, my name is John and I'm 30 years old.
emily.greet() // 출력: Hello, my name is Emily and I'm 25 years old.

 

 

 

★ 초기화 방법 ★

 

#1. 기본 초기화(Default Initialization)

  • Swift는 클래스의 속성이 기본 값으로 초기화되도록 지원 → 클래스의 모든 속성이 기본 값을 가지고 있을 때 자동 발생
  • 값과 타입이 모두 지정되어 있을 경우 별도의 초기화 작업을 해 주지 않아도 된다.
class Person {
    var name: String = ""
    var age: Int = 0
}

let person = Person() // 기본 초기화

 

 

#2.  지정 초기화(Designated Initialization)

  • init 키워드를 사용하여 클래스의 모든 속성을 초기화하는 메서드
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let person = Person(name: "John", age: 30) // 지정 초기화

 

 

#3.  편의 초기화(Convenience Initialization)

  • 기본 초기화 또는 지정 초기화를 간편하게 호출하는 보조 메서드
class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    convenience init(name: String) {
        self.init(name: name, age: 0)
    }
}

let person = Person(name: "Alice") // 편의 초기화 사용

 

 

#4.  강제 해제(Force Unwrapping)되지 않는 옵셔널 초기화

  • 옵셔널 타입을 가진 속성은 선언과 동시에 초기화되거나 나중에 값을 할당할 수 있다.
class Person {
    var name: String?
    var age: Int = 0
}

let person = Person()
person.name = "Emily"
person.age = 25

 

 

 

 실패 가능 생성자 (Failable Initializer)

  • 기존 생성자는 컴파일 시점에 모든 프로퍼티가 초기화 되어야 하기 때문에 초기화 실패의 경우, 컴파일 에러가 발생
  • 하지만 실패 가능 생성자는 초기화에 실패하더라도 에러가 발생하지 않고 nil을 리턴 : Optional한 생성자
// class의 실패 가능 생성자

class UserProfile {
    let username: String

    // 실패 가능 생성자: 유효하지 않은 이름을 사용할 경우 초기화 실패
    init?(username: String) {
        // 유효한 사용자 이름인지 확인
        guard username.count >= 5 else {
            return nil // 입력된 사용자 이름이 유효하지 않을 경우 초기화 실패
        }

        self.username = username
    }
}

// 실패 가능 생성자를 사용하여 인스턴스 생성
if let validProfile = UserProfile(username: "user123") {
    print("Valid username: \\(validProfile.username)")
} else {
    print("Invalid username. Username should be at least 5 characters long.")
}

if let invalidProfile = UserProfile(username: "user") {
    print("Valid username: \\(invalidProfile.username)")
} else {
    print("Invalid username. Username should be at least 5 characters long.")
}

// struct의 실패 가능 생성자

struct Animal {
    let name: String
    
    init?(name: String) {
        if name.isEmpty {
            return nil    // 생성자 내에서 실패 가능 부분에 nil을 리턴하면 됨
        }
        self.name = name
    }
}

let animal1 = Animal(name: "choco") //인스턴스 생성. 타입은 Animal? 이다

let animal2 = Animal(name: "") // 문자열이기에 유효한 타입이지만 nil이 리턴된다

// enum의 실패 가능 생성자

enum HeightUnit {
    case feet
    case centiMeter
    
    init?(symbol: String) {
        switch symbol {
        case "f":
            self = HeightUnit.feet
        case "cm":
            self = HeightUnit.centiMeter
        default:
            return nil
        }
    }
}

let feet: HeightUnit = HeightUnit.feet // HeightUnit 타입

let centi: HeightUnit? = HeightUnit(symbol: "cm") 

let feet2: HeightUnit? = HeightUnit(symbol: "F") // nil