구조체와 클래스는 프로그램의 코드의 구성요소가 되게 하는 유연한 범용 구조이다. 상수, 변수, 함수의 정의 문법으로 프로퍼티와 메소드 기능을 구조체와 클래스 내부에 정의가 가능하다.
다른 언어와는 다르게, 스위프트는 사용자화 구조체, 클래스로 구현된 파일과 분리된 인터페이스를 만드라고 요구하지 않는다. 스위프트 내에서, 하나의 파일에 클래스 또는 구조체를 정의하고, 자동적으로 다른 코드에서 사용하기 위해 외부 인터페이스를 정의한다.
NOTE
클래스의 인스턴스는 전통적으로object
라고 알려져 있다. 그러나 스위프트 구조체 클래스는 다른 언어에 비해 좀 더 기능적으로 가깝고, 이 챕터의 대부분을 클래스와 구조체 타입의 인스턴스에 적용되는 기능에 대해 설명할것이다. 따라서 여기서는instance
용어를 사용한다.
스위프트의 구조체와 클래스는 많은 공통점이 있다.
클래스는 구조체에는 없는 추가 특징이 있다.
클래스의 추가 기능은 복잡성을 증가 시킨다. 일반적인 가이드라인은 구조체를 위 같은 이유로 선호하고, 추가 기능이 적절하거나, 필수일때 클래스를 사용해라. 실제로, 이는 많은 사용자화 데이터 타입을 구조체와 열거형으로 정의하고 있음을 의미한다.
구조체와 클래스는 동일한 정의 구문을 가진다.
struct SomeStructure {
// structure 정의 작성 부분
}
class SomeClass {
// class 정의 작성 부분
}
NOTE
새로 구조체와 클래스를 정의할 때UpperCamelCase
를 따라야 한다. 프로퍼티와 메소드는lowerCamelCase
를 따라야한다.
// 픽셀 기반 디스플레이 해상도 구조체 예시
struct Resolution {
var width = 0
var height = 0
}
// 비디오 디스플레이의 비디오 모드를 특정하는 클래스 예시
class VideoMode {
// Resolution 구조체의 인스턴스로 초기화
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
저장된 속성은 구조체 또는 클래스의 일부로 묶어서 저장되는 상수나 변수이다.
구조체 정의와 클래스 정의는 해상도와 비디오모드의 모양만 설명한다. 그 자체로 특정 해상도나 비디오 모드를 설명하지 않는다. 그렇게 하려면, 구조체 또는 클래스의 인스턴스를 만들 필요가 있다.
let someResolution = Resolution()
let someVideoMode = VideoMode()
점 구문을 사용하여 인스턴스의 프로퍼티에 접근이 가능하다.
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
// 하위 속성 안에서도 사용 가능
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
// 할당도 가능하다~
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
모든 구조체는 자동적으로 생성된 새 구조체의 인스턴스의 멤버 프로퍼티를 초기화 하기위해 사용 가능한 멤버별 초기화를 가지고 있다. 새 인스턴스의 속성에 대한 초기값은
let vga = Resolution(width: 640, height: 480)
값 타입은 상수나 변수에 할당되거나 함수로 전달할때 값이 복사되는 타입이다.
사실, 스위프트의 모든 기본 타입(정수형, 실수, 불린, 문열, 배열, 딕셔너리)는 값 타입이고, 내부에서 구조체로서 구현되었다.
모든 구조체와 열거형은 스위프트에서 값 타입이다. 이는, 생성한 어떤 구조체나 열거형 인스턴스와 속성으로 포함된 모든 값 타입은 코드에서 전달 시 항상 복사된다.
NOTE
컬렉션은 복사 성능의 비용을 줄이기 위해 최적화를 사용한다. 즉시 복사하는것 대신에, 이 컬렉션은 복사본과 원본 사이에 저장된 원소의 메모리를 공유한다. 컬렉션의 복사본이 변경된다면, 변경 직전에 원소가 복사된다. 이 동작은 항상 복사가 즉시 발생한 것처럼 보인다.
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// 2048 pixels wide
print("hd is still \(hd.width) pixels wide")
// 1920 pixels wide
위 예제는 hd
상수를 호출하고 Resolution
의 인스턴스(HD video 1920*1080)로 초기화 설정을 하였다.
cinema
에 할당을 하였지만, 두 상수와 변수는 내부에서 엄연히 다른 인스턴스이다.
시네마에 할당시, hd
에 저장된 값은 새로운 시네마 인스턴스로 복사된다. 결과 값은 엄연히 다른 두개의 인스턴스이다.
열거형도 같은 동작을 보인다
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"
값 타입과 다르게 참조 타입은 상수나 변수에 할당하거나, 함수로 전달시 복사되지 않는다. 복사 대신에 같은 인스턴스를 참조를 사용한다.
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
클래스는 참조 타입이기 때문에, 위 두 상수는 실제로 같은 인스턴스를 참조한다. 실제로, 하나의 같은 인스턴스의 다른 이름일 뿐이다.
속성 값을 변경하면 다른 이름에서도 호출 시 똑같은 값을 가지고 있다.
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
위 예제는 참조 타입의 추론이 더 어려움을 보여준다. 위 상수들이 멀리 떨어져있으면 안의 원소가 변경되는 모든 방법을 찾기 어려울 수 있다. 같은 인스턴스를 참조하는 상수 또는 변수를 사용시 생각을 잘 해야한다. 반대로, 값 타입은 참조 타입과 다르게 멀리 떨어져있지 않기 때문에 좀 더 추론하기 쉽다.
위 상수들은 인스턴스를 저장 하지는 않고, 내부에서 인스턴스를 참조한다. 실제로 변경된 것은 VideoMode
의 속성값이며, 상수의 참조 값이 변경된 것이 아니다.
클래스는 참조 타입이기 때문에, 여러개의 상수나 변수를 같은 단일 인스턴스를 참조하게끔 가능하다.
이것은 가끔 두 상수 또는 변수가 동일 클래스의 인스턴스를 참조하는지 알려고 할 때 유용하다. 이것을 가능하게 하기 위해 스위프트는 두가지 식별 연산자를 제공한다.
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
===
는 ==
와 같지 않다. 동등 연산자는 두 개의 인스턴스가 값이 같은지 비교하고, 일치 연산자는 두 개의 상수 또는 변수가 같은 클래스 인스턴스를 참조하는지 비교한다.
커스텀 구조체 또는 클래스를 정의할 때, 두 인스턴스가 같은지에 대한 여부의 결정의 책임은 우리한테 있다.
C, C++, Objective-C의 경험이 있다면 매모리에 주소를 참조하는 포인터에 대해 알것이다. 스위프트의 일부 참조 타입의 인스턴스를 참조하는 상수 또는 변수는 포인터와 비슷하다. 하지만 메모리 주소에 직접 포인터하는 것은 아니다. 대신 이 참조는 스위프트에 다른 모든 상수 또는 변수를 정의하는것 처럼 정의된다. 표준 라이브러리는 포인터와 직접 상호 작용해야 하는 경우 사용이 가능한 포인터와 버퍼 타입을 제공한다.