[Swift] Class & Struct


Swift Class & Struct


Class & Struct 클래스와 구조체

클래스와 구조체의 개념이 나오고부터 프로그래밍의 패러다임이 바뀌었음

  • 클래스 : 어떤 하나의 틀 (ex. 붕어빵 틀)
  • 객체 : 메모리에 각각 저장 된 실제 데이터 (ex. 붕어빵)

Class 클래스

// 클래스(붕어빵 틀) 만들기
// class를 struct로 바꾸어도 아래 코드는 문제없이 동작
// 둘의 차이는 아래에서..
class Dog {
    var name = "강아지"  // 클래스 내의 변수들을 속성(property)라고 지칭
    var weight = 0
    func sit() {  // 클래스 내의 함수들을 메서드(method)라고 지칭
        print("앉았습니다.")
    }
}

// 붕어빵 찍어내기 (객체의 생성)
var bori = Dog()

// 객체의 속성에 접근
bori.name = "보리"
bori.weight = 15
bori.sit()
  • 일반적으로 클래스, 구조체 선언할때 모두 속성을 먼저 쓰고 메서드를 나중에 작성
  • 클래스 내부에는 직접 메서드 실행문이 올 수 없음 (메서드 실행문은 메서드의 정의문 내에 존재해야함)

클래스와 구조체의 차이

  • 클래스와 구조체 둘다, 메모리에 찍어낸 것을 인스턴스(instance)라고 함
  • 인스턴스는 실제로 메모리에 할당되어 구체적 실체를 갖춘 것이라는 의미
  • 클래스의 instance를 객체(object)라고 부름

    • 클래스의 인스턴스 (객체)
    • 구조체의 인스턴스
    • 열거형의 인스턴스

메모리 저장 방식의 차이

 구조체 (struct)클래스 (class)
타입Value Type (값 형식)Reference Type (참조 형식)
메모리 관련
값의 저장
Stack / 복사 전달
(메모리에서 자동 제거)
Heap / 주소 전달
(ARC로 관리)
let / var 선언인스턴스 상수(let)로 선언시
저장 속성이 전부 상수로 선언됨
인스턴스 상수(let)로 선언하면
가리키는 인스턴스 고정
(저장 속성은 각 let/var 선언에 따름)
생성자 관련멤버와이즈 이니셜라이저 자동 제공편의 생성자 존재
메서드 + 속성메서드 내에서 속성 변경 원칙적으로는 불가능메서드 내에서 속성 변경 가능
소멸자소멸자 없음소멸자 있음
상속 가능 여부상속 불가능상속 가능 (클래스가 유일)

값 형식과 참조 형식

 값 형식 (구조체)참조 형식 (클래스)
타입Value TypeReference Type
메모리 상의
저장 위치
필요시에 항상 메모리의 값이 복사되어 전달
값의 저장 : Stack
필요시에 항상 메모리의 주소를 전달
값의 저장 : Heap (주소를 Stack에 저장)
메모리
관리 방식
값이 들어있는 스택의 스코프가 종료되면
메모리에서 자동 제거
RC(Reference Counting)을 통해 메모리 관리
Swift에서 사용하는 ARC 모델
각 형식의
타입 예시
기본 타입, 튜플, 열거형, 컬렉션, 구조체클래스, 클로저

클래스와 구조체의 let과 var 키워드

class PersonClass {
    var name = "사람"
    var age = 0
}

struct AnimalStruct {
    var name = "동물"
    var age = 0
}

let astruct = AnimalStruct()
// 구조체의 경우에 let으로 선언하면 메모리 구조가 스택에 생기기 때문에 속성을 변경할 수 없음, 전체 내부의 속성 모두가 let으로 선언된 것
let pclass = PersonClass()
// 클래스의 경우에 let으로 선언하면 새로운 메모리 주소로 변경할 수 없다는 뜻으로 다른 객체를 가르킬 수 없지만 반면에 가리키고 있는 속성은 var로 선언되어 있기 때문에 변경이 가능


astruct.name = "동물1"  // let으로 선언했으므로 값을 바꿀 수 없음 (Error)
pclass.name = "사람1"  // let으로 선언했어도 값이 변함

Initialize 초기화

생성자(Initialize)는 인스턴스를 만들 때 사용하는 특별한 메서드

class Dog {
    var name: String
    var weight: Int
    
    init(name: String, weight: Int) {
        self.name = name
        self.weight = weight
    }
}

var dog1 = Dog(name: "흰둥이", weight: 25)

초기화 메서드 / 이니셜라이저 / 생성자

  • 모든 저장 속성(변수)을 초기화 해야함 (구조체, 클래스 동일)
  • 생성자 실행 종료시점에는 모든 속성의 초기값이 저장되어 있어야 함 (초기화가 완료되지 않으면 컴파일 에러)
  • 클래스, 구조체, (열거형)은 모두 설계도 일뿐이고, 실제 데이터(속성), 동작(메서드)을 사용하기 위해서는 초기화 과정이 반드시 필요함
  • 인스턴스 초기화를 완료하면 메모리에 정상적으로 인스턴스가 생성
  • 옵셔널 타입을 가진 변수의 경우는 nil로 초기화되기 때문에 반드시 초기화값이 있을 필요는 없음

