Skip to content

Latest commit

 

History

History
255 lines (157 loc) · 11.1 KB

Struct, Class.md

File metadata and controls

255 lines (157 loc) · 11.1 KB

구조체와 클래스

1. 구조체

1.1 구조체 정의

  • 키워드 : struct

  • 구조체 명명법 : 기본 타입 이름 (Int, String, Bool 등)처럼 UpperCamelCase를 사용하여 표기한다. 프로퍼티와 메서드는 LowerCamelCase를 사용한다.

struct 구조체이름 {
    프로퍼티와 메서드들
}
  • 예시
struct BasicInformation {
    var name: String
    var age: Int
}

1.2 구조체 인스턴스의 생성 및 초기화

  • 구조체 정의를 마친 후, 인스턴스를 생성하고 초기화하고자 할 때는 기본적으로 생성되는 멤버와이즈 초기화를 사용한다.

    • 멤버와이즈(Memberwise) 초기화 구문 : 구조체 안에 초기화 함수를 만들지 않아도 구조체 안에 정의한 속성들이 초기 값을 설정할 수 있는 구문. 즉, 초기화 함수 없어도 인스턴스 객체를 만들 때 소괄호 안에 파라미터를 넣어 할당할 수 있음.

      비교 클래스는 멤버와이즈 초기화 구문을 제공하지 않는다. 따라서 클래스는 파라미터가 하나도 없는 초기화 함수만 기본으로 제공하므로 나머지는 직접 init 함수를 추가해야 한다. 하지만 구조체는 파라미터가 하나도 없는 초기화 함수와 함께 모든 속성들의 초기 값을 넣을 수 있는 초기화 함수를 함께 제공한다.

  • 구조체에 기본 생성된 이니셜라이저의 매개변수는 구조체의 프로퍼티 이름으로 자동 지정된다.

  • 기본 제공되는 멤버와이즈 초기화 외에 사용자 정의 이니셜라이저도 구현 가능하다. 인스턴스에서 추후 업데이트

  • 인스턴스가 생성되고 초기화된 후 프로퍼티 값에 접근하고 싶다면 마침표(.)를 사용하면 된다.

  • 구조체를 상수 let으로 선언하면 인스턴스 내부의 프로퍼티 값을 변경할 수 없고, 변수 var로 선언하면 내부의 프로퍼티가 var로 선언된 경우에 값을 변경할 수 있다.

  • 예시

// 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체를 생성
var averyInfo: BasicInformation = BasicInformation(name: "Avery", age: 20)
averyInfo.age = 25         // 변경 가능
averyInfo.name = "Ava"     // 변경 가능

// 프로퍼티 이름(name, age)으로 자동 생성된 이니셜라이저를 사용하여 구조체를 생성
let EveInfo: BasicInformation = BasicInformation(name: "Eve", age: 20)
eveInfo.age = 25         // 변경 불가
oliviaInfo.age = 25      // 변경 불가
}

2. 클래스

2.1 클래스 정의

  • 키워드 : class

  • 클래스 명명법 : 클래스를 정의한다는 것은 새로운 타입을 생성해주는 것과 마찬가지므로 기본 타입 이름 (Int, String, Bool 등)처럼 UpperCamelCase를 사용하여 표기한다. 프로퍼티와 메서드는 LowerCamelCase를 사용한다.

class 클래스이름 {
    프로퍼티와 메서드를
}
  • 클래스를 정의하는 방법은 구조체와 흡사하다. 다만 클래스는 상속받을 수 있기 때문에 상속받을 때는 클래스 이름 뒤에 콜론(:)을 써주고 부모클래스 이름을 명시한다. 상속에서 추후 업데이트
class 클래스이름: 부모클래스이름 {
    프로퍼티와 메서드들
}

예시

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

2.2 클래스 인스턴스의 생성과 초기화

  • 클래스를 정의한 후, 인스턴스를 생성하고 초기화하고자 할 때는 기본적인 이니셜라이저를 사용한다.

