TIL 0105 - 실패 가능 생성자, 소멸자, is/as 연산자, 다형성, Any/Anyobject

ian·2022년 1월 5일
0

TIL

목록 보기
3/11

갓생 살기 3회차 .. 오늘은 약간 김이 빠지는 하루였지만..! 아 오늘도 화이텡 ~~!~!~!!!~!~!!~!~! 🤓


실패 가능 생성자

  • 실패가능 생성자는 인스턴스 생성에 실패할 수도 있는 가능성을 가진 생성자
  • 생성자에 ?를 붙여서 init?(파라미터) 라고 정의하면 실패가능 생성자로 정의되는 것임

로 간단하게 얘기할 수 있죠잉?

코드를 보면서 얘기해볼까요?

struct Animal {
    let species: String
    
    init?(species: String) {
        if species.isEmpty {
            return nil          // species 가 nil일 수도 있으니! 옵셔널 작업을 함.. nil 이면 nil을 리턴 하도록!
        }
        self.species = species
    }
}

let a = Animal(species: "Giraffe")    // ====> 인스턴스 생성
let b = Animal(species: "")           // 문자열이기에 유효한 타입이지만 =====> nil

species 가 nil일 수도 있응께.... 난 ? 를 붙여서.. 실패 가능 생성자를 만든 것 뿐..
어떻게 만드는지 알겠죠?

아니 근데 실패 가능 생성자는 enum 에서도 구현 가능한 거.. 혹시 아나?



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

let c: TemperatureUnit = TemperatureUnit.celsius      // TemperatureUnit()
let f: TemperatureUnit? = TemperatureUnit(symbol: "F")

일반 적인 enum 은 위와 같은 식으로 init 뒤에 ? 를 붙여서 실패 가능 생성자로 표현해야하지만,

// 열거형의 원시값 설정 (실패가능 생성자의 구현과 유사)
enum TemperatureUnit1: Character {
    case kelvin = "K"
    case celsius = "C"
    case fahrenheit = "F"
}
// 원시값이있는 열거형은 자동으로 실패가능 생성자 init?(rawValue :)를 구현함 ==> 일치하면 인스턴스 생성, 아니면 nil 
let f1: TemperatureUnit1? = TemperatureUnit1(rawValue: "F")     // .fahrenheit
let u: TemperatureUnit1? = TemperatureUnit1(rawValue: "X")      // nil

원시값을 가진 enum 은! 자동으로.. 실패가능 생성자를 만드는 사실.. 알고 있었나?
여튼 그랬다는 사실 ~


소멸자는!

  • 인스턴스가 메모리에서 해제되기 직전 정리가 필요한 내용을 구현하는 메서드
  • 클래스 정의시 클래스에는 최대 1개의 소멸자(deinitializer)를 정의 가능
  • 소멸자는 파라미터(매개 변수)를 사용하지 않음

이렇게 간단 요약을 해봤음 .. 클래스에서만 존재하며 간단한 코드를 보여주겠음

class Aclass {
    var x = 0
    var y = 0
    
    deinit {
        print("인스턴스의 소멸 시점")
    }
}

var a: Aclass? = Aclass()
a = nil   // 메모리에 있던 a인스턴스가 제거됨

아직 이해가 잘 가지 않아, 주말에 좀 더 공부 할 예정이다 !


다음은 타입캐스팅!
타입캐스팅으로는 is 연산자, as 연산자가 있지만..!

일단 간단하게 상속을 다시 되집어 볼거임!

class Person {
    var id = 0
    var name = "이름"
    var email = "abc@gmail.com"
}

class Student: Person {
    // id
    // name
    // email
    var studentId = 1
}

class Undergraduate: Student {
    // id
    // name
    // email
    // studentId
    var major = "전공"
}

let person1 = Person()
person1.id
person1.name
person1.email

let student1 = Student()
student1.id
student1.name
student1.email
student1.studentId // 추가적으로

let undergraduate1 = Undergraduate()
undergraduate1.id
undergraduate1.name
undergraduate1.email
undergraduate1.studentId
undergraduate1.major    //  추가적으로

기본적으로 상속이 어떻게 진행이 되는지는 100% 이해가 됐음.. 웬일이지

위 코드에서 만든 인스턴스들을 이제 이용할거임! 왜? 인스턴스 타입을 검사하는is 연산자 때문!

