간단하게는 앞에 static이 붙지 않으면 인스턴스 멤버, 붙으면 타입 멤버이다.
struct Sample {
// 인스턴스 프로퍼티
var mutableProperty: Int = 100
let immutableProperty: Int = 100
// 타입 프로퍼티
static var typeProperty: Int = 100
// 인스턴스 메서드
func instanceMethod() {
print("instance method")
}
// 타입 메서드
static func typeMethod() {
print("type method")
}
}
인스턴스 멤버(프로퍼티 & 메서드)는 말 그대로 인스턴스를 생성해야 사용이 가능하다.
반면, 타입 멤버는 인스턴스 없이 타입 이름만 알면 접근 가능하다.
// 인스턴스 프로퍼티
var mutable: Sample = Sample()
mutable.mutableProperty = 200
// 타입 메서드와 프로퍼티
Sample.typeProperty = 300
Sample.typeMethod()
타입 메서드에서 인스턴스 멤버는 사용할 수 없다. 왜냐하면 인스턴스 멤버는 인스턴스를 선언해야만 저장 공간을 갖기 때문이다.
class Eve {
let name = "eve"
static let alias = "colli"
static func sayHello() {
print(name) // error! Instance member 'name' cannot be used on type 'Eve'
print(alias)
}
}
하지만 인스턴스 메서드에서는 인스턴스 멤버와 타입 멤버 모두에 접근이 가능하다.
class Eve {
let name = "eve"
static let alias = "colli"
func sayHello() {
print(name)
print(Eve.alias)
}
}
타입 메서드에는 static 메서드와 class 메서드가 있는데 오버라이딩이 가능한지의 여부에 따라 갈린다.
class Sample {
var mutableProperty: Int = 100
let immutableProperty: Int = 100
static var typeProperty: Int = 100
// 재정의(오버라이딩) 가능
static func instanceMethod() {
print("type method - static")
}
// 재정의(오버라이딩) 불가능
class func typeMethod() {
print("type method - class")
}
}
let으로 class의 instance를 선언해도 내부 mutable property의 값을 바꿀 수 있다.
// 위의 코드에 이어서
let immutableReference: Sample = Sample()
immutableReference.mutableProperty = 200
class는 참조 타입, struct는 값 타입이다.
값 타입은 데이터를 전달할 때 값을 복사하여 전달하고, 참조 타입은 데이터를 전달할 때 값의 메모리 위치를 전달한다.
class SimpleClass {
var count: Int = 0
deinit { print("할당 해제") } }
struct SimpleStruct {
var count: Int = 0 }
var class1 = SimpleClass()
var class2 = class1
var class3 = class1
class3.count = 3
print(class1.count) // class3의 값을 변경했지만 참조타입이므로 class1도 변경 되는 것을 볼 수 있습니다.
var struct1 = SimpleStruct()
var struct2 = struct1
var struct3 = struct1
struct2.count = 2
struct3.count = 3
print(struct1.count) // 0
print(struct2.count) // 2 <- 구조체는 값 타입이므로 항상 새로운 메모리가 할당됩니다.
print(struct3.count) // 3
enum은 상수 역할의 값들을 보기 좋게 나열한 것이다.
// 원시값(raw value)이 없는 열거형
enum Weekday {
case mon
case tue
case wed
case thu, fri, sat, sun
}
// raw value가 있는 열거형
// number type
enum Fruit: Int {
// 가장 먼저 선언된 case부터 0부터 1씩 증가된 값이 들어감
case apple // 0
case grape // 1
case peach // 2
}
enum Fruit2: Int {
// raw value가 없는 case는 바로 이전 case의 값에 +1
case apple = 0 // 0
case grape = 10 // 10
case peach // 11
}
// Character type - 반드시 raw value가 있어야 함
enum School: Character {
case elementary = "초"
case middle = "중"
case high = "고"
case university // error
}
// String type
enum School2: String {
case elementary = "초등"
case middle = "중등"
case high = "고등"
case university // university
}
// rawValue라는 속성을 이용하면 raw value에 접근 가능
print("School2.univeristy.rawValue == \(School2.university.rawValue)")
// raw value를 통해 enum의 case를 return 받을 수 있음
// 이 떄 raw value가 없을 수도 있으므로 반드시 optional 사용
let AppleInstance: Fruit? = Fruit(rawValue: 0) // apple
모든 case가 동일한 raw value를 가져야 하고, 그 값은 지정된 한 가지 값이어야 한다는 단점을 보완한 개념이다.
// 열거형 선언 방법
enum AppleProduct {
case iPad(model: String)
case iPhone(model: String, storage: Int)
case macbook(model: String, storage: Int, size: Int)
}
// 열거형 생성 방법
let product: AppleProduct = .iPhone(model: "8", strage: 64)
// switch문 활용법
switch product {
case. iPad("5s"): break // 연관값이 5s면 매칭
case .iPad: break // 연관값 무시
case .iPhone("X", _): break // 연관값 생략도 가능함
case .iPhone(let model, var storage): break // 연관값에 상수 및 변수 바인딩하기
case let .mackbook(model, storage, size): break // 모두 상수(let)인 경우 let을 맨 앞으로 뺄 수 있음
// if 활용법
if case let .iPhone("8", storage) = product { // product의 첫 번째 연관값이 "8"이면 매칭
print(storage)
}
if case .iPhone(_, 64) = product { // product의 첫 번째 연관값은 상관 없고, 두 번째 연관값이 64면 매칭
print("iPhone 64GB")
}