Swift study - Structures and Classes

rbw·2022년 3월 12일
0

swift-study

목록 보기
12/17
post-thumbnail

Swift - Structures and Classes

구조체와 클래스는 프로그램의 코드의 구성요소가 되게 하는 유연한 범용 구조이다. 상수, 변수, 함수의 정의 문법으로 프로퍼티와 메소드 기능을 구조체와 클래스 내부에 정의가 가능하다.

다른 언어와는 다르게, 스위프트는 사용자화 구조체, 클래스로 구현된 파일과 분리된 인터페이스를 만드라고 요구하지 않는다. 스위프트 내에서, 하나의 파일에 클래스 또는 구조체를 정의하고, 자동적으로 다른 코드에서 사용하기 위해 외부 인터페이스를 정의한다.

NOTE

클래스의 인스턴스는 전통적으로 object라고 알려져 있다. 그러나 스위프트 구조체 클래스는 다른 언어에 비해 좀 더 기능적으로 가깝고, 이 챕터의 대부분을 클래스와 구조체 타입의 인스턴스에 적용되는 기능에 대해 설명할것이다. 따라서 여기서는 instance 용어를 사용한다.

Comparing Structures and Classes (구조체와 클래스의 비교)

스위프트의 구조체와 클래스는 많은 공통점이 있다.

  • 값을 저장하기 위해 프로퍼티를 정의한다
  • 기능을 제공하기 위해 메소드를 정의한다
  • 서브스크립트 구문을 사용하여 값에 접근을 제공하기 위해 서브스크립트를 정의한다
  • 초기 상태를 설정하기 위해 이니셜라이저(초기화)를 정의한다
  • 기본 구현을 넘어 기능을 확장하기 위해 확장 된다
  • 특정 종류의 표준 기능을 제공하기 위해 프로토콜을 준수한다

클래스는 구조체에는 없는 추가 특징이 있다.

  • 상속은 하나의 클래스로, 다른 클래스의 특징의 상속을 가능하게 한다.
  • 런타임 시에, 타입 캐스팅은 클래스 인스턴스의 타입을 체크하고 해석하는것을 가능하게 한다.
  • 소멸자는 클래스의 인스턴스가 할당된 리소스를 해제할 수 있게 한다
  • 참조 카운팅은 하나 이상의 클래스 인스턴스 참조가 가능하다

클래스의 추가 기능은 복잡성을 증가 시킨다. 일반적인 가이드라인은 구조체를 위 같은 이유로 선호하고, 추가 기능이 적절하거나, 필수일때 클래스를 사용해라. 실제로, 이는 많은 사용자화 데이터 타입을 구조체와 열거형으로 정의하고 있음을 의미한다.

Definition Syntax (정의 구문)

구조체와 클래스는 동일한 정의 구문을 가진다.

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?
}

저장된 속성은 구조체 또는 클래스의 일부로 묶어서 저장되는 상수나 변수이다.

Structure and Class instances (구조체와 클래스 인스턴스)

구조체 정의와 클래스 정의는 해상도와 비디오모드의 모양만 설명한다. 그 자체로 특정 해상도나 비디오 모드를 설명하지 않는다. 그렇게 하려면, 구조체 또는 클래스의 인스턴스를 만들 필요가 있다.

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

Accessing Properties (프로퍼티 접근)

점 구문을 사용하여 인스턴스의 프로퍼티에 접근이 가능하다.

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"

Memberwise Initializers for Structure Types (구조체 타입에 대한 멤버별 초기화)

모든 구조체는 자동적으로 생성된 새 구조체의 인스턴스의 멤버 프로퍼티를 초기화 하기위해 사용 가능한 멤버별 초기화를 가지고 있다. 새 인스턴스의 속성에 대한 초기값은

let vga = Resolution(width: 640, height: 480)

Structures and Enumerations Are Value Types (구조체와 열거형은 값 타입)

값 타입은 상수나 변수에 할당되거나 함수로 전달할때 값이 복사되는 타입이다.

사실, 스위프트의 모든 기본 타입(정수형, 실수, 불린, 문열, 배열, 딕셔너리)는 값 타입이고, 내부에서 구조체로서 구현되었다.

모든 구조체와 열거형은 스위프트에서 값 타입이다. 이는, 생성한 어떤 구조체나 열거형 인스턴스와 속성으로 포함된 모든 값 타입은 코드에서 전달 시 항상 복사된다.

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"

Classes Are Reference Types (클래스는 참조 타입)

값 타입과 다르게 참조 타입은 상수나 변수에 할당하거나, 함수로 전달시 복사되지 않는다. 복사 대신에 같은 인스턴스를 참조를 사용한다.

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의 속성값이며, 상수의 참조 값이 변경된 것이 아니다.

Identity Operators (식별 연산자)

클래스는 참조 타입이기 때문에, 여러개의 상수나 변수를 같은 단일 인스턴스를 참조하게끔 가능하다.

이것은 가끔 두 상수 또는 변수가 동일 클래스의 인스턴스를 참조하는지 알려고 할 때 유용하다. 이것을 가능하게 하기 위해 스위프트는 두가지 식별 연산자를 제공한다.

  • 일치 연산자 (===)
  • 일치하지 않은 연산자 (!==)
if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

=====와 같지 않다. 동등 연산자는 두 개의 인스턴스가 값이 같은지 비교하고, 일치 연산자는 두 개의 상수 또는 변수가 같은 클래스 인스턴스를 참조하는지 비교한다.

커스텀 구조체 또는 클래스를 정의할 때, 두 인스턴스가 같은지에 대한 여부의 결정의 책임은 우리한테 있다.

Pointers (포인터)

C, C++, Objective-C의 경험이 있다면 매모리에 주소를 참조하는 포인터에 대해 알것이다. 스위프트의 일부 참조 타입의 인스턴스를 참조하는 상수 또는 변수는 포인터와 비슷하다. 하지만 메모리 주소에 직접 포인터하는 것은 아니다. 대신 이 참조는 스위프트에 다른 모든 상수 또는 변수를 정의하는것 처럼 정의된다. 표준 라이브러리는 포인터와 직접 상호 작용해야 하는 경우 사용이 가능한 포인터와 버퍼 타입을 제공한다.

profile
hi there 👋

0개의 댓글