21. 10. 18 구조체와 클래스
Structures and Classes
- 구조체와 클래스의 공통점
- 값을 저장할 프로퍼티를 정의할 수 있다.
- 기능을 제공하는 메소드를 정의할 수 있다.
- subscripts 문법을 통해 프로퍼티에 접근하도록 subscripts를 정의할 수 있다.
- 초기화 상태를 설정하기 위한 이니셜라이저를 정의할 수 있다.
- 기본 구현 이상으로 기능을 확장하도록 extension을 사용할 수 있다.
- 특정 종류의 표준 기능을 제공하는 프로토콜을 준수할 수 있다.
- 구조체와 클래스의 차이점
- 클래스에는 구조체에 없는 추가 기능이 있다.
- 상속을 통해 한 클래스가 다른 클래스의 특성을 상속할 수 있다. (구조체는 안됨)
- 타입 캐스팅을 사용하면 런타임에 클래스 인스턴스의 타입을 확인하고 해석할 수 있다.
Deinitializer
는 클래스의 인스턴스가 할당된 리소스를 해제할 수 있도록 한다.
- 참조 카운팅은 클래스 인스턴스에 대한 둘 이상의 참조를 허용한다.
- 클래스가 지원하는 추가 기능은 복잡성이 증가한다.
- 일반적으로 구조체는 추론하기 쉽기 때문에 선호되고 필요할 때 클래스를 사용한다. 대부분의 사용자 정의 데이터 타입은 구조체 및 열거형으로 되어있다.
/* Resolution과 VideoMode의 모양만 설명하는 것.
특정한 실체를 만들려면 구조체나 클래스의 인스턴스를 만들어야 한다. */
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
// 클래스 초기화
VideoMode
클래스에는 4개의 가변 저장 프로퍼티가 있다.
var resolution = Resolution()
은 구조체 Resolution
의 프로퍼티 타입을 유추하는 새로운 Resolution
구조 인스턴스로 초기화 된다.
name
은 옵셔널 타입이기 때문에 기본값으로 nil 또는 "이름 값 없음"이 자동으로 지정된다.
Structure and Class Instances
// 인스턴스 만드는 방법
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"
// 프로퍼티에 새로운 값 할당해서도 사용 가능
- 인스턴스의 프로퍼티를 사용하려면
someResolution.width
와 같은 방법으로 접근할 수 있다.
- 이 예제에서
someResolution.width
는 someResolution
의 너비 프로퍼티를 참조하고 기본 초기 값인 0을 반환한다.
Memberwise Initializers for Structure Types
- 모든 구조체에는 자동으로 생성된 멤버별 이니셜라이저가 있어 새 구조체 인스턴스의 멤버 속성을 초기화하는데 사용할 수 있다. 새 인스턴스의 속성에 대한 초기 값은 이름으로 멤버별 이니셜라이저에 전달할 수 있다.
let vga = Resolution(width: 640, height: 480)
- 구조체와 달리 클래스 인스턴스는 기본 멤버별 이니셜라이저를 받지 않는다.
구조체와 열거형은 값 타입
이다.
- 값 타입은 변수나 상수에 할당되거나 함수에 전달될 때 값이 복사되는 타입이다.
- Swift의 모든 기본 타입(integers, floating-point numbers, Booleans, strings, arrays and dictionaries)은 값 타입이며 구조체로 구현되어 있다.
- 생성한 모든 구조체 및 열거형 인스턴스와 프로퍼티로 포함된 모든 값 타입은 코드에서 전달될 때 항상 복사된다는 의미.
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
// 이렇게 cinema의 넓이 값을 변경 가능
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
// cinema의 넓이 값이 실제로 바뀌었지만,
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
// hd의 넓이는 변하지 않고 그대로!
Resolution
은 구조체이기 때문에 기존 인스턴스의 복사본이 만들어지고 이 새 복사본이 cinema
에 할당 된다. hd
와 cinema
의 너비와 높이가 동일하지만 완전히 다른 두 인스턴스이다.
- 열거형에서도 똑같이 적용된다.
- 값을 저장할 프로퍼티를 정의할 수 있다.
- 기능을 제공하는 메소드를 정의할 수 있다.
- subscripts 문법을 통해 프로퍼티에 접근하도록 subscripts를 정의할 수 있다.
- 초기화 상태를 설정하기 위한 이니셜라이저를 정의할 수 있다.
- 기본 구현 이상으로 기능을 확장하도록 extension을 사용할 수 있다.
- 특정 종류의 표준 기능을 제공하는 프로토콜을 준수할 수 있다.
- 클래스에는 구조체에 없는 추가 기능이 있다.
- 상속을 통해 한 클래스가 다른 클래스의 특성을 상속할 수 있다. (구조체는 안됨)
- 타입 캐스팅을 사용하면 런타임에 클래스 인스턴스의 타입을 확인하고 해석할 수 있다.
Deinitializer
는 클래스의 인스턴스가 할당된 리소스를 해제할 수 있도록 한다.- 참조 카운팅은 클래스 인스턴스에 대한 둘 이상의 참조를 허용한다.
/* Resolution과 VideoMode의 모양만 설명하는 것.
특정한 실체를 만들려면 구조체나 클래스의 인스턴스를 만들어야 한다. */
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
// 클래스 초기화
VideoMode
클래스에는 4개의 가변 저장 프로퍼티가 있다.var resolution = Resolution()
은 구조체 Resolution
의 프로퍼티 타입을 유추하는 새로운 Resolution
구조 인스턴스로 초기화 된다.name
은 옵셔널 타입이기 때문에 기본값으로 nil 또는 "이름 값 없음"이 자동으로 지정된다.// 인스턴스 만드는 방법
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"
// 프로퍼티에 새로운 값 할당해서도 사용 가능
someResolution.width
와 같은 방법으로 접근할 수 있다.someResolution.width
는 someResolution
의 너비 프로퍼티를 참조하고 기본 초기 값인 0을 반환한다.let vga = Resolution(width: 640, height: 480)
값 타입
이다.let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
// 이렇게 cinema의 넓이 값을 변경 가능
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
// cinema의 넓이 값이 실제로 바뀌었지만,
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
// hd의 넓이는 변하지 않고 그대로!
Resolution
은 구조체이기 때문에 기존 인스턴스의 복사본이 만들어지고 이 새 복사본이 cinema
에 할당 된다. hd
와 cinema
의 너비와 높이가 동일하지만 완전히 다른 두 인스턴스이다.클래스는 참조 타입
이다.
- 값 타입과 달리 참조 타입은 변수나 상수에 할당되거나 함수에 전달될 때 복사되지 않는다. 복사본 대신 동일한 기존 인스턴스에 대한 참조가 사용된다.
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
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"
- 이 예제는 참조 타입에 대해 추론하기가 더 어려울 수 있음을 보여준다. 프로그램 코드에서
tenEighty
와alsoTenEighty
가 멀리 떨어져 있으면 비디오 모드가 변경되는 모든 방법을 찾기 어려울 수 있다. 또한,tenEighty
를 사용할 때마다alsoTenEighty
를 사용하는 코드에 대해서도 생각해야 하고 그 반대의 경우도 마찬가지이다. 이와 반대로, 값 타입은 동일한 값과 상호작용하는 모든 코드가 소스파일에서 가까이 있기 때문에 추론하기가 더 쉽다. tenEighty
및alsoTenEighty
는 상수로 선언되어 있지만 상수 자체의 값은 실제로 변경되지 않기 때문에 여전히tenEighty.frameRate
및alsoTenEighty.frameRate
를 변경할 수 있다.tenEighty
및alsoTenEighty
자체는VideoMode
인스턴스를 "저장"하지 않는다. 대신 둘 다 뒤에서VideoMode
인스턴스를 참조한다. 변경된 것은 해당VideoMode
에 대한 상수 참조 값이 아니라 기본VideoMode
의frameRate
속성이다.
디이니셜라이저(Deinitializer)
- 클래스의 인스턴스는 참조 타입이어서 더이상 참조할 필요가 없을 때 메모리에서 소멸된다. 이 때 소멸 직전에 deinit이라는 메서드가 호출되는데, 클래스 내부에 deinit 메서드를 구현하면 소멸되기 직전에 해당 메서드가 호출된다. deinit 메서드는 클래스당 하나만 구현할 수 있고, 매개변수와 반환 값을 가질 수 없다.
- deinit에는 인스턴스가 메모리에서 소멸되기 직전에 처리할ㄹ 코드를 작성한다. (ex. 인스턴스 소멸 전에 데이터를 저장하거나 다른 객체에 소멸 사실을 알려야 할 때, deinit 메서드를 사용해야한다!)
class Coffee {
var size: String
var brand: String
deinit {
print("Coffee 클래스의 ㅇ니스턴스가 소멸됩니다.")
}
}
var americano: Coffee? = Coffee()
americano = nil
Identity Operators
- 클래스는 참조 타입이기 때문에 여러 상수와 변수가 뒤에서 클래스의 동일한 단일 인스턴스를 참조할 수 있다. (구조체와 열거형은 상수나 변수에 할당되거나 함수에 전달될 때 항상 복사되기 때문에 안됨)
- 두 개의 상수 또는 변수가 클래스의 정확히 동일한 인스턴스를 참조하는지 여부를 확인하기 위해 Swift는 두가지 연산자를 제공한다.
-
동일함 === / 동일하지 않음 !==
if tenEighty === alsoTenEighty { print("tenEighty and alsoTenEighty refer to the same VideoMode instance.") } // Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
-
- ===는 ==와 동일 한 것을 의미하지 않는다. ===은 클래스 타입의 두 상수 또는 변수가 정확히 동일한 클래스 인스턴스를 참조한다는 것을 의미한다. ==는 타입 디저이너가 정의한 같음의 적절한 의미에 대해 두 인스턴스가 값이 같거나 동등한 것으로 간주됨을 의미한다.
다음 조건 중 하나 이상에 해당된다면 구조체를 사용하면 좋다 (feat. apple guideline)
- 연관된 간단한 값의 집합을 캡슐화 하는 것만이 목적일 때
- 캡슐화된 값이 참조되는 것보다 복사되는 것이 합당할 때
- 구조체에 저장된 프로퍼티가 값 타입이며 참조되는 것보다 복사되는 것이 합당할 때
- 다른 타입으로부터 상속받거나 자신이 상속될 필요가 없을 때
이런 몇 가지 상황을 제외하면 클래스로 정의하여 사용한다. 대다수 사용자정의 데이터 타입은 클래스로 구현할 일이 더 많을 수 있다.
참고자료
Structures and Classes - The Swift Programming Language (Swift 5.5)
Swift - 구조체 클래스 - yagom's blog
Swift) 프로퍼티 정복하기 (2/4) - 연산 프로퍼티(Computed Property)
Author And Source
이 문제에 관하여(21. 10. 18 구조체와 클래스), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@allie/TIL-iqchioyi저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)