웹사이트 검색

신속한 초기화()


이 Swift 튜토리얼에서는 Swift 초기화 또는 Swift 초기화라는 중요한 개념에 대해 논의할 것입니다. 초기화는 어떤 유형의 인스턴스를 만들 때 발생합니다.

신속한 초기화()

초기화는 사용할 클래스, 구조체 또는 열거형의 인스턴스를 준비하는 프로세스입니다. 이 프로세스에는 해당 인스턴스의 각 저장된 속성에 대한 초기 값을 설정하고 새 인스턴스를 사용할 준비가 되기 전에 필요한 기타 설정 또는 초기화를 수행하는 작업이 포함됩니다.

이니셜라이저는 Java 프로그래밍의 생성자와 비슷합니다. Type-safe 언어인 Swift는 이니셜라이저에 대해 많은 규칙을 적용했습니다. 개념을 잘 파악하지 않으면 구현하기가 까다로울 수 있습니다.

신속한 초기화() 구문

init() {
    // initialise the stored properties here.
}

아래의 샘플 클래스를 살펴보겠습니다.

class A{
    
    //Compilation error. No initializer is defined.
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
}

위의 클래스는 컴파일되지 않습니다. Swift 컴파일러는 저장된 속성이 초기화되지 않았다고 불평합니다. 저장 속성은 결정되지 않은 상태로 유지할 수 없습니다. 따라서 두 가지 가능한 옵션이 있습니다.

  1. 속성 정의 자체에 기본 속성 값을 할당합니다.
  2. 속성을 초기화하기 위해 init() 초기화 프로그램을 사용하십시오.

각 접근 방식을 한 번에 하나씩 살펴보겠습니다.

class A{
    
    var a : Int = 5
    var b : String = "Hello. How you're doing"
    var c : Int?
    let website = "JournalDev"
}

여기서 우리는 저장된 속성 각각에 대한 기본값을 설정했습니다. 따라서 Swift는 암시적으로 기본 초기화를 제공합니다. 클래스가 초기화되면 클래스의 인스턴스에 대해 도트 연산자를 사용하여 모든 속성과 함수에 액세스할 수 있습니다.

var object = A()
object.a = 10
object.c = 2

두 번째 방법은 아래와 같이 init() 메서드를 사용하여 저장된 속성을 초기화하는 것입니다.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(a: Int, b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(a: 5, b: "Hello World")

참고: Swift Optional은 저장된 속성이 아닙니다. 따라서 초기화할 필요가 없습니다. 저장된 속성은 self 속성을 사용하여 init() 메서드 내에서 액세스됩니다. 참고: self는 자체 인스턴스 메소드 내에서 현재 인스턴스를 참조하는 데 사용됩니다(자바의 this와 유사). 위의 이니셜라이저는 클래스의 기본 이니셜라이저입니다. 지정 초기화라고도 합니다(나중에 설명하겠습니다). 이니셜라이저를 사용하면 상수 속성도 수정할 수 있습니다.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website : String
    
    init(a: Int, b: String, website: String) {
        self.a = a
        self.b = b
        self.website = website
    }
}

var object = A(a: 5,b: "Hello World", website: "JournalDev")

구조체의 멤버별 초기화 프로그램

구조체는 값 유형이므로 반드시 정의된 이니셜라이저가 필요하지 않습니다. 구조체 유형은 사용자 지정 초기화를 정의하지 않은 경우 멤버별 초기화를 자동으로 받습니다. 다음은 구조체를 초기화하는 다양한 방법을 설명하는 코드 스니펫입니다.

struct Rect{
    var length : Int
    var breadth : Int
}
var r = Rect(length: 5, breadth: 10)
struct Rect{
    var length : Int = 5
    var breadth : Int = 10
}

var r = Rect()
var r1 = Rect(length: 10, breadth: 5)

위 스니펫에서 저장된 속성에 기본값을 할당했기 때문에 멤버 초기화와 함께 멤버 초기화 없이 기본 초기화를 받습니다.

struct Rect{
    var length : Int
    var breadth : Int
    
