빠르고 명시적인 예제에서 편의성 init와 init의 차이점은 무엇입니까?


82

둘 다의 차이점 또는 .NET Framework의 목적을 이해하는 데 어려움이 convenience init있습니다.


The Swift Programming Language 책 의 초기화 섹션 을 읽어 보셨습니까 ? 당신의 혼란은 정확히 무엇입니까?
rmaddy

1
이미 SO에서 사용할 수있는 답변이 있습니다. 참조하시기 바랍니다 - stackoverflow.com/questions/30896231/...
산토

답변:


105

표준 init:

지정된 이니셜 라이저는 클래스의 기본 이니셜 라이저입니다. 지정된 이니셜 라이저는 해당 클래스에 의해 도입 된 모든 속성을 완전히 초기화하고 적절한 수퍼 클래스 이니셜 라이저를 호출하여 수퍼 클래스 체인까지 초기화 프로세스를 계속합니다.

convenience init:

편의 이니셜 라이저는 보조 이니셜 라이저로 클래스에 대한 이니셜 라이저를 지원합니다. 지정된 이니셜 라이저의 일부 매개 변수를 기본값으로 설정하여 편의 이니셜 라이저와 동일한 클래스에서 지정된 이니셜 라이저를 호출하도록 편의 이니셜 라이저를 정의 할 수 있습니다. 또한 편의 이니셜 라이저를 정의하여 특정 사용 사례 또는 입력 값 유형에 대한 해당 클래스의 인스턴스를 만들 수 있습니다.

스위프트 문서

요컨대, 이것은 편리한 이니셜 라이저를 사용하여 지정된 이니셜 라이저를 더 빠르고 "편리하게"호출 할 수 있음을 의미합니다. 편의 초기화가의 사용이 필요 그래서 self.init대신의를 super.init당신은 지정된 초기화의 재정에 볼 수 있습니다.

의사 코드 예 :

init(param1, param2, param3, ... , paramN) {
     // code
}

// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
     self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}

주로 기본값을 가진 긴 이니셜 라이저가있는 커스텀 뷰를 만들 때 많이 사용합니다. 문서는 내가 할 수있는 것보다 더 잘 설명합니다. 확인해보세요!


73

편의 이니셜 라이저는 속성이 많은 클래스가있는 경우 항상 모든 변수로 위트를 초기화하는 것이 "고통 스럽습니다". 따라서 편의 이니셜 라이저로 수행하는 작업은 일부 변수를 전달하여 초기화하는 것입니다. 개체, 나머지는 기본값으로 할당합니다. Ray Wenderlich 웹 사이트에는 유료 계정이 있기 때문에 무료인지 아닌지 확실하지 않은 매우 좋은 비디오가 있습니다. 다음은 모든 변수를 사용하여 내 개체를 초기화하는 대신 제목을 지정하는 대신 확인할 수있는 예입니다.

struct Scene {
  var minutes = 0
}

class Movie {
  var title: String
  var author: String
  var date: Int
  var scenes: [Scene]

  init(title: String, author: String, date: Int) {
    self.title = title
    self.author = author
    self.date = date
    scenes = [Scene]()
  }

  convenience init(title:String) {
    self.init(title:title, author: "Unknown", date:2016)
  }

  func addPage(page: Scene) {
    scenes.append(page)
  }
}


var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer

4
좋은 예입니다. 이것은 개념을 명확하게했습니다. 감사합니다.
Arjun Kalidas

5
저자에 대한 기본값과 날짜 매개 변수를`init (title : String, author : String = "Unknown", date : Int = 2016) {self.title = title self.author = author self. date = date scenes = [Scene] ()}`그렇다면 왜 편리한 이니셜 라이저가 필요한가요?
shripad20

@ shripad20 같은 질문이 여기에 있습니다. 다른 이니셜 라이저를 만들어 기본 매개 변수를 보낼 수 있지만 편리함을 사용해야하는 이유를 찾았습니까? 답을 찾으면 알려주세요
user100

편의는 "main"self.init에 추가 되는 반면, 기본 매개 변수가있는 self.init 는 "main"self.init를 대체 합니다. hackingwithswift.com/example-code/language/…
user1105951

