관리 메뉴

나만을 위한 블로그

[Swift] 클래스와 구조체 본문

iOS/Swift

[Swift] 클래스와 구조체

참깨빵위에참깨빵 2024. 2. 11. 00:08
728x90
반응형

객체 지향 프로그래밍을 한다면 반드시 쓸 수밖에 없는 요소가 클래스다. 그런데 스위프트에는 클래스 뿐 아니라 구조체란 것도 있다.

역시 공식문서를 보면서 확인해 보자.

참고로 스위프트에선 객체보다 인스턴스라는 단어를 사용한다. 엄밀히 말하면 둘은 서로 다르지만, 스위프트가 처음 공부하는 언어라면 '객체 = 인스턴스'라 생각해도 상관없다.

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/

 

Documentation

 

docs.swift.org

struct, class는 프로그램 코드의 구성 요소가 되는 범용적, 유연한 구성이다. 상수, 변수, 함수를 정의하는 데 쓰는 것과 같은 구문을 써서 struct, class에 기능을 추가하는 속성, 메서드를 정의한다. 다른 언어와 다르게 스위프트는 커스텀 struct, class에 대해 별도의 인터페이스, 구현 파일을 만들 필요가 없다. 스위프트에선 단일 파일에 struct, class를 정의하면 해당 class, struct에 대한 외부 인터페이스를 자동으로 다른 코드에서 쓸 수 있게 된다

struct, class는 공통적으로 아래를 수행할 수 있다
- 값을 저장할 프로퍼티 정의
- 기능을 제공하는 메서드 정의
- 아래 첨자 구문을 정의해서 해당 값에 대한 접근을 제공
- 초기 상태 정의를 위한 이니셜라이저 정의
- 기본 구현 이상으로 기능을 확장 가능
- 특정 종류의 표준 기능을 제공하는 프로토콜 준수

클래스에는 struct엔 없는 추가 기능이 있다
- 상속으로 한 클래스가 다른 클래스의 특성을 상속받을 수 있음
- 타입 캐스팅을 쓰면 런타임 시 인스턴스의 타입을 확인하고 해석(interpret) 가능
- 초기화 해제(Deinitializers)를 쓰면 인스턴스가 할당한 리소스 해제 가능
- 참조 카운팅(Reference counting)은 인스턴스에 대한 둘 이상의 참조를 허용함

일반적으로 추론하기 더 쉽기 때문에 struct를 사용하고 필요한 경우에만 클래스를 사용한다. 실제로 이는 정의하는 대부분의 커스텀 타입이 struct 또는 enum이란 걸 의미한다
struct, class는 비슷한 정의 구문을 갖는다. struct 키워드 또는 class 키워드를 사용한다. 둘 다 전체 정의를 한 쌍의 중괄호 안에 넣는다
struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}
새 struct, class를 정의할 때마다 새 타입을 정의한다. 표준 스위프트 타입의 대문자 사용과 일치하도록 타입에 UpperCamelCase 이름(SomeStructure, SomeClass 등)을 제공한다. 프로퍼티와 메서드에 lowerCamelCase 이름을 지정해서 타입 이름과 구별하라
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}
(중략)...첫 번째 속성인 resolution에는 해상도 속성 타입을 유추하는 새로운 Resolution이라는 struct 인스턴스로 초기화된다...(중략)...name 속성은 옵셔널이기 때문에 기본값인 nil 또는 "no name value"가 자동 지정된다

Resolution structure와 VideoMode 클래스 정의는 Resolution, VideoMode의 모양만 설명한다. 그 자체로는 특정 해상도나 비디모 모드를 설명하지 않는다. 그렇게 하려면 struct나 클래스의 인스턴스를 만들어야 한다. 인스턴스 생성 구문은 struct, 클래스 모두 유사하다. 초기화 구문의 가장 간단한 형태는 클래스 또는 struct 이름 뒤에 소괄호를 쓰는 것이다. 그러면 모든 속성이 기본값으로 초기화된 클래스, struct의 인스턴스가 생성된다...(중략)
let someResolution = Resolution()
let someVideoMode = VideoMode()

 