    init(length: Int, breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(length: 10, breadth: 5)

위의 경우, 우리는 우리만의 커스텀 이니셜라이저를 정의했습니다. 외부 이름 없이 매개변수 사용 초기화에 외부 이름이 필요하지 않은 경우 아래와 같이 밑줄 '_'을 사용하여 동일함을 나타냅니다.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(_ a: Int, _ b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(5,"Hello World")
struct Rect{
    var length : Int
    var breadth : Int
    
    init(_ length: Int, _ breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(10, 10)

Swift 이니셜라이저의 유형

클래스의 이니셜라이저는 다음 유형으로 크게 분류할 수 있습니다.

  1. 지정된 이니셜라이저: 클래스의 기본 이니셜라이저입니다. 슈퍼클래스 이니셜라이저를 호출하기 전에 해당 클래스에 의해 도입된 모든 속성을 완전히 초기화해야 합니다. 클래스는 둘 이상의 지정된 초기화 프로그램을 가질 수 있습니다. 모든 클래스에는 지정된 이니셜라이저가 하나 이상 있어야 합니다.
  2. 편리한 이니셜라이저: 클래스에 대한 이니셜라이저를 지원하는 보조입니다. 동일한 클래스의 지정된 초기화 프로그램을 호출해야 합니다. 이는 선택 사항이며 사용자 지정 설정에 사용할 수 있습니다. 동일한 스타일로 작성되지만 init 키워드
  3. 앞에 convenience 수정자가 있습니다.\n

class Student{
    
    var name : String
    var degree : String
    
    init(name : String, degree: String) {
        self.name = name
        self.degree = degree
    }
    
    convenience init()
    {
        self.init(name: "Unnamed", degree: "Computer Science")
    }
    
}
var student = Student()
student.degree // "Computer Science"
student.name // "Unnamed"

Convenience Initializers는 저장된 속성에 기본값을 할당할 때 유용합니다.

값 유형에 대한 신속한 초기화 위임

다른 초기화 프로그램에서 초기화 프로그램을 호출하여 코드 중복을 피할 수 있습니다. 구조와 같은 값 유형은 상속을 지원하지 않습니다. 따라서 가능한 유일한 방법은 동일한 구조 내에서 초기화 프로그램을 호출하는 것입니다. 아래에 예가 나와 있습니다.

struct Rect{
    var length : Int
    var breadth : Int
    
    init(_ length: Int, _ breadth: Int) {
        self.length =  length
        self.breadth = breadth
    }
    
    init(_ length: Int)
    {
        self.init(length, length)
    }
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15

참조 유형에 대한 Swift 초기화 위임

참조 유형인 클래스는 상속을 지원합니다. 따라서 이니셜라이저는 슈퍼클래스에서 다른 이니셜라이저를 호출할 수 있으므로 모든 값을 올바르게 상속하고 초기화할 책임이 추가됩니다. 다음은 이니셜라이저 간의 관계를 처리하기 위해 정의된 기본 규칙입니다.

  • 지정 이니셜라이저는 직속 슈퍼클래스에서 지정 이니셜라이저를 호출해야 합니다.
  • 편의 초기화는 동일한 클래스에서 다른 초기화를 호출해야 합니다.
  • 편의 초기화는 궁극적으로 지정된 초기화를 호출해야 합니다.

다음 그림은 위의 규칙을 설명합니다.

지정된 이니셜라이저는 항상 위임해야 합니다. 편의 이니셜라이저는 항상 위임해야 합니다. super 키워드는 하위 클래스의 편리한 초기화에 사용할 수 없습니다.

Swift Initializer 상속 및 재정의

Swift의 하위 클래스는 특정 조건이 충족되지 않는 한 기본적으로 상위 클래스의 초기화를 상속하지 않습니다(Automatic Initializer Inheritance). 이는 하위 클래스에서 반만 초기화되는 것을 방지하기 위해 수행됩니다. 지정 및 편의 초기화가 상속을 통해 어떻게 작동하는지 살펴보겠습니다. 관련 하위 클래스에서 상속할 Vehicle 기본 클래스를 정의할 것입니다. Enum을 클래스의 유형으로 사용합니다. 기본 클래스인 Vehicle은 아래와 같이 정의됩니다.

enum VehicleType : String {
    case twoWheeler = "TwoWheeler"
    case fourWheeler = "FourWheeler"
}

class Vehicle{
    
    var vehicleType : VehicleType
    
    init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    
    convenience init()
    {
        self.init(vehicleType: .fourWheeler)
    }
}

var v = Vehicle(vehicleType: .twoWheeler)

참고: 편의 초기화는 self.init를 사용하여 동일한 클래스의 지정된 초기화를 호출해야 합니다. 아래와 같이 위 클래스의 하위 클래스를 정의해 보겠습니다.

enum TwoWheelerType : String
{
    case scooty = "Scooty"
    case bike = "Bike"
}

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
}

참고해야 할 중요 사항:

  • 서브클래스의 지정 이니셜라이저는 슈퍼클래스의 지정 이니셜라이저를 호출하기 전에 자체 속성을 초기화해야 합니다.
  • 하위 클래스는 super.init가 호출된 후에만 슈퍼 클래스의 상속된 속성을 수정할 수 있습니다.

다음 코드는 컴파일 시간 오류로 이어질 것입니다.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        self.vehicleType = vType //Won't compile
        super.init(vehicleType: vType)
        //self.vehicleType = .fourWheeler //This would work.
        
    }
}

var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)

앞서 설명한 것처럼 상위 클래스 초기화는 하위 클래스에서 자동으로 상속되지 않습니다. 따라서 아래 초기화는 실패합니다.

var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.

이니셜라이저를 재정의하려면 서브클래스 이니셜라이저가 슈퍼클래스의 지정된 이니셜라이저와 일치해야 합니다. 이 경우 override 키워드가 이니셜라이저에 추가됩니다.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override init(vehicleType: VehicleType)
    {
        print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: vehicleType)
    }



아래 이니셜라이저는 매개변수 이름이 다르기 때문에 상위 클래스의 이니셜라이저를 재정의하지 않습니다.

//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
    {
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: v)
    }

