본문 바로가기
아이폰 개발/Swift

swift 기초 - 상속, 인스턴스의 생성과 소멸

by 인생여희 2021. 3. 4.

swift 기초 - 상속, 인스턴스의 생성과 소멸 

 

[1] 상속

 스위프트의 상속은 클래스, 프로토콜 등에서 가능하다.

 열거형, 구조체는 상속이 불가능하다.

 스위프트는 다중상속을 지원하지 않는다.

 

 클래스의 상속과 재정의

 

 class 이름 : 상속받을 클래스 이름 {

    //구현부

 }

 

 

[1-1] 클래스 정의

class Person {
    
    //[1] 인스턴스 변수
    var name : String = ""
    
    //[2] 인스턴스 메소드
    func selfIntroduce(){
        print("저는 \(name) 입니다.")
    }
    
    //[2] 인스턴스 메소드 2
    //final 키워드 - 재정의 방지
    final func sayHello(){
        print("hello")
    }
    
    //[3] 타입 메서드
    //재정의 불가 타입 메서드 - static
    static func typeMethod(){
        print("type method - static!")
    }
    
    //[4] 재정의 가능 타입 메서드 - class
    class func classMethod() {
        print("type method - class!")
    }
    
    //[5] 재정의 가능한 class 메서드라도
    //final 키워드를 사용하면 재정의 할 수 없다.
    //메서드 앞의 static 과 final class 는 똑같은 역할을 한다.
    final class func finalClassMethod(){
        print("type method - final class")
    }
}

 

 

 

[1-2] 서브 클래스 정의

class Student: Person {
    
    //인스턴스 변수
    var major : String = ""
    
    //오버라이드 - 1
    override func selfIntroduce() {
        print("저는 \(name)이고 , 전공은 \(major) 입니다.")
    }
    
    //오버라이드 - 2
    override class func classMethod() {
        print("overriden type method - class")
    }
    

 

 

 !주의 [1] : "static" 을 사용한 타입 메서드는 재정의 할 수 없습니다.

  //Cannot override static method

  //override static func typeMethod(){}

    

     

  !주의 [2] : "final" 키워드를 사용한 메소드, 프로퍼티는 재정의 할 수 없습니다.

  //override func sayHello() {    }

  //override class func finalClassMethod() {    }

 

 

 

[1-3] 사용하기

//인스턴스 변수 생성
let person : Person = Person()
let kim : Student = Student()

//대입
person.name = "gildong"
kim.name = "kimdongsoo"
kim.major = "swift"

//인스턴스 객체로 인스턴스 메소드 호출
person.selfIntroduce()  //저는 gildong 입니다.
kim.selfIntroduce()     //저는 kimdongsoo이고 , 전공은 swift 입니다.

//슈퍼클래스(부모 클래스)로 타입 메소드 호출
Person.classMethod()    //type method - class!
Person.typeMethod()     //type method - static!
Person.finalClassMethod() //type method - final class

//서브클래스 (자식클래스)로 부모의 타입 메소드 호출
Student.classMethod()     //overriden type method - class
Student.typeMethod()      //type method - static!
Student.finalClassMethod()  //type method - final class

 

[2] 인스턴스의 생성과 소멸

 

 이니셜라이저와 디이니셜라이저 (init, deinit)

 

스위프트의 모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효한 값이 할당되어 있어야 한다.

프로퍼티에 미리 기본값을 할당해 두면,  인스턴스가 생서됨과 동시에 초기값을 지니게 된다.

 

 

[2-1] 기본값이 있는 클래스

class PersonA {
    //모든 저장 프로퍼티에 기본값 할당
    var name : String = "unknown"
    var age :  Int = 0
    var nickName : String = "nick"
}

let park : PersonA = PersonA()
park.name = "park"
park.age = 30
park.nickName = "hopark"

 

 

[2-2] 이니셜라이저 

 프로퍼티 기본값을 지정하기 어려운 경우,

 이니셜라이저를 통해서 인스턴스가 가져야 할 초기값을 전달 할 수 있다.

 

class PersonB {
    