인스턴스와 객체

보통 객체지향 프로그래밍 패러다임을 지향하는 언어에서는 클래스의 인스턴스를 객체라고 부른다. 물론 스위프트에서도 객체라고 부르는 것이 틀린 것은 아니지만, 스위프트 공식 문서에서는 좀 더 한정적인 인스턴스라는 용어를 사용한다.

즉, 클래스와 클래스로 만든 객체를 구별하기 위해 클래스로부터 만들어진 객체를 '인스턴스(Instance)'라고 부름.

  • 인스턴스가 생성되고 초기화된 후 프로퍼티 값에 접근하고 싶다면 마침표(.)를 사용하면 된다.

  • 구조체와는 다르게 클래스의 인스턴스는 참조 타입이므로 클래스의 인스터스를 상수 let으로 선언해도 내부 프로퍼티 값을 변경할 수 있다.

var Ava: Person = Person()
Ava.height = 165.0
Ava.weight = 50.0

let Finn: Person = Person()
Finn.height = 185.0
jenny.weight = 70.0
  • 기본 이니셜라이저 외에 사용자가 직접 이니셜라이저를 정의할 수도 있다. 인스턴스에서 추후 업데이트

2.3 클래스 인스턴스의 소멸

  • 클래스의 인스턴스는 참조 타입이므로 더는 참조할 필요가 없을 때 메모리에서 해제된다. ARC에서 추후 업데이트

  • 이 과정을 소멸이라고 하는데 소멸되기 직전 deinit라는 메서드가 호출된다.

  • 클래스 내부에 deinit 메서드를 구현해주면 소멸되기 직전에 deinit 메서드가 호출되는데, 이렇게 호출되는 deinit 메서드를 디이니셜라이저(Deinitializer)라고 부른다.

  • deinit는 클래스당 하나만 구현할 수 있고 매개변수를 위한 소괄호도 적지 않으며, 매개변수와 반환 값을 가질 수 없다.

예시

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
    
    deinit {
        print("Person 클래스의 인스턴스가 소멸됩니다.")
    }
}

var Ava: Person? = Person()
Ava = nil // Person 클래스의 인스턴스가 소멸됨
  • 보통 deinit 메서드에는 인스턴스가 메모리에서 해제되기 직전에 처리할 코드(인스턴스 소멸 전에 데이터를 저장하거나 다른 객체에 인스턴스 소멸을 알릴 때 등)를 넣는다. 인스턴스에서 추후 업데이트

3. 구조체와 클래스의 비교

공통점

  • 값을 저장하기 위해 프로퍼티를 정의할 수 있다.

  • 기능 실행을 위해 메서드를 정의할 수 있다.

  • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값(프로퍼티)에 접근하도록 정의할 수 있다.

  • 초기화될 때의 상태를 지정하기 위해 이니셜라이저를 정의할 수 있다.

  • 초기구현과 더불어 새로운 기능 추가를 위해 익스텐션을 확장할 수 있다.

  • 특정 기능을 실행하기 위해 특정 프로토콜을 준수할 수 있다.

차이점

  • 클래스의 인스턴스 객체를 변수에 할당하면 그 변수가 인스턴스 객체를 참조 혹은 레퍼런스(Reference)한다. 따라서 실제 객체가 만들어진 메모리의 위치만 가리킨다.

    하지만 구조체의 인스턴스 객체를 변수에 할당하면 그 변수는 인스턴스 객체를 복사(Copy)한다.

    구조체 -> 값을 전달하는 자료형 (Value Type)

    클래스 -> 참조를 전달하는 자료형 (Reference Type)

  • 구조체의 인스턴스 객체를 만들 때 멤버와이즈 초기화 구문을 사용할 수 있다.

  • 클래스는 상속할 수 있지만 구조체는 상속할 수 없다.

  • 클래스의 인스턴스 객체는 타입 변환(Type Casting)이 가능하다.

  • 클래스의 인스턴스 객체는 메모리에서 없어질 때 직접 값을 해제할 수 있도록 디이니셜라이저(소멸화 구문)를 제공한다.

  • 참조 횟수 계산(Reference Counting)은 클래스의 인스턴스에만 적용된다.