Convenience Initializer를 사용하여 수퍼 클래스의 초기화를 재정의합니다.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override convenience init(vehicleType: VehicleType) {
        self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
        self.vehicleType = vehicleType
    }
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)

//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler

Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler

편의 초기화 프로그램에는 override 키워드가 추가되어 있습니다. 같은 클래스의 지정된 이니셜라이저를 호출합니다. 참고: 키워드 convenienceoverride의 순서는 중요하지 않습니다.

필수 이니셜라이저

이니셜라이저 앞에 required 키워드를 작성하면 각 하위 클래스가 해당 이니셜라이저를 구현해야 함을 나타냅니다. 또한 required 한정자는 각각의 하위 클래스 구현에도 있어야 합니다. 위의 두 클래스에 대한 필수 초기화 프로그램의 예는 다음과 같습니다.

class Vehicle{
    
    var vehicleType : VehicleType
    
    required init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    
    convenience init()
    {
        self.init(vehicleType: .fourWheeler)
    }
}

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
     required init(vehicleType: VehicleType) {
        self.manufacturer = "Not Defined"
        self.twoWheelerType = .bike
        super.init(vehicleType: vehicleType)
    }
}

참고: 필수 수정자를 추가하면 이니셜라이저가 재정의됨을 나타냅니다. 따라서 위의 경우 override 키워드를 생략할 수 있습니다. Convenience와 함께 Required Initializer 사용 Required 및 Convenience Initializer는 서로 독립적이며 함께 사용할 수 있습니다. 필수편의성 수정자를 함께 사용하는 방법을 보여주기 위해 차량의 또 다른 하위 클래스를 만들어 보겠습니다.

enum FourWheelerType : String
{
    case car = "Car"
    case bus = "Bus"
    case truck = "Truck"
}


class FourWheeler : Vehicle
{
    var fourWheelerType : FourWheelerType
    var name : String
    
    init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
        self.fourWheelerType = fourWheelerType
        self.name = name
        print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
        super.init(vehicleType: vehicleType)
        self.vehicleType = vehicleType
    }
    
    required convenience init(vehicleType: VehicleType) {
        self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
    }
}


