아요 개발 일기

[Swift] Class - 클래스 본문

Swift/Grammar

[Swift] Class - 클래스

소진이 2023. 1. 27. 18:45

Class

  •  프로퍼티(클래스 안의 변수)와 메소드(클래스 안의 함수)를 가질 수 있는 컨테이너 타입을 정의하기위한 용도
  •  일반적으로 단일 상속이 가능하지만, 프로토콜을 사용하면 다중 상속도 가능
  •  참조타입 (=reference type) (call by reference)
  • 형 변환(Type Casting)과 관련된 기능과 파괴자(Deinitializer)등의 기능을 가질 수 있다.
  • iOS 프레임 워크의 대부분의 큰 뼈대는 모두 클래스로 구성
  • 메모리의 Heap 영역에 존재

 

속성 (property) : 클래스에서 제공되는 변수

메소드 (method) : 클래스에서 제공되는 함수

 


참조 타입이 뭐에요?

 

참조 타입이란, 데이터의 크기가 크고 가변적이여서 동적으로 관리 되는 메모리의 힙에 생성되는 것을 말 합니다.

 

  • 선언 : 선언에 의해 참조만 생성될 뿐이지 데이터를 저장할 수 있는 실제 메모리가 할당되는 것은 아니므로 선언 즉시 사용할 수 없으며, 반드시 new 연산자로 메모리를 할당 받아 초기화 해야 합니다.
  • 종료 : 더 이상 참조하는 변수가 없을 때 가비지 컬렉터에 의해 파괴 됩니다.
  • 데이터 : 참조 타입끼리의 대입은 힙에 할당된 데이터를 참조하는 참조자가 하나 더 늘어날 뿐 별도의 메모리가 추가로 할당되는 것은 아닙니다. 그래서 둘 중 하나를 변경하면 상대 쪽도 같이 변경 됩니다.

Heap에 대해 자세히 알고 싶은 분들은 메모리 구조 (feat.Stack과 Heap)포스팅을 참고해주세요!


 

 

클래스에 대해 이론적으로 보았으니, 정의와 구현을 해볼까요?


정의 및 구현

 

아래는 정의 방식입니다.


class 클래스명 {
 ... }

 

이제 구현해 볼까요? Exm이라는 클래스를 만들어 보았습니다!

 

class Exm { 
	var name = "Sojin";
    
    func say(){ 
    	print("Hello, " + name + "!"); 
        
    } 
  }

 

위 클래스에는 name이라는 속성과 say라는 메소드가 있습니다. name 과 say 모두 기능이겠죠?

say 메소드 안에 name 속성이 사용되고 있는 것 처럼 클래스에 있는 메소드에 내부는 그 클래스에 있는 속성과 메소드를 그대로 사용할 수 있도록 되어있습니다.

 


클래스 밖에서 사용하기 위해서 인스턴스를 생성해 볼까요?

인스턴스에 대해서 모르겠는 분은 OOP와 POP 포스팅을 참고해주세요!

 

실행해보기

인스턴스를 생성했으니, 한번 활용해서 실행해봅시다!

class Exm {
    var name = "Sojin"
    
    func say(){
        print("Hello, " + name + "!")
    }
}

var obj:Exm = Exm(); // 인스턴스 생성

obj.say(); // Hello, Sojin!

obj.name = "Hanako"; // Sojin -> Hanako

obj.say(); // Hello, Hanako


코드를 보니, 클래스 내부에 생성되어있는 객체(변수) 값을 변경 할 수도있네요?

 

 


이제 클래스의 큰 특징인 상속을 해보겠습니다!

 

상속하기

먼저 코드를 보기 전에 상속 코드는 어떻게 작성하는 걸까요?

class 자식 클래스명 : 부모 클래스명 {
...
}

오른쪽에 : 부모클래스명만 추가 되었죠?! 어렵지 않아요!

위와 같은 부모 클래스를 상속 받아 새로운 자식 클래스를 만드는 과정을 서브 클래싱(Subclassing)이라고 합니다:-)

 

이제 코드 예시로 이해해볼까요? 

 

Hello 클래스가 Helo 클래스를 상속 받고있는 코드 입니다.

class Helo {
    var name:String = "Sojin";
    func say(){
        print("Hello, " + name + "!");
        
    }
    
}

class Hello:Helo {
    var name2:String = "YAMADA";
    func say2(){
        print("Hi," + name + "-" + name2 + "!");
        
    }
    
}
var obj:Hello = Hello();

obj.say();
obj.name = "Sojin";
obj.name2 = "TANAKA";
obj.say2();

 

위 코드에서는 Hello 클래스 인스턴스만 생성했는데 Helo클래스의 함수와 객체를 선언해서 문제 없이 사용하고 있는 것을 볼 수 있습니다.

 

