📕 class 인스턴스 메서드
class로 선언된 형태의 코드는 메모리의 데이터 영역에 저장되어 있으며
class의 메서드는 메모리의 코드 영역에 명령어의 집합 형태로 저장되어 있고 데이터 영역에 저장된 class의 메서드는 코드 영역에 저장된 메모리의 주소를 갖고 있음.
class Introduce {
var name: String
var age: Int
var country: String
init(name: String, age: Int, country: String) {
self.name = name
self.age = age
self.country = country
}
func speak() {
print("안녕하세요, 저의 이름은 \(self.name)이고\n나이는 \(self.age)세이며\n사는 나라는 \(self.country)입니다.")
}
}
var minjae = Introduce(name: "김민재", age: 29, country: "대한민국")
minjae.speak()
메모리의 영역
- 코드 - speak()이라는 메서드는 컴파일되어 컴퓨터가 읽을 수 있는 형태의 명령어로 저장되어 있음
- 데이터 - Introduce라는 class가 저장되어 있으며, 각각의 메서드는 코드에 저장되어 있는 메모리 주소를 가리킴
- 힙 - minjae라는 변수처럼 인스턴스 데이터를 저장하고 있음
- 스택 - speak()이라는 메서드를 호출하게 되면
- 해당 메서드 minjae.speak()이 스택의 스택프레임으로 쌓이고
- 데이터 영역의 메서드로 가서 메모리 주소를 확인하고 코드 영역으로 이동
- 명령어를 순차적으로 실행하면서
- 힙 영역으로 이동해 인스턴스의 속성을 확인하면서 계속 명령어 실행
- 명령어의 끝이 실행이 되면 해당 메서드의 스택프레임을 스택 메모리에서 해제
📘 struct 인스턴스 메서드
- 값 타입에서 기본적으로 인스턴스 메서드 내에서 속성 수정이 불가능
- 수정하려면, mutating(변형되는) 키워드를 붙이면 속성 수정 가능해짐 (클래스와 구조체 차이)
struct Human {
var name: String
init(name: String) {
self.name = name
}
mutating func changeName(_ name: String) {
self.name = name
}
}
📗 계산 속성
class Inbody {
var height: Dobule = 160.0
var weight: Double = 60.0
var bmi: Double {
get {
return weight / (height * height) * 10000
}
set {
weight = newValue * height * height / 10000
}
}
}
계산 속성 - 1) getter(get 블록) / 2) setter(set블록)
- 속성의 형태를 가진 실질적 메서드
- 메서드이기 때문에 인스턴스에 메모리 공간이 할당되어 있지 않음
- var로만 선언 가능, 자료형까지 선언해야함(타입 추론 방식이 안됨)
- get블록만 선언하면 읽기전용(read-only) 계산 속성이 됨 (필수 구현)
- set블록은 선택적으로 구현할 수 있음(set만 구현하는 것은 불가능)
- set블록은 기본 파라미터 newValue가 제공됨(직접 파라미터 이름 설정도 가능)
메모리 구조
- getter, setter를 한 번에 작성해줬지만 메서드로 작동되기 때문에 코드 영역에 따로 저장됨
- 나머지는 인스턴스의 메서드가 메모리에서 작동하는 방식과 동일
📙 타입 속성
class의 인스턴스 데이터 자체는 힙의 영역에 저장이 되지만 class 자체는 데이터 영역에 저장된다.
즉, 타입 속성이라고 한다면 해당 타입에 종속되는 속성이기 때문에 class 데이터가 저장된 데이터 영역에 같이 저장된다.
그렇기 떄문에 인스턴스에서 접근은 불가능하고 타입 자체에서만 접근이 가능하다
class Circle {
static let pi: Double = 3.14
}
let circle = Circle()
circle.pi // error: static member 'pi' cannot be used on instance of type 'Circle'
Circle.pi // 3.14
- 실제 사용 예시
Int.max
Int.min
- 저장 타입속성은 지연 속성(lazy)의 성격을 가짐
- 저장 타입속성에 처음 접근하는 순간에 초기화가 이루어짐
- lazy var 즉, 지연 속성은 여러 스레드에서 동시에 접근하게되면 메모리의 여러 공간에 생성이 될 수 있는데
- 저장 타입속성은 여러 스레드에서 동시에 접근해도 한 번만 생성 즉, 초기화가 됨. Thread-Safe
- 계산 타입속성은 사실상 getter / setter를 가진 메서드 이기때문에 메모리 구조는 class의 메서드 메모리 구조와 동일하게 작동
📘 속성 감시자(Property Observer)
- 저장 속성을 감시하는 역할
class Search {
var keyword: String = "" {
// 저장속성 + 저장 속성이 변하는 시점을 관찰하는 메서드
willSet(newKeyword) {
print("검색 키워드: \(keword) -> \(newKeyword)로 변경예정")
}
}
}
Note
'속성 감시자'의 성격은 메서드, 저장속성 감시
📚 '속성 감시자' 의 종류는 2가지
- 속성의 값이 변경되기 직전 -> willSet
- 속상의 값이 변경된 직후 -> didset
- 보통은 didset만 사용
class Search {
var keyword: String = "" {
// 저장속성 + 저장 속성이 변하는 시점을 관찰하는 메서드
willSet(newKeyword) { // 변경될 값이 파라미터로 전달
print("검색 키워드: \(keword) -> \(newKeyword)로 변경예정")
}
didset(oldKeyword) { // 변경되기 전 과거값이 파라미터로 전달
print("검색 키워드: \(oldKeword) -> \(keyword)로 변경완료")
}
}
}
- swift 자체에서 파라미터를 받지 않아도 newValue / oldValue로 사용이 가능
class Search {
var keyword: String = "" {
// 저장속성 + 저장 속성이 변하는 시점을 관찰하는 메서드
willSet { // 변경될 값이 파라미터로 전달
print("검색 키워드: \(keword) -> \(newValue)로 변경예정")
}
didset { // 변경되기 전 과거값이 파라미터로 전달
print("검색 키워드: \(oldValue) -> \(keyword)로 변경완료")
}
}
}
📚 '속성 감시자' 사용시 주의할 점
- 속성 감시자를 사용할 수 있는 경우
- 저장 속성 (원래, 상속한 경우 둘 다 가능)
- 계산 속성 (상속해서 재정의한 경우에만 가능)
- 계산 속성의 경우, setter 메서드에서 값 변경에 대한 관찰이 가능하기 떄문
- let(상수)에는 사용이 당연히 불가능 (값이 변하지 않기 때문)
- 지연저장 속성에도 불가능
📙 타입 메서드
타입 메서드는 타입 속성과 마찬가지로 func 키워드 앞에 static 키워드를 붙여주면 된다.
타입 속성과 마찬가지로 타입자체에 종속되는 메서드로 인스턴스에서는 접근이 불가능
class Circle {
static let pi: Double = 3.14
static func printPi() {
print(pi)
}
}
let circle = Circle()
circle.printPi() // error: static member 'printPi' cannot be used on instance of type 'Circle'
Circle.printPi() // 3.14
Note
타입이라는 단어와 함께 사용되는 친구들끼리는 서로 Type.속성, Type.메서드() 이런 식으로 하지 않고
Type의 생략이 가능하다.
class Circle {
static let pi: Double = 3.14
static func printPi() {
print(pi)
}
func printPi() {
print(pi)
} // error: static member 'pi' cannot be used on instance of type 'Circle'
}
struct의 경우 static 키워드만 사용하면 되지만
class의 경우 타입메서드 정의 방법이 2가지가 있다.
- static: 상속 시 재정의 불가능
- class: 상속 시 재정의 가능
class Circle { static let pi: Double = 3.14 static func printPi() { // 재정의 불가능 print(pi) } class func printPi2() { // 재정의 가능 print(pi) } }
메모리에서의 작동방식은 인스턴스 메서드와 타입메서드 둘 다 동일하게 작동
'iOS > Swift' 카테고리의 다른 글
Swift - 생성자(Initializer) (0) | 2024.04.28 |
---|---|
Swift - Any와 AnyObject (1) | 2024.04.18 |
Swift Optional(2) (1) | 2024.04.06 |
Swift 문자열 출력 방식에 대한 테스트 (1) | 2024.04.06 |
Swift @discardableResult (1) | 2024.04.06 |