@ shripad20 편의 이니셜 라이저가 기본값을 능가하는 설명은 아래 예제를 확인하십시오 . 도움이 되었기를 바랍니다.
Chris Graf

14

다음은 Apple 개발자 포털 에서 가져온 간단한 예 입니다.

기본적으로 지정된 이니셜 라이저는이며 init(name: String)저장된 모든 속성이 초기화되도록합니다.

init()편의 이니셜, 인수를 복용하지를 automaticly 값 설정 name에 저장된 속성을 [Unnamed]지정 초기화를 이용하여.

class Food {
    let name: String

    // MARK: - designated initializer
    init(name: String) {
        self.name = name
    }

    // MARK: - convenience initializer
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food()               // name will be "[Unnamed]"

최소한 몇 개의 저장된 속성이있는 큰 클래스를 다룰 때 유용합니다. Apple 개발자 포털 에서 선택 사항 및 상속에 대해 자세히 읽어 보는 것이 좋습니다 .


6

편의 이니셜 라이저가 기본 매개 변수 값 설정을 능가하는 경우

나를 위해, convenience initializers단순히 클래스 속성에 대한 기본값을 설정하는 것보다 더 많은 일 이 있다면 유용합니다.

지정된 init ()로 클래스 구현

그렇지 않으면 init정의에 기본값을 설정합니다 . 예 :

class Animal {

    var race: String // enum might be better but I am using string for simplicity
    var name: String
    var legCount: Int

    init(race: String = "Dog", name: String, legCount: Int = 4) {
        self.race = race
        self.name = name
        self.legCount = legCount // will be 4 by default
    }
}

편리한 init ()를 사용한 클래스 확장

그러나 단순히 기본값을 설정하는 것보다 더 할 일이있을 수 있으며, 이것이 convenience initializers편리합니다.

extension Animal {
    convenience init(race: String, name: String) {
        var legs: Int

        if race == "Dog" {
            legs = 4
        } else if race == "Spider" {
            legs = 8
        } else {
            fatalError("Race \(race) needs to be implemented!!")
        }

        // will initialize legCount automatically with correct number of legs if race is implemented
        self.init(race: race, name: name, legCount: legs)
    }
}

사용 예

// default init with all default values used
let myFirstDog = Animal(name: "Bello")

// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")

// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)

// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")

1
잘 간단한 여기에 설명
VIVI

4

편의 이니셜 라이저는 클래스 확장 에서 정의 할 수 있습니다 . 그러나 표준적인 것은 할 수 없습니다.


1
질문에 대한 완전한 대답은 아니지만 이것은 아직 고려하지 않은 것입니다. 감사합니다!
Marc-André Weibezahn

3

convenience init값으로 클래스를 초기화하는 것을 선택적으로 만듭니다.

여기에 이미지 설명 입력

여기에 이미지 설명 입력


2

참고 : 전체 텍스트 읽기

지정된 이니셜 라이저는 클래스의 기본 이니셜 라이저입니다. 지정된 이니셜 라이저는 해당 클래스에 의해 도입 된 모든 속성을 완전히 초기화하고 적절한 수퍼 클래스 이니셜 라이저를 호출하여 수퍼 클래스 체인까지 초기화 프로세스를 계속합니다.

편의 이니셜 라이저는 보조 이니셜 라이저로 클래스에 대한 이니셜 라이저를 지원합니다. 지정된 이니셜 라이저의 매개 변수 중 일부를 기본값으로 설정하여 편의 이니셜 라이저와 동일한 클래스에서 지정된 이니셜 라이저를 호출하도록 편의 이니셜 라이저를 정의 할 수 있습니다.

클래스에 지정된 이니셜 라이저는 값 유형에 대한 간단한 이니셜 라이저와 동일한 방식으로 작성됩니다.

init(parameters) {
statements
}

편의 이니셜 라이저는 동일한 스타일로 작성되지만 편의 수정자가 init 키워드 앞에 공백으로 구분되어 배치됩니다.

convenience init(parameters) {
statements
}

실제 예는 다음과 같습니다.

class Food {
var name: String
init(name: String) {
    self.name = name
}
convenience init() {
    self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”

Food 클래스의 init (name : String) 이니셜 라이저는 새 Food 인스턴스의 저장된 모든 속성이 완전히 초기화되도록하기 때문에 지정된 이니셜 라이저로 제공됩니다. Food 클래스에는 수퍼 클래스가 없으므로 init (name : String) 이니셜 라이저는 초기화를 완료하기 위해 super.init ()를 호출 할 필요가 없습니다.

“Food 클래스는 인수없이 편리한 이니셜 라이저 init ()도 제공합니다. init () 이니셜 라이저는 이름 값이 [Unnamed] :”인 Food 클래스의 init (name : String)에 위임하여 새 음식에 대한 기본 자리 표시 자 이름을 제공합니다.

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”

계층 구조의 두 번째 클래스는 RecipeIngredient라는 Food의 하위 클래스입니다. RecipeIngredient 클래스는 요리 레시피의 재료를 모델링합니다. 이는 Quantity라는 Int 속성 (Food에서 상속되는 이름 속성 외에)을 도입하고 RecipeIngredient 인스턴스를 만들기위한 두 개의 이니셜 라이저를 정의합니다.

class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
    self.quantity = quantity
    super.init(name: name)
}
override convenience init(name: String) {
    self.init(name: name, quantity: 1)
}
}

RecipeIngredient 클래스에는 새로운 RecipeIngredient 인스턴스의 모든 속성을 채우는 데 사용할 수있는 init (name : String, quantity : Int)라는 단일 지정된 이니셜 라이저가 있습니다. 이 이니셜 라이저는 전달 된 수량 인수를 수량 속성에 할당하여 시작합니다.이 속성은 RecipeIngredient에 의해 도입 된 유일한 새 속성입니다. 이렇게하면 초기화 프로그램은 Food 클래스의 init (name : String) 초기화 프로그램까지 위임합니다.

페이지 : 536 발췌 : Apple Inc. "Swift 프로그래밍 언어 (Swift 4)". iBooks. https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11


2

따라서 클래스에 대한 모든 속성을 지정할 필요가 없을 때 유용합니다. 예를 들어 시작 HP 값이 100 인 모든 모험을 만들고 싶다면 다음과 같은 편의 초기화를 사용 하고 이름 만 추가하면됩니다. 이것은 코드를 많이 줄일 것입니다.

class Adventure { 

// Instance Properties