여기까지는 자바/코틀린 중 하나라도 써 봤다면 무리없이 이해할 수 있을 것이다. 써보지 않았더라도 struct, 클래스 모두 특정 네이밍 체계를 갖고 있으며 중괄호 안에 해당 struct, 클래스가 가져야 하는 속성(프로퍼티)과 기능(메서드)들을 정의하고 바로 위 코드처럼 클래스의 인스턴스를 생성한다.

만약 VideoMode의 인스턴스인 someVideoMode를 통해 resolution 프로퍼티에 접근하려면 아래와 같이 할 수 있다.

 

struct Resolution {
  var width = 100
  var height = 100
}

class VideoMode {
  var resolution = Resolution()
  var interlaced = false
  var frameRate = 0.0
  var name: String?
}

let someResolution = Resolution()
let someVideoMode = VideoMode()

print(someVideoMode.resolution.height)

// 100

 

var은 수정 가능한 변수기 때문에 메인 파일 안에서 해당 값을 바꿀 수도 있다.

 

struct Resolution {
  var width = 100
  var height = 100
}

class VideoMode {
  var resolution = Resolution()
  var interlaced = false
  var frameRate = 0.0
  var name: String?
}

let someResolution = Resolution()
let someVideoMode = VideoMode()

print(someVideoMode.resolution.height)

someVideoMode.resolution.height = 200

print(someVideoMode.resolution.height)

// 100
// 200

 

또한 구조체는 값 유형에 속한다. 값 유형이 뭔지에 대해선 아래를 참고한다.

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/#Structures-and-Enumerations-Are-Value-Types

 

Documentation

 

docs.swift.org

값 유형은 변수, 상수에 할당되거나 함수에 전달될 때 값이 복사되는 타입이다. 스위프트의 모든 기본형(정수, 부동 소수점 숫자, 부울, 문자열, 배열, 딕셔너리)은 값 유형이다. 모든 struct와 enum은 값 유형이다. 즉 생성한 모든 struct와 enum 인스턴스, 속성으로 포함된 모든 값 유형은 코드에서 전달될 때 항상 복사된다

 

반면 클래스는 참조 타입이다.

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/#Classes-Are-Reference-Types

 

Documentation

 

docs.swift.org

참조 타입은 변수, 상수에 할당되거나 함수에 전달될 때 복사되지 않는다. 복사본 대신 같은 기존 인스턴스에 대한 참조가 사용된다

 

스위프트는 값 유형, 참조 유형으로 구조체와 클래스의 특성이 나뉘는 반면, 코틀린에선 모든 변수는 객체로 취급되기 때문에 모든 변수는 참조 타입으로 취급된다.

구조체는 값 유형이고 클래스는 참조 유형이란 건 알겠지만 위 설명만으론 값 유형, 참조 유형의 설명이 조금 부족하다. 다른 스위프트 문서를 참고한다.

 

https://www.swift.org/documentation/articles/value-and-reference-types.html

 

Swift.org

Swift is a general-purpose programming language built using a modern approach to safety, performance, and software design patterns.

www.swift.org

스위프트의 struct, enum, tuple은 모두 값 유형이다. 친구에게 문서 사본을 보내는 것과 유사하게 작동한다. 상수, 변수에 값을 할당하거나 함수, 메서드에 값을 전달하면 항상 값의 사본이 만들어진다. 아래 코드에서 Document 유형의 구조체가 하나의 프로퍼티 텍스트로 선언돼 있다
struct Document {
  var text: String
}

var myDoc = Document(text: "Great new article")
var friendDoc = myDoc

friendDoc.text = "수정"

print(friendDoc.text)
print(myDoc.text)