class Car : FourWheeler{
    
    var model : String
    
    init(model: String) {
        self.model = model
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
    }
    
    required init(vehicleType: VehicleType)
    {
        self.model = "Not defined"
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
    }
    
}

위의 코드 스니펫에서 주목해야 할 중요한 사항:

  • 편의 초기화는 클래스의 보조 초기화입니다.
  • 필요에 따라 편리한 초기화를 설정한다는 것은 하위 클래스에서 구현하는 것이 필수라는 것을 의미합니다.

자동 초기화 상속

하위 클래스가 자동으로 상위 클래스에서 초기화를 상속하는 두 가지 상황이 있습니다.

  • 하위 클래스에 지정된 초기화 프로그램을 정의하지 마세요.
  • 슈퍼클래스의 모든 지정된 이니셜라이저를 구현합니다. 모든 편의 초기화 프로그램도 자동으로 상속됩니다.

실행 중인 첫 번째 규칙은 아래 스니펫에 설명되어 있습니다.

class Name {
    
    var name: String
    
    init(n: String) {
        self.name = n
    }
}

class Tutorial: Name {
    
    var tutorial : String? = "Swift Initialization"
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")

작동 중인 두 번째 규칙은 아래 스니펫에 설명되어 있습니다.

class Name {
    
    var name: String
    
    init(n: String) {
        self.name = n
    }
    
    convenience init()
    {
        self.init(n: "No name assigned")
    }
}

class Tutorial: Name {
    
    var tutorial : String? = "Swift Tutorial"
    
    override init(n : String) {
        super.init(n: n)
    }
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned

상위 클래스의 편의 초기화 프로그램은 위 코드의 하위 클래스에서 자동으로 사용할 수 있습니다.

신속한 실패 가능 초기화 프로그램

초기화 프로세스가 실패할 때 트리거되는 클래스, 구조 또는 열거형에서 init? 키워드를 사용하여 실패 가능한 초기화를 정의할 수 있습니다. 초기화는 여러 가지 이유로 실패할 수 있습니다. 잘못된 매개변수 값, 외부 소스가 없는 경우 등. 실패할 수 있는 초기화 프로그램은 초기화하는 유형의 선택적 값을 만듭니다. 초기화 실패를 유발하기 위해 nil을 반환할 것입니다(init는 아무 것도 반환하지 않지만). 구조가 있는 실패할 수 있는 초기화 프로그램

struct SName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

var name = SName(name: "JournalDev")
if name != nil {
    print("init success") //this gets displayed
}
else{
    print("init failed")
}
name  = SName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed") //this gets displayed
}

열거형이 있는 실패할 수 있는 초기화 프로그램

enum CharacterExists {
    case A, B
    init?(symbol: Character) {
        switch symbol {
        case "A":
            self = .A
        case "B":
            self = .B
        default:
            return nil
        }
    }
}


let ch = CharacterExists(symbol: "C")
if ch != nil {
    print("Init failed. Character doesn't exist")
}
class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name  = CName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed")
}

참고: 실패할 수 있는 초기화와 실패할 수 없는 초기화는 동일한 매개변수 유형과 이름을 가질 수 없습니다.

실패할 수 있는 초기화 재정의

하위 클래스에서 실패할 수 있는 초기화를 재정의할 수 있습니다. 실패할 수 있는 이니셜라이저는 실패할 수 없는 이니셜라이저로 재정의할 수 있지만 그 반대의 경우는 불가능합니다. 실패할 수 없는 이니셜라이저로 실패할 수 있는 초기화를 재정의하는 예는 다음과 같습니다.

class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name  = CName(name: "")

class SubName : CName{
    
    var age : Int
    override init(name: String)
    {
        self.age = 23
        super.init(name: name)!  
    }
}

참고: 강제 언래핑은 하위 클래스의 실패할 수 없는 초기화 구현의 일부로 상위 클래스에서 실패할 수 있는 초기화를 호출하는 데 사용됩니다. 이것으로 swift initi 튜토리얼을 마칩니다. 참조 : Apple 문서