2019년 1월 18일 금요일

Failable Initializers



Swift Programming Language Reference: Declarations, 코드부분
Swift Programming Language Guide: Failable Initializers, 코드부분
Swift Blog: Failable Initializers, 코드부분




/* Failable Initializers (Language Reference) */

struct SomeStruct {
   let property: String
   // produces an optional instance of 'SomeStruct'
   init?(input: String) {
       if input.isEmpty {
           // discard 'self' and return 'nil'
           return nil
       }
       property = input
   }
}

if let actualInstance = SomeStruct(input: "Hello") {
   // do something with the instance of 'SomeStruct'
} else {
   // initialization of 'SomeStruct' failed and the initializer returned 'nil'
}


/* Failable Initializers (Language Guide) */

let wholeNumber: Double = 12345.0
let pi = 3.14159

if let valueMaintained = Int(exactly: wholeNumber) {
   print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}

let valueChanged = Int(exactly: pi)

if valueChanged == nil {
   print("\(pi) conversion to Int does not maintain value")
}

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

let someCreature = Animal(species: "Giraffe")

if let giraffe = someCreature {
   print("An animal was initialized with a species of \(giraffe.species)")
}

let anonymousCreature = Animal(species: "")
if anonymousCreature == nil {
   print("The anonymous creature could not be initialized")
}

// Failable Initializers for Enumerations

enum TemperatureUnit {
   case kelvin, celsius, fahrenheit
   init?(symbol: Character) {
       switch symbol {
       case "K":
           self = .kelvin
       case "C":
           self = .celsius
       case "F":
           self = .fahrenheit
       default:
           return nil
       }
   }
}

let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
   print("This is a defined temperature unit, so initialization succeeded.")
}

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
   print("This is not a defined temperature unit, so initialization failed.")
}


// Failable Initializers for Enumerations with Raw Values

enum TemperatureUnit: Character {
   case kelvin = "K", celsius = "C", fahrenheit = "F"
}

let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
   print("This is a defined temperature unit, so initializaion succeeded.")
}

let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
   print("This is not a defined temperature unit, so initialization failed.")
}

// Propagation of Initialization Failure

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

class CarItem: Product {
   let quantity: Int
   init?(name: String, quantity: Int){
       if quantity < 1 { return nil }
       self.quantity = quantity
       super.init(name: name)
   }
}

if let twoSocks = CarItem(name: "sock", quantity: 2) {
   print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}

if let zeroShirts = CarItem(name: "shirt", quantity: 0) {
   print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
   print("Unable to initialize zero shirts")
}

if let oneUnnamed = CarItem(name: "", quantity: 1) {
   print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
   print("Unable to initialize one unnamed product")
}

// Overriding a Failable Initializer

// Note: You can override a failable initializer with a nonfailable initializer but not the other way around.

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

class AutomaticallyNamedDocumnet: Document {
   override init() {
       super.init()
       self.name = "[Untitled]"
   }
   override init(name: String) {
       super.init()
       if name.isEmpty {
           self.name = "[Untitled]"
       } else {
           self.name = name
       }
   }
}

class UntitledDocument: Document {
   override init() {
       super.init(name: "[Untitled]")!
   }
}


/* Failable Initializers (Swiff Blog) */

if let image = NSImage(contentsOfFile: "swift.png") {
   // loaded the image successfully
} else {
   // could not load the image
}

extension Int {
   init?(fromString: String){
       if let i = Int(fromString) {
           self = i
       } else {
           return nil
       }
   }
}

enum Color: Int {
   case Red = 0, Green = 1, Blue = 2
   
   init?(rawValue: Int){
       switch rawValue {
       case 0:
           self = .Red
       case 1:
           self = .Green
       case 2:
           self = .Blue
       default:
           return nil
       }
   }
}