이번 포스팅에서는
앞서 다뤄온 '클래스'와 비슷해 보이지만, 큰 차이점이 존재하는
구조체(Struct)에 대한 내용을 살펴볼까 합니다.
어떻게 선언하고, 사용하는지와 더불어
클래스와의 차이점에 대해 알아보도록 하겠습니다.
Swift의 구조체
클래스와 유사하나, 값(Value) 타입의 캡슐화를 제공하는 틀
클래스와 동일하게,
구조체 또한 [객체지향 프로그래밍]의 기초를 형성합니다.
선언된 데이터와 기능(프로퍼티, 메서드)을 재 사용할 수 있는 객체이며,
추상화(인스턴스로의 실체화를 위해 공통된 속성과 행위를 정의)를 목표로
관련된 데이터를 캡슐화(결합, 집합화)하는 방법을 제공합니다.
선언 및 활용방법
- 클래스와는 달리, class 키워드 대신 struct 키워드를 사용하여 선언합니다.
- 클래스와 동일하게 프로퍼티와 메서드를 캡슐화 할 수 있습니다.
- 또한, 인스턴스(객체)를 생성하여 활용할 수 있습니다.
- 다음 예시코드는 간단한 구조체를 선언하는 방식입니다.
- Human이란 이름의 구조체입니다.
- name이란 String 타입의 프로퍼티와 초기화(initializer) 메서드로 구성됩니다.
struct Human {
var name: String // 프로퍼티
// initializer 메서드
init(name: String) {
self.name = name
}
}
- 다음 예시코드는 위 구조체를 활용, 인스턴스를 생성하는 방식입니다.
- 역시나, 클래스에서 인스턴스를 생성하는 구문과 완벽하게 동일한 형식입니다.
var lime: Human1 = Human1(name: "Lime")
lime.name // "Lime"
구조체의 확장 및 상속
- 클래스와는 달리, 구조체는 상속을 할 수 없습니다!
- 하지만, Swift에서는 프로토콜(Protocol)을 활용한 구조체의 확장을 권장합니다.
- 이와 같은 [프로토콜 지향 패러다임]을 토대로 확장/상속/조합 기능을 활용합니다.
- 다음 예시코드는 위 Human 구조체가 'Animals 프로토콜'을 상속받는 방식입니다.
- 구조체 뿐만 아닌, 클래스와 Enum 또한 프로토콜을 채택할 수 있습니다.
- thread-safe 하며, OOP(객체지향 프로그래밍)보다 확장 측면에서 유연합니다.
// 프로토콜(protocol) 선언
protocol Animals {
// 1. 각 프로퍼티에 gettable/settable(읽기/쓰기)인지 반드시 명시해야 함
// 2. 항상 variable로 선언되어야 함
var name: String { get }
}
// (마치 클래스가 상속받는것과 동일하게) :(콜론) 뒤에 프로토콜 이름을 작성
struct Human: Animals {
var name: String
}
(프로토콜의 경우, 다음 포스팅에서 더욱 자세히 다루도록 하겠습니다)
클래스 vs 구조체
인스턴스가 복사되거나 / 특정 함수의 매개변수로 전달될 시 동작의 차이가 존재
앞서 다룬 구조체가 클래스와 다른점을 정리하자면,
- class 키워드가 아닌, struct 키워드를 사용한다!
- 상속을 할 수 없으며, 프로토콜을 채택함으로서 기능을 확장한다!
하지만,
더 중요한 차이점이 존재합니다.
값(Value)타입과 참조(Reference) 타입
- 값과 참조타입은 클래스와 구조체의 '인스턴스' 생성 과정에서 차이가 발생합니다.
- 구조체는 ➟ 값 타입이며, 생성되는 인스턴스는 일종의 '복사본'이 됩니다.
- 클래스는 ➟ 참조 타입이며, 인스턴스가 저장된 메모리의 위치에 대한 '참조체'가 됩니다.
- 아래 예시 코드는 Animals란 이름의 구조체 입니다.
- 인스턴스 myDog(①) 를 생성하고, name 초기값으로 "Van"을 설정합니다.
- myDog(①)를 복사하는 yourDog(②) 인스턴스의 name 초기값을 "Coco"로 설정합니다.
- yourDog(②)는 myDog(①)는 별개의 객체이므로, 각자만의 '값'을 가지게 됩니다.
- 따라서, 프로퍼티 name의 값은 각각 다른 "Van"과 "Coco"가 출력됩니다.
- 인스턴스 myDog(①) 를 생성하고, name 초기값으로 "Van"을 설정합니다.
// 구조체 (Animals)
struct Animals {
var name: String
init(name: String) {
self.name = name
}
}
// 첫 번째 인스턴스 'myDog(①)'
let myDog: Animals = Animals(name: "Van")
print(myDog.name) // "Van"
// myDog(①)의 복사본, yourDog(②)
var yourDog = myDog
yourDog.name = "Coco" // 'Coco'로 이름을 변경!
// 아래와 같이 각각 다르게 출력되며, 자신만의 인스턴스 데이터를 갖게 됨
print(myDog.name) // "Van"
print(yourDog.name) // "Coco"
- 아래 예시 코드는 Animals란 이름의 클래스 입니다.
- 인스턴스 myDog(①) 를 생성하고, name 초기값으로 "Van"을 설정합니다.
- myDog(①)를 복사하는 yourDog(②) 인스턴스의 name 초기값을 "Coco"로 설정합니다.
- 참조체의 데이터(name)를 변경하게 되면, 원본인 클래스 인스턴스도 영향을 미칩니다.
- 따라서, 프로퍼티 name의 값은 동일한 "Coco"가 출력됩니다.
- 이유는 즉, 동일한 '클래스 인스턴스'에서 파생되어 나온 참조체 이기 때문입니다.
- 인스턴스 myDog(①) 를 생성하고, name 초기값으로 "Van"을 설정합니다.
class Animals {
var name: String
init(name: String) {
self.name = name
}
}
// 첫 번째 인스턴스 'myDog(①)'
let myDog: Animals = Animals(name: "Van")
print(myDog.name) // "Van"
// myDog(①)의 복사본, yourDog(②)
var yourDog = myDog
yourDog.name = "Coco"
print(myDog.name) // "Coco" -> name 프로퍼티의 값을 변경했더니, 원본 클래스 인스턴스의 프로퍼티 값도 변경!
print(yourDog.name) // "Coco"
구조체의 전반적인 내용을
기존에 다뤘던 유사한 형태인 클래스와 함께 살펴보았습니다.
미쳐 다루지 못한 프로토콜(Protocol)에 대한 포스팅으로 돌아오겠습니다.
'iOS > Swift' 카테고리의 다른 글
[iOS/Swift] immutable한 구조체, mutating을 적용한다면? (0) | 2023.03.07 |
---|---|
[iOS/Swift] 약속을 위한 청사진(Blueprint), 프로토콜(Protocol) (0) | 2023.03.05 |
[iOS/Swift] 상속(Inheritance)을 통한 재 정의, 오버라이딩(Overriding) (0) | 2023.02.19 |
[iOS/Swift] 클래스의 계층구조, 상속(Inheritance)의 개념과 활용 (0) | 2023.02.13 |
[iOS/Swift] 간단하게 알아보는 프로퍼티(Property)의 유형 (0) | 2023.02.12 |
댓글