    var name: String
    var hp: Int
    let maxHealth: Int = 100

    // Optionals

    var specialMove: String?

    init(name: String, hp: Int) {

        self.name = name
        self.hp = hp
    }

    convenience init(name: String){
        self.init(name: name, hp: 100)
    }
}

2

사용 사례같은 클래스의 다른 이니셜 라이저에서 이니셜 라이저 를 호출 하는 것이라면 의미가 있습니다 .

놀이터 에서 해보세요

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    init(name: String) {
        self.init(name: name, level: 0) //<- Call the initializer above?

        //Sorry you can't do that. How about adding a convenience keyword?
    }
}

Player(name:"LoseALot")

편의 키워드로

class Player {
    let name: String
    let level: Int

    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    //Add the convenience keyword
    convenience init(name: String) {
        self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
    }
}

1

모든 대답은 좋은 것 같지만 간단한 예를 들어 이해하겠습니다.

class X{                                     
   var temp1
   init(a: Int){
        self.temp1 = a
       }

이제 우리는 클래스가 다른 클래스를 상속 할 수 있다는 것을 알고 있습니다.

class Z: X{                                     
   var temp2
   init(a: Int, b: Int){
        self.temp2 = b
        super.init(a: a)
       }

이제이 경우 Z 클래스에 대한 인스턴스를 만드는 동안 'a'와 'b'값을 모두 제공해야합니다.

let z = Z(a: 1, b: 2)

그러나 b의 값만 전달하고 나머지는 다른 값을 기본값으로 사용하려면 어떻게해야할까요?이 경우 다른 값을 기본값으로 초기화해야합니다. 그러나 어떻게 기다려?, U는 수업 전에 그것을 잘 설정해야합니다.

//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
    self.init(a: 0, b: b)
}
convenience init(){
    self.init(a: 0, b: 0)
}

이제 변수에 대한 값의 일부, 전부 또는 없음을 제공하여 클래스 Z의 인스턴스를 만들 수 있습니다.

let z1 = Z(b: 2)
let z2 = Z()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.