is 연산자는: 타입에 대한 검사를 수행하는 연산자임
참이면 true 를 리턴하고,, 거짓이면 false 를 리턴하고.. 아마 상속관계의 계층에서 포함관계를 생각해보면 좀 쉬움!!

person1 is Person                // true
person1 is Student               // false
person1 is Undergraduate         // false

// 학생 인스턴스는 대학생 타입은 아니다.  (사람/학생 타입니다.)
student1 is Person               // true
student1 is Student              // true
student1 is Undergraduate        // false

// 대학생 인스턴스는 사람이거나, 학생이거나, 대학생 타입 모두에 해당한다.
undergraduate1 is Person         // true
undergraduate1 is Student        // true
undergraduate1 is Undergraduate  // true

요렇게 표현 할 수 있겠죠?


is 를 알아봤으니 이제 as 를 알아보죠 ~
가볍게 as!, as?as 이렇게 2개로 나눌 수가 있음..

자세히 고고!

  • 타입캐스팅 / 타입캐스팅 연산자 (이항 연산자)
    (1) Upcasting (업캐스팅)

    • 인스턴스 as 타입
    • 하위클래스의 메모리구조로 저장된 인스턴스를 상위클래스 타입으로 인식

    (2) Downcasting (다운캐스팅) (실패가능성이 있음)

    • 인스턴스 as? 타입 / 인스턴스 as! 타입
      ▶︎ as? 연산자
      • 참이면 반환타입은 Optional타입
      • 실패시 nil 반환
      ▶︎ as! 연산자
      • 참이면 반화타입은 Optional타입의 값을 강제 언래핑한 타입
      • 실패시 런타임 오류
let ppp = person as? Undergraduate 

위와 같은 코드를 해석하자면.. person 이라는 변수에 들어있는 인스턴스를 as? 를 이용해 Undergraduate 로 변환을 하고ppp 에 넣는다!

라고 해석하면 될 것 같아요..

아무래도 ?, ! 를 사용하다보니, 옵셔널이 있으므로..

if let newPerson = person as? Undergraduate {   // if let 바인딩과 함께 사용 (옵셔널 언래핑)
    newPerson.major
    print(newPerson.major)
}

로 주로 사용이 됩니다!

as! 가 사용될 땐

let person3: Undergraduate = person as! Undergraduate
person3.major

강제 언래핑을 한다고 생각하니, if let 같은 식을 사용하지 않겠죠?

as?, as! 는 다운캐스팅을 진행한 것이고..! as 는 업캐스팅을 한건데..!

이제 요놈을 알아볼거에요


let undergraduate2: Undergraduate = Undergraduate()
undergraduate2.id
undergraduate2.name
undergraduate2.studentId
undergraduate2.major
//undergraduate2.name = "길동"

let person4 = undergraduate2 as Person       // 항상 성공 (컴파일러가 항상 성공할 수 밖에 없다는 것을 알고 있음)
person4.id
person4.name

딱 봐도 하위클래스의 메모리구조로 저장된 인스턴스를 상위클래스 타입으로 인식해오는 걸 알 수 있습니다!


1) Any 타입

  • 기본 타입(Int, String, Bool, ...) 등 포함, 커스텀 클래스, 구조체, 열거형, 함수타입
    까지도 포함해서 어떤 타입의 인스턴스도 표현할 수 있는 타입 (옵셔널타입도 포함)
var some: Any = "Swift"
some = 10
some = 3.2

(some as! String).count

타입이 이것저것 섞였을 때 주로 이용하는 걸 알 수 있죠..

2) AnyObject 타입

  • 어떤 클래스 타입의 인스턴스도 표현할 수 있는 타입
let array: [Any] = [5, "안녕", 3.5, Person(), Superman(), {(name: String) in return name}]


//array[1].count
(array[1] as! String).count

학습 키워드

  • 실패 가능 생성자
  • 소멸자
  • is 연산자
  • as 연산자
  • 다형성
  • Any
  • AnyObject

문제점 / 고민한 점

  • 아무래도 .. 하루에 하나씩 적으려고 하니까 시간에 쫓겨 막 적는 경향이 있는 것 같다.. 제대로 이해되지 않은 점이랑, 잘 모르는 걸 적은 것도 주말에 한 번 훑는 시간을 가지는 게 좋을 것 같다!

TIL 시리즈는 막연히 제가 배운 걸 기록하는 공간입니다.


출처:
앨런 swift 문법 마스터 스쿨

profile
디자인씽킹을 하며 iOS 를 공부합니다

0개의 댓글