3.1 값 타입과 참조 타입이므로

  • 구조체는 값 타입이고 클래스는 참조 타입이다. 값 타입과 참초 타입의 가장 큰 차이는 무엇이 전달되느냐이다.

  • 예를 들어 어떤 함수의 전달인자로 값 타입만 넘긴다면 전달될 값이 복사되어 전달된다.

  • 그러나 참조타입이 전달인자로 전달될 때에는 값을 복사하지 않고 참조(주소)가 전달된다. 참조라는 것은 C, C++, Objective-C 등의 언어에서 사용되는 포인터와 매우 유사한 개념이지만, 표현할 때 애스터리스크(*)를 쓰진 않는다.

  • 함수의 전달인자로 넘길 때도 참조가 전달되며, 다른 변수 또는 상수에 할당될 때도 마찬가지로 참조가 할당된다.

  • 클래스의 인스턴스끼리 참조가 같은지 확인할 때에는 식별 연산자(Identify Operators)를 사용한다.


3.2 스위프트의 기본 데이터 타입은 모두 구조체

public struct String {
    /// An empty 'String'.
    public init()
}
  • 위의 코드는 스위프트 표준 라이브러리에 포함되어 있는 스위프트의 String 타입 기본 정의이다.

  • 다른 기본 타입(Bool, Int, Array, Dictionary, Set 등)도 String 타입과 마찬가지로 모두 구조체로 구현되어 있다.

  • 이는 기본 데이터 타입은 모두 값 타입이며, 전달인자를 통해 데이터를 전달하면 값이 복사되어 전달될 뿐 함수 내부에서 아무리 전달된 값을 변경해도 기존의 변수나 상수에는 전혀 영향을 미치지 못함을 의미한다. 이래서 스위프트의 전달인자는 모두 상수로 취급되는 건가?


4. 구조체와 클래서 선택해서 사용하기

앞서 알아본 듯이 구조체와 클래스는 새로운 데이터 타입을 정의하고 기능을 추가한 다는 점이 같지만 각각 인스턴스가 값 타입(Pass By Value)과 참조 타입(Pass By Reference)이라는 점은 다르다.

생긴 것은 비슷하지만 용도는 다르므로 프로젝트의 성격에 따라, 데이터의 활용도에 따라, 특정 타입을 구현할 때 구조체와 클래스 둘 중 하나를 선택해서 사용해야 한다.

다음은 다음과 같은 경우에는 구조체를 사용하는 것이 좋다.

  • 여러 개의 값을 하나로 묶어두고 싶을 때

  • 새로운 틀을 정의하는 데 기존 틀을 상속해서 만들 필요가 없을 때

  • 하나로 묶어 둔 데이터를 복사해서 만드는 것이 좋을 때

구조체로 사용하기에 가장 적합한 예로는 좌표계가 있다.

클래스는 새로운 인스턴스 객체를 만든 후 여러 변수에 여러 번 할당해도 인스턴스 객체를 복사해서 만들지 않으므로 메모리를 좀 더 효율적으로 사용할 수 있다.

따라서 위의 상황을 제외하면 클래스로 정의하여 사용하도록 한다.


객체 자료형의 비교 (연산자 === 와 !==)

  1. 두 객체의 자료형을 비교한다.

  2. 두 객체의 자료형이 같다면 그 안에 들어있는 속성을 비교한다.



참고 자료

클래스과 구조체(Classes and Structures)

Swift – 구조체 클래스

Swift 성능 이해하기 struct-class