이유는 당연히 상속을 사용했기 때문이겠지요? Hello 클래스(자식 클래스) 가 Helo 클래스(부모 클래스)를 상속 받았기 때문에 부모 클래스인 Helo의 객체와 함수등을 모두 가져다가 사용할 수 있는 것입니다!

 


 

initializer (생성자) & Deinitializer

생성자는 인스턴스가 생성될 때의 형식과 할 일을 정의하는 부분입니다!!

 

생성자에 관련된 자세한 내용은 생성자(initializer) 포스팅을 참고해주세요!

struct Grade {
    var letter: Character
    var points: Double
    var credits: Double
}

class Person {
    var firstName: String
    var lastName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }

    func printMyName() {
        print("My name is \(firstName) \(lastName)")
    }
}

class Student: Person {
    var grades: [Grade] = []
    
    override init(firstName: String, lastName: String) {
        super.init(firstName: firstName, lastName: lastName)
    }
    
    convenience init(student: Student) {
        self.init(firstName: student.firstName, lastName: student.lastName)
    }
    
    
}

// 학생인데 운동선수
class StudentAthlete: Student {
    var minimumTrainingTime: Int = 2
    var trainedTime: Int = 0
    var sports: [String]
    
    init(firstName: String, lastName: String, sports: [String]) {
        // Phase 1
        self.sports = sports
        super.init(firstName: firstName, lastName: lastName)
        
        // Phase 2
        self.train()
    }
    
    
    convenience init(name: String) {
        self.init(firstName: name, lastName: "", sports: [])
    }
    
        
    func train() {
        trainedTime += 1
    }
}

// 운동선인데 축구선수
class FootballPlayer: StudentAthlete {
    var footballTeam = "FC Swift"

    override func train() {
        trainedTime += 2
    }
}

let student1 = Student(firstName: "Jason", lastName: "Lee")
let student1_1 = Student(student: student1)
let student2 = StudentAthlete(firstName: "Jay", lastName: "Lee", sports: ["Football"])
let student3 = StudentAthlete(name: "Mike")

 

deinit

  • 클래스 타입에서만 구현 가능
  • deinit은 클래스의 인스턴스가 메모리에서 해제되는 시점에서 호출
  • 인스턴스 해제되는 시점에 해야할 일을 구현
class PersonE {
    var name: String
    var pet: Puppy?
    var child: PersonC

    init(name: String, child: PersonC) {
        self.name = name
        self.child = child
    }

    deinit { //사람이 사망했을 때 다른 주인에게 인도 (인스턴스가 해제되는 시점에 해야할 일)
        if let petName = pet?.name {
            print("\(name)가 \(child.name)에게 \(petName)를 인도합니다.")
            self.pet?.owner = child
        }
    }
}

var donald: PersonE? = PersonE(name: "donald", child: jenny)
donald?.pet = happy
donald = nil // donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됩니다.
//donald가 jenny에게 happy를 인도합니다

 


클래스만 가능한 기능

- 상속 : 클래스의 특성을 다른 클래스에게 물려줄 수 있다.

- 타입 캐스팅 : 실행 시 컴파일러가 클래스 인스턴스의 타입을 미리 파악하고 검사할 수 있다.

- 소명화 구문 : 인스턴스가 소멸되기 직전에 처리해야 할 구문을 미리 등록해 놓을 수 있다.

- 참조에 의한 전달 : 클래스 인스턴스가 전달될 때에는 참조 형식으로 제공되며, 이때 참조가 가능한 개수는 제약이 없다.

 

채택과 상속?

  • 클래스 상속 : 상속받을 클래스의 모든 기능을 물려 받는 기능
    • Single Responsibility(단일 책임)
    • Type Safety (타입이 분명해야할떄)
    • Shared Base Classes (다자녀가 있을때)
    • Extensibility(확장성이 필요한 경우)
    • Identity(정체를 파악하기 위해)
  • 프로토콜 채택 : 내가 이제 이 프로토콜을 준수하겠어! 라는 뜻

Class를 써야하는 경우

1. 두 object의 인스턴스 자체가 같음을 확인해야할 떄

2. 하나의 객체가 필요하고, 여러 대상에 의해 접근되고 변경이 필요한 경우

 


 

'Swift > Grammar' 카테고리의 다른 글

[Swift] 조건문 - Switch case  (0) 2023.01.29
[Swift] Struct - 구조체  (0) 2023.01.27
[Swift] 고차 함수  (0) 2023.01.27
[Swift] 오류처리  (0) 2023.01.27
[Swift] Extension - 익스텐션  (0) 2023.01.27