// 수정
// Great new article
Document 인스턴스가 생성되서 myDoc에 할당된다. myDoc이 friendDoc에 할당되면 원본 인스턴스가 새 인스턴스로 복사된다. 친구에게 문서 사본을 보낼 때 사본이 언제 바뀌는지 완벽하게 제어할 수 있다. 친구가 내 문서 사본을 예기치 않게 변경할 것을 걱정할 필요가 없다. 마찬가지로 값 유형을 쓰면 프로그램의 다른 부분이 값을 바꾸는 것을 걱정할 필요가 없다
스위프트의 클래스, 액터, 클로저는 모두 참조 유형이다. 친구에게 공유 문서 링크를 보내는 것과 비슷하게 작동한다. 상수, 변수에 참조 유형을 할당하거나 함수, 메서드에 전달하면 항상 할당되거나 전달된 참조 유형은 공유 인스턴스에 대한 참조가 된다. 아래 코드는 위와 동일하지만 struct 대신 class로 선언됐다는 변경점이 있다
class Document {
  var text: String
}

var myDoc = Document(text: "Great new article")
var friendDoc = myDoc

friendDoc.text = "Blah blah blah"

print(friendDoc.text)
print(myDoc.text)

// Blah blah blah
// Blah blah blah
Document 인스턴스가 만들어져 myDoc에 할당되기 전과 같다. 하지만 이제 myDoc이 friendDoc 변수에 할당되면 할당된 인스턴스에 대한 참조가 된다. 같은 인스턴스에 대한 참조라서 friendDoc의 text를 바꾸면 myDoc의 값을 포함해서 해당 공유 인스턴스가 수정된다. 친구에게 공유 문서의 링크를 보내면 내가 모르는 새 문서를 바꿀 수 있다. 문서가 그대로 유지되어야 할 수 있다. 마찬가지로 참조 유형을 쓰면 참조가 있는 프로그램의 모든 부분이 바뀔 수 있다

 

Document 클래스 예시를 replit에서 사용하면 에러가 발생한다. 이니셜라이저가 없고, Document의 생성자에 "Great new article"을 넣어서 에러가 발생하는데, 아래처럼 수정하면 작동한다. 예시 앱을 만드는 것도 아닌 기본 구성요소를 설명하는 문서인데, 이 문서의 코드를 그대로 실행하면 에러가 발생하는 건 좀 아닌 것 같다.

 

class Document {
  var text: String

  init() {
    text = ""
  }
}

var myDoc = Document()
var friendDoc = myDoc

friendDoc.text = "Blah blah blah"

print(friendDoc.text)
print(myDoc.text)

 

아무튼 value type, reference type을 정리하면

 

  • value type : 값을 복사해서 전달
  • reference type : 값의 메모리 위치를 전달

 

메모리 위치라고 하면 어려울 수 있으니 처음에는 값의 링크를 전달한다고 이해하고 넘어가도 될 듯하다.

또한 클래스의 생성자는 init 블록을 써서 작성하고 클래스 내부 변수가 처음부터 어떤 값이어야 하는 경우 사용한다.

그리고 init 블록 안에서 text 앞에 "self."를 붙여서 쓸 수도 있다.

 

class Document {
  var text: String

  init() {
    self.text = "수정 전"
  }
}

var myDoc = Document()
print(myDoc.text)

// 수정 전

 

여기서 self는 클래스 or 구조체 자기 자신을 가리킨다. 즉 Document 클래스의 init 블록 안에서 self.text는 Document 클래스의 document 변수를 의미하는 것이다. 자바 / 코틀린의 this와 같은 용도라고 보면 된다.

반응형

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

[Swift] 반복문 (for-in, while, repeat-while)  (0) 2024.01.17
[Swift] 조건문 (if, switch)  (0) 2024.01.15
[Swift] 변수와 상수, 자료형  (0) 2024.01.14
[Swift] Swift란?  (0) 2024.01.11
Comments