Struct
상속
- 클래스를 받아올 때 사용하는 단어
- 부모 클래스에 있는 모든 기능을 자식 클래스가 물려 받겠다는 의미
class ViewController : UIViewController { }
- [코드 1] : 'ViewController' 라는 클래스 (자식 클래스) 가 'UIViewController' 라는 클래스 (부모 클래스) 를 상속받는 것이기 때문에 'ViewController' 클래스는 'UIViewController' 클래스의 모든 기능을 물려 받는다.
채택
- 프로토콜을 받아올 때 사용하는 단어
- 채택한 프로토콜에 있는 것들을 준수하겠다는 의미
struct Point {
var x = 0.0
var y = 0.0
}
let point = Point.init()
var clonePoint = point
clonePoint.x = 5
print(clonePoint.x)
print(point.x)
- [코드 2], [코드 3] 를 실행하면 [코드 3] 의 네번째 줄에 의해 5.0, [코드 3] 의 다섯번째 줄에 의해 0.0 이 출력된다.
- [코드 3] 의 'clonePoint' 에는 'point' 의 복사본이 들어간다. (clonePoint 와 point 는 각각 다른 개체라고 볼 수 있다.)
Class
class Point {
var x = 0.0
var y = 0.0
}
- [코드 3], [코드 4] 을 실행하면 [코드 3] 의 네번째 줄에 의해 5.0, [코드 3] 의 다섯번째 줄에 의해 5.0 이 출력된다.
- [코드 3] 의 'clonePoint' 에는 'point' 의 메모리 주소가 들어간다. (clonePoint 와 point 는 똑같이 원본인 Point 의 주소를 가리키는 것이다.)
Enum
- 열거형
- 값 타입
- 상속이 불가능하다.
- C 나 C++ 에서 열거형을 선언하면 따로 값을 지정해주지 않았을 때 처음 값이 0 으로 자동 할당되지만, Swift 에서는 그렇지 않다. (다른 언어로 공부한 열거형이랑은 다르다.)
- 유사한 종류의 여러 값을 유의미한 이름으로 한 곳에 모아 정의한 것
- 열거형 자체가 하나의 데이터 타입이다.
- 값이 없을 수 있다. (할당된 값이 없으면 없는대로 존재하기 때문에 할당된 값이 없어도 기본값이 할당되지 않는다.)
- extension 이 가능하다.
- rawValue 를 통해 원시값을 지정할 수 있다.
enum Animal {
case tiger
case bear
case dog
}
let myTiger : Animal = Animal.tiger
let myBear : Animal = .bear
- [코드 5], [코드 6] 을 실행하면 [코드 6] 의 첫번째 줄에서 Animal 타입의 인스턴스인 'myTiger' 이 생성된다.
- [코드 6] 의 두번째 줄에서 Animal 타입의 인스턴스인 'myBear' 이 생성된다.
print(myTiger)
print(myBear)
- [코드 5], [코드 6], [코드 7] 을 실행하면 [코드 7] 의 첫번째 줄에 의해 tiger, 두번째 줄에 의해 bear 가 출력된다. (값을 따로 주지 않아도 이름 그 자체를 의미한다는 것을 알 수 있다.)
enum Animal : Int {
case tiger = 0
case bear
case dog
}
- [코드 8] : Animal 타입이 Int 를 상속하는 것이 아니다. (열거형은 상속이 불가능하기 때문이다.)
- C 나 C++ 에서 사용하는 열거형을 Swift 에서 사용하고 싶다면 [코드 8] 처럼 쓴 후에 rawValue (원시값) 를 사용하면 된다. ([코드 9] 참고)
원시값
- String, Int 등 여러 가지 타입 모두 가능하다.
- 각각의 원시값은 열거형 정의 안에서 반드시 유일해야 한다. (하나의 열거형 안에서 원시값이 겹치면 안된다.)
- 원시값으로 정수가 부여되었다면 열거형 멤버의 일부에 아무 값도 넣어주지 않아도 자동 증가되어 넣어진다. ([코드 10] 참고)
- 원시값을 통한 인스턴스 생성은 옵서녈 값으로 반환된다. ([코드 13] 참고)
let myTiger : Animal = Animal.tiger
let myDog : Animal = .dog
print(myTiger.rawValue)
print(myDog.rawValue)
- [코드 8], [코드 9] 를 실행하면 [코드 8] 의 두번째 줄에서 tiger 에게 원시값으로 0 을 주었기 때문에 [코드 9] 의 세번째 줄에서 0 이 출력되고, [코드 9] 의 네번째 줄에서 2 가 출력된다. ([코드 8] 의 bear 에는 1 이 자동으로 할당된다.)
enum Animal : Double {
case tiger = 0
case bear
case dog
}
- [코드 9], [코드 10] 를 실행하면 [코드 10] 의 두번째 줄에서 tiger 에게 원시값으로 0 을 주었기 때문에 [코드 9] 의 세번째 줄에서 0.0 이 출력되고, [코드 9] 의 네번째 줄에서 2.0 이 출력된다. ([코드 10] 의 bear 에는 1.0 이 자동으로 할당된다.)
enum Animal : Double {
case tiger = 0.1
case bear
case dog
}
- [코드 9], [코드 11] 을 실행하면 'Enum case must declare a raw value when the preceding raw value is not an integer' 라는 에러가 발생하는데, 이는 이전의 원시값이 정수가 아닐 경우 enum case 의 원시값을 정의해줘야 한다는 뜻이다. (bear 에 원시값을 1 로 줘야 할지 1.1 로 줘야 할지 애매하기 때문이다.)
enum Animal : String {
case tiger = 'A'
case bear
case dog
}
- [코드 9], [코드 12] 를 실행하면 [코드 9] 의 세번째 줄에 의해 A 가 출력되고, [코드 9] 의 네번째 줄에 의해 C 가 출력된다고 생각할 수 있지만 C 대신 dog 가 출력된다.
- 이는 열거형이 이름 (String 타입) 그 자체만으로 의미를 가지기 때문이다. (원시값이 정수 타입이 아니기 때문에 자동 증가되지 않는다.)
let newAnimal = Animal(rawValue: 4)
print(newAnimal)
- [코드 8], [코드 13] 을 실행하면 [코드 13] 의 두번째 줄에 의해 4 가 출력될 것 같지만 실제로 실행해보면 nil 이 출력된다.
- 이를 통해 새로 열거형 인스턴스를 생성하면 nil 로 생성된다는 것을 알 수 있다.
Struct vs Class
공통점
- 서로 다른 타입들을 하나로 묶을 수 있다.
- 묶인 자료형들을 새로운 타입처럼 사용할 수 있다.
- 안에서 함수 / 프로퍼티 를 정의할 수 있다.
- extension 이 가능하다.
- 프로토콜을 사용할 수 있다.
차이점
- struct : 값 타입 / class : 참조 타입
- struct : 상속 불가능 & 채택 가능 / class : 상속 가능
참고