    //기본값이 없는 인스턴스 변수
    var name : String
    var age : Int
    var nickName : String
    
    //이니셜 라이저
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
 
}

let huang:PersonB = PersonB(name: "huangkyo", age: 20, nickName: "황닉")

//nil 할당시 오류!
//let huang2:PersonB = PersonB(name: "huangkyo", age: 20, nickName: nil)

 

 

[2-3] 프로퍼티의 초기값이 꼭 필요 없을때 - 옵셔널 사용

class PersonC {
    
    //기본값이 없는 인스턴스 변수 - init 메소드를 반드시 써줘야 에러가 안난다!
    var name : String
    var age : Int
    
    //옵셔널 사용
    var nickName : String?
    
    //이니셜 라이저 - 1
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
    
    //이니셜 라이저 - 2
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let jenny: PersonC = PersonC(name: "jenny", age: 10)
let mike: PersonC = PersonC(name: "mike", age: 15, nickName: "m")

 

 

[2-4] 암시적 추출 옵셔널

 

 인스턴스 사용에 꼭 필요하지만

 초기값을 할당하지 않고자 할 때 사용

class Puppy {
    //name 변수에 원래 값이 없으면 안된다 : Class 'Puppy' has no initializers
    var name: String     //강아지 이름
    var owner: PersonC!   //주인 객체
    
    init(name: String) {
        self.name = name
    }
    
    func goOut() {
        print("\(name)가 주인 \(owner.name)와 산책을 합니다.")
    }
}

let happy : Puppy = Puppy(name: "happy")

//강아지는 주인없이 산책할 수 없다.
//happy.goOut() //오류발생

happy.owner = jenny
happy.goOut()
//happy가 주인 jenny와 산책을 합니다.

 

 

[2-5] 실패 가능한 이니셜라이저

 이니셜라이저 매개변수로 전달되는 초기값이 잘못된경우

 인스턴스 생성에 실패할 수 있다

 인스턴스 생성에 실패하면 nil을 반환한다.

 그래서 실패 가능한 이니셜라이저의 반환 타입은 옵셔널이다.

 

class PersonD {
    var name:String
    var age:Int
    var nickName: String?

    //초기화 하기전에 값 검증
    init?(name: String , age:Int){
        
        if (0...120).contains(age) == false {
            return nil
        }
        
        if name.count == 0 {
            return nil
        }
        
        self.name = name
        self.age = age
    }
}

//옵셔널 타입으로 작성해야됨
//Value of optional type 'PersonD?' must be unwrapped to a value of type 'PersonD'
//let jhonadan1 : PersonD = PersonD(name: "jhonadan", age: 23)

let jhonadan2 : PersonD? = PersonD(name: "jhonadan", age: 23)

//오류 - age 223 이다...
//let jhonadan : PersonD? = PersonD(name: "jhonadan", age: 223)

 

 

[2-6] 디이니셜라이저

 deinit은 클래스의 인스턴스가 메모리에서 해제되는 시점에 호출된다.

 인스턴스가 해제되는 시점에 해야될 일을 구현할 수 있다.

 

class PersonE {

    //기본값이 없는 인스턴스 변수 - init 메소드를 반드시 써줘야 에러가 안난다!
    var name : String
    var pet : Puppy?
    var chaild:PersonC
    
    //매개변수 : 주인이름 , 자식객체
    init(name: String, chaild: PersonC) {
        self.name = name
        self.chaild = chaild
    }
    
    //메모리에서 해제될 시점에 호출됨
    deinit {
        if let petName = pet?.name {
            print("\(name)가 \(chaild.name)에게 \(petName)을 인도합니다.")
            self.pet?.owner = chaild
        }
    }
}

//var donald:PersonE = PersonE(name: :"donald", chaild: jenny)

//개 주인 객체 생성
var donald:PersonE? = PersonE(name: "donald", chaild: jenny)
donald?.pet = happy

donald = nil // donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됩니다
//donald가 jenny에게 happy을 인도합니다.

 

 

test2.zip
0.02MB

 

 

 

 

참고

blog.yagom.net/