클래스와 구조체

  • 클래스와 구조체는 기본적으로 하나의 큰 코드 블록이다.
  • 클래스와 구조체에서는 변수나 상수는 프로퍼티로, 함수는 메소드로 불린다.
  • 프로퍼티나 메소드는 클래스 혹은 구조체의 멤버다.
  • Swift는 객체지향언어로, 필요한 기능을 객체로 구현하여 사용한다. 이때 객체를 만들어내는 주요 대상이 바로 클래스와 구조체이다.

클래스와 구조체의 공통점

  • 프로퍼티나 메소드를 정의할 수 있다.
  • 객체를 원하는 상태로 초기화하는 초기화 블록을 제공한다.
  • 객체에 함수적 기능을 추가하는 확장(extends)구문을 사용할 수 있다.
  • 프로토콜을 구현할 수 있다.

클래스만이 갖는 특징

  • 상속이 가능하다.
  • 인스턴스가 소멸되기 직전 실행할 구문을 미리 등록할 수 있다.
  • 클래스는 참조에 의한 전달, 구조체는 값에 의한 전달이다.
  • 참조에 의한 전달은 주소값을 전달하기 때문에 각각 다른 인스턴스도 같은 주소를 가리키게 되어 한 인스턴스에서의 변화는 다른 인스턴스에 영향을 준다.
  • 또한, 클래스는 참조에 의한 전달로 값 비교는 일반 ==이 아닌 === 즉 같은 메모리를 참조하는지를 비교해야 한다.
class StudentA {
    var name: String = "안재형"
    var score: Double = 99.9

    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

var studentA: StudentA = StudentA()
var ajh = studentA
ajh.name = "안 재형"
studentA.name //안 재형
studentA === ajh //true
  • 반면 값에 의한 전달은 값을 복사한 형태로 가지고 오기 때문에 인스턴스는 서로 완전히 다른 인스턴스이다. 그러므로 서로 영향을 줄 수 없다.
struct StudentB {
    var name: String = "불량학생"
    var score: Double = 15.1

    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

var studentB: StudentB = StudentB()
var random = studentB
random.name = "랜 덤 학 생"
studentB.name //불량학생

클래스와 구조체의 정의

class StudentA {
    var name: String = "안재형"
    var score: Double = 99.9
    
    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

struct StudentB {
    var name: String = "불량학생"
    var score: Double = 15.1
    
    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

클래스와 구조체의 사용

  • 클래스와 구조체는 정의만으로는 아무런 의미가 없다.
  • 클래스 혹은 구조체를 통해 인스턴스를 만들어야만 비로소 사용이 가능하다.
  • 클래스 혹은 구조체는 인스턴스를 만들기 위한 일종의 ‘틀’이다.
var studentA: StudentA = StudentA() //변수 studentA StudentA를 할당하여 프로퍼티와 메소드 사용이 가능해진다.

studentA.name //안재형
studentA.score //99.9
studentA.sleep(time: 9.1) //중간고사 시험에서 99.9점 맞은 안재형은 9.1시간 잔다.

var studentB: StudentB = StudentB() //변수 studentB에 StudentB를 할당하여 프로퍼티와 메소드 사용이 가능해진다.

studentB.name //불량학생
studentB.score //15.1
studentB.sleep(time: 23.9) //중간고사 시험에서 15.1점 맞은 불량학생은 23.9시간 잔다.
  • 프로퍼티 혹은 메소드로의 접근은 인스턴스를 통해서만 가능하다.
  • 인스턴스를 만든 후 Dot Syntax를 통해 접근한다.

클래스와 구조체의 초기화

  • 클래스와 구조체는 인스턴스를 만드는 과정에서 반드시 프로퍼티 초기화가 이루어져야만 한다.
  • 초기화의 방법으로는 선언과 동시에 초기화, 초기화 메소드 사용, 옵셔널 타입으로 선언의 방법이 있다.
class StudentA {
    var name: String
    var score: Double

    //초기화 메소드 사용
    init() {
        self.name = "안재형"
        self.score = 99.9
    }
    
    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

var studentA: StudentA = StudentA()
studentA.name //안재형
studentA.score //99.9
class StudentA {
    var name: String? //옵셔널 타입으로 선언
    var score: Double? //옵셔널 타입으로 선언

    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

var studentA: StudentA = StudentA()
studentA.name //nil
studentA.score //nil
  • 구조체의 경우 특별하게 멤버와이즈 초기화 구문을 제공한다.
struct StudentB {
    var name: String = "불량학생"
    var score: Double = 15.1
    
    func sleep(time: Double) {
        print("중간고사 시험에서 \(score)점 맞은 \(name)\(time)시간 잔다.")
    }
}

var studentB: StudentB = StudentB() //name = "불량학생", score = 15.1

//멤버와이즈 초기화
var studentBv2: StudentB = StudentB(name: "성실학생", score: 75.5) //name = 성실학생, score = 75.5
  • 초기화시 원하는 프로퍼티만을 선택적으로 초기화하는 구문을 추가할 수 있다.
  • 이 경우 어떤 인자값도 받지 않는 기본 초기화 구문은 사용이 불가능하다.
class A {
    var name: String = "이름"
    var age: Int = 19
}

var test: A = A() //기본 초기화 구문

class B {
    var name: String = "이름"
    var age: Int?
    
    init(age: Int) { //초기화 구문 설정
        self.age = age
    }
}

var test2: B = B() //에러 -> init(age:) 라는 초기화 구문을 설정했기 때문
var test2: B = B(age: 20)
  • 이처럼 초기화 구문을 별도로 지정한 경우 해당 구문을 이용한 초기화만이 사용가능하다.
  • 만약 기본 초기화 구문을 계속 사용하고 싶다면, 클래스 내부에 기본 초기화 구문을 정의해야만 하며, 이때 모든 프로퍼티는 옵셔널로 선언이 되거나 초기화 전까지 반드시 값이 정의되어야만 한다.

클래스와 구조체의 선택

  • 구조체를 선택하는 경우는 다음과 같다.
  • 서로 연관된 몇 개의 데이터를 묶는 것이 목적일 때
  • 캡슐화된 데이터에 상속이 필요하지 않을 때
  • 캡슐화된 데이터의 전달이 값에 의한 전달이 합리적일 때
  • 캡슐화된 원본 데이터를 보존해야 할 때
  • 이 외에 경우에는 클래스를 사용하는 것이 좋다.