식별 연산자

두개의 참조가 같은 인스턴스를 가르키고 있는지를 비교하는 방법

print(dog1 === dog2)
print(dog1 !== dog2)

클래스와 구조체 사용 이유와 상황에 따른 사용

결국 클래스/구조체는 의미있는 데이터를 묶음으로 만들려는 것이다.

클래스/구조체를 사용하는 이유

  1. 사용하려는 모델의 설계
    • Data Transfer Object (DTO) / VO : 관련 데이터를 한 곳에 담는 역할
    • Data Access Object (DAO) : 데이터 처리 (비즈니스 로직)
    • Helper Object : 유틸리티 (도움주는 기능 : 날짜, 시간, 통화, 인코딩)
  2. 미리 설계 해 놓은 클래스/구조체들을 잘 활용하기 위함 (프레임 워크)

사용 상황의 차이

  • struct 사용
    • 상속이 필요하지 않고 모델의 사이즈가 그리 크지 않을 떄
    • JSON의 필드와 1:1 mapping되는 간단한 모델이 필요할 때
      (JSON대신 다른 데이터 encoder/decoder를 구현가능하지만 Swift에서는 JSON만 제공됨)
  • class 사용
    • 해당모델을 serialize 해서 전송하거나 파일로 저장할 일이 있을 때
    • 해당 모델이 Obj-C와 상호 운용성이 필요할 떄

필연적으로 클래스는 구조체보다 여러가지 면에서 속도가 느릴 수 밖에 없으므로 차이점을 명확하게 인지하고 사용

애플 가이드 라인

  • 연관된 간단한 값의 집합을 캡슐화 하는 것만이 목적일 때
  • 캡슐화한 데이터를 참조하는 것보다 복사되는 것이 효율적일 때
  • 구조체에 저장된 프로퍼티가 값 타입이며 참조되는 것보다 복사되는 것이 합당할 때
  • 다른 타입으로부터 상속받거나 자신이 상속될 필요가 없을 때

이러한 상황에서 애플은 구조체를 사용하기를 권장하고 있다.
애플은 상속 등의 클래스의 기능들을 필요로 하지 않는다면 구조체를 사용하기를 권장한다.

객체지향 4대 특징

  1. 추상화 (Abstraction) [모델링]
    • 실생활에서의 구체적인 것들을 관찰자가 관심있는 부분만 가지고 재조합 하는 것
    • 실체들에서 관심있고, 공통적인 특성을 뽑아내서 하나의 분류(class)로 만든 것 (모델링)
    • 객체지향의 관점에서, 실체들의 공통적 특성을 뽑아내서 클래스로 정의하는 것 자체가 추상화의 개념

    ex) 도서관리 프로그램 -> 제목 / 저자 / 출판사 / 가격 등

  2. 캡슐화 (Encapsulation) [모델링 / 정보 은닉 / 데이터 캡슐화]
    • 연관이 있는 속성(상태)과 메서드(기능)를 ‘하나의 클래스’로 묶어서 활용한다는 개념
    • 묶을 때, 객체는 자신이 맡은 역할을 수행하기 위한 하나의 목적을 가진 실체라는 관점에서 접근해야함
    • 추상화와 밀접하게 연결되는 개념
      (추상화는 디자인 레벨 관점에서 바라보는 개념인데 반해, 캡슐화는 실제 코드로 구현하는 레벨에서의 개념)
    • 은닉화 (Information Hiding)
      • 캡슐화를 하면, 접근 제어자(private, public 등)를 사용해 객체 외부에서 내부 데이터의 접근 통제가 가능해짐
  3. 상속성 (Inheritance) [재사용 / 확장]
    • 부모 클래스의 속성과 메서드를 자식 클래스에서 그대로 물려 받는 개념
    • 상속을 통해, 코드가 재활용되기 때문에 생산성이 높아짐 (클래스의 재사용과 확장을 위해 상속이 사용됨)
    • 객체지향의 개념에서, 클래스가 다른 타입과 구별되는 결정적인 차별점
  4. 다형성 (Polymorphism) [사용 편의 / 동적 바인딩]
    • 하나의 객체가 여러가지 타입의 형태로 저장될 수 있고, 다양한 메서드의 형태로 동작 가능함을 의미
      (하나의 객체는 부모의 타입으로도 저장이 가능하고, 프로토콜 타입으로도 저장이 가능)
    • 하나의 객체는 다양한 방식으로 동작 가능 (동적 바인딩 / Method Dispatch)
      (또한 오버라이딩과 오버로딩을 통해 하나의 메서드나 클래스를 다양한 방법으로 동작시키는 것을 포함하는 개념)

참고

Ellen Swift Class






© 2021. by hminkim