Java 에서처럼 변수의 종류들이 여러가지 있어
우선 잘 모르겠으면 이 포스트를 참고하도록 해봐
https://juns-jc.tistory.com/160
Java_ 지역변수 , 인스턴스 변수 차이
자꾸 안드로이드 스튜디오에서 heap size 에 대해서 초과되었다고 경고 메시지를 주고 있다...뭐지 ?? 근데 언뜻 찾아보니 인스턴스 변수들의 사용량이 매우 커서 힙사이즈가 초과되었다는 얘기를
juns-jc.tistory.com
우선 Swift 에서의 변수부분을 설명할꺼야
✅ Swift 변수의 종류
변수 종류선언 위치메모리 영역초기화 필요 여부특징
저장 프로퍼티 (Stored Property) | 클래스/구조체 내부 | Heap (클래스) / Stack (구조체) | 필요 (옵셔널 제외) | 인스턴스마다 개별 값 유지 |
타입 프로퍼티 (Type Property, static/ class) | static 또는 class 사용 | Data 영역 | 자동 초기화 | 모든 인스턴스가 공유 |
지역 변수 (Local Variable) | 함수/메서드 내부 | Stack | 필요 | 선언된 블록 내에서만 사용 가능 |
매개변수 (Parameter) | 함수/메서드의 인자 | Stack | 자동 초기화 | 함수 호출 시 전달받은 값 저장 |
계산 프로퍼티 (Computed Property) | 클래스/구조체 내부 | Heap (클래스) / Stack (구조체) | X | 값 저장 없이 연산을 통해 결과 반환 |
1️⃣ 저장 프로퍼티 (Stored Property)
클래스 또는 구조체 내부에서 값을 저장하는 변수
Java의 인스턴스 변수와 비슷하지만, 구조체에서는 값 타입이므로 Stack에 저장됨
📌 특징
- let(상수) 또는 var(변수)로 선언 가능
- 클래스에서는 Heap 메모리에 저장 (참조 타입)
- 구조체에서는 Stack 메모리에 저장 (값 타입)
- 옵셔널이 아니라면 반드시 초기화 필요! (init()에서 초기화 가능)
📌 예제
class Person {
var name: String // 저장 프로퍼티 (초기화 필요)
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let p1 = Person(name: "Alice", age: 25)
print(p1.name) // "Alice"
- name과 age는 저장 프로퍼티로, init()에서 초기화해줘야 함.
- Person은 클래스이므로 Heap 메모리에 저장됨.
2️⃣ 타입 프로퍼티 (Type Property)
클래스나 구조체 자체에 속하며, 모든 인스턴스가 공유하는 변수
Java의 static 변수와 동일한 개념
📌 특징
- static 키워드를 사용해 선언
- class 키워드를 사용하면 서브클래스에서 재정의 가능
- 프로그램 실행 시 초기화되고, 인스턴스 없이 접근 가능
📌 예제
class Counter {
static var count = 0 // 타입 프로퍼티 (모든 객체가 공유)
init() {
Counter.count += 1
}
}
let c1 = Counter()
let c2 = Counter()
print(Counter.count) // 2 (모든 객체가 공유)
- count는 static 변수이므로 모든 인스턴스가 같은 값을 공유함.
3️⃣ 지역 변수 (Local Variable)
함수나 메서드 내부에서 선언된 변수로, 해당 블록 내에서만 사용 가능
📌 특징
- Stack 메모리에 저장됨
- 자동 초기화되지 않음 → 직접 초기화 필요
- 함수 실행이 끝나면 사라짐
📌 예제
func printNumber() {
let num = 10 // 지역 변수 (자동 초기화 X → 직접 초기화 필요)
print("Number: \(num)")
}
printNumber()
// print(num) // 오류! (num은 지역 변수라 접근 불가)
- num은 함수 내부에서만 사용 가능.
- 함수 실행이 끝나면 num은 메모리에서 제거됨.
4️⃣ 매개변수 (Parameter)
함수 호출 시 전달받아 사용되는 변수
📌 특징
- Stack 메모리에 저장됨
- 함수 호출 시 값이 전달되고, 함수 종료 후 사라짐
- 기본적으로 상수(let)로 취급되므로 값 변경 불가 (inout으로 변경 가능)
📌 예제
func greet(name: String) { // 매개변수 name
print("Hello, \(name)")
}
greet(name: "Alice") // "Hello, Alice"
- name은 함수가 호출될 때 값이 전달됨.
- 함수 실행 중에만 존재하고 끝나면 사라짐.
5️⃣ 계산 프로퍼티 (Computed Property)
저장된 값 없이, 특정 연산을 통해 값을 반환하는 변수
📌 특징
- get과 set을 사용하여 값 계산 가능
- Heap(클래스) / Stack(구조체)에 저장됨
- 내부적으로 값을 저장하지 않고, 매번 연산하여 값을 반환
📌 예제
struct Rectangle {
var width: Double
var height: Double
var area: Double { // 계산 프로퍼티
return width * height
}
}
let rect = Rectangle(width: 5, height: 10)
print(rect.area) // 50.0
- area는 실제 값을 저장하지 않고, width * height의 결과를 반환.
✅ 변수 종류별 차이점 정리
변수 종류선언 위치저장 공간초기화 필요 여부특징
저장 프로퍼티 | 클래스/구조체 내부 | Heap (클래스) / Stack (구조체) | 필요 (옵셔널 제외) | 인스턴스마다 개별 값 유지 |
타입 프로퍼티 | static 또는 class 사용 | Data 영역 | 자동 초기화 | 모든 인스턴스가 공유 |
지역 변수 | 함수/메서드 내부 | Stack | 필요 | 선언된 블록 내에서만 사용 가능 |
매개변수 | 함수/메서드의 인자 | Stack | 자동 초기화 | 함수 호출 시 전달받은 값 저장 |
계산 프로퍼티 | 클래스/구조체 내부 | Heap (클래스) / Stack (구조체) | X | 값 저장 없이 연산을 통해 결과 반환 |
✅ 마무리
- 저장 프로퍼티 → 일반적인 멤버 변수 (var name: String)
- 타입 프로퍼티 → 모든 객체가 공유하는 변수 (static var count)
- 지역 변수 → 함수 내부에서만 사용 (let num = 10)
- 매개변수 → 함수 호출 시 전달된 값 (func greet(name: String))
- 계산 프로퍼티 → 저장 없이 연산을 통해 값 반환 (var area: Double { width * height })
Swift에서 변수를 사용할 때 값 타입(구조체)과 참조 타입(클래스)의 차이도 고려해야 해.
별책부록
✅ 1. Property Observers (프로퍼티 옵저버)
willSet과 didSet을 이용하여 변수 값이 변경될 때 실행할 코드 지정
📌 특징
- willSet → 값이 변경되기 직전에 실행
- didSet → 값이 변경된 직후 실행
- 초기화할 때는 호출되지 않고, 기존 값에서 변경될 때만 호출됨
- 주로 UI 업데이트나 특정 이벤트 감지할 때 유용
📌 예제
class User {
var name: String = "Unknown" {
willSet(newName) { // 값이 변경되기 직전 실행
print("이름이 \(name)에서 \(newName)로 변경될 예정!")
}
didSet { // 값이 변경된 직후 실행
print("이름이 \(oldValue)에서 \(name)로 변경됨!")
}
}
}
let user = User()
user.name = "Alice"
/*
출력:
이름이 Unknown에서 Alice로 변경될 예정!
이름이 Unknown에서 Alice로 변경됨!
*/
✅ willSet에서 newValue, didSet에서 oldValue를 사용할 수 있음!
🔹 언제 유용할까?
- UI 업데이트 필요할 때
var score: Int = 0 {
didSet {
scoreLabel.text = "점수: \(score)" // UI 자동 업데이트
}
}
- 값 변경을 감지하고, 로직을 실행할 때
var batteryLevel: Int = 100 {
didSet {
if batteryLevel < 20 {
print("배터리가 부족합니다!")
}
}
}
✅ 2. Computed Property (계산 프로퍼티)
실제 값을 저장하지 않고, 연산을 통해 값을 반환하는 프로퍼티
📌 특징
- 저장되지 않고 항상 연산을 통해 값을 반환
- get만 사용하면 읽기 전용, get-set을 사용하면 읽기/쓰기 가능
📌 예제
struct Rectangle {
var width: Double
var height: Double
var area: Double { // 계산 프로퍼티 (읽기 전용)
return width * height
}
var halfWidth: Double { // 읽기/쓰기 가능
get {
return width / 2
}
set(newHalfWidth) {
width = newHalfWidth * 2
}
}
}
var rect = Rectangle(width: 10, height: 5)
print(rect.area) // 50.0
rect.halfWidth = 8
print(rect.width) // 16.0
✅ get만 사용하면 읽기 전용, set을 추가하면 값 변경 가능!
🔹 언제 유용할까?
- 실시간으로 값이 계산되어야 할 때
var temperatureInFahrenheit: Double {
return temperatureInCelsius * 9/5 + 32
}
- 특정 로직을 실행하고 값을 반환할 때
var isAdult: Bool {
return age >= 18
}
✅ 3. Lazy Property (지연 저장 프로퍼티)
값이 실제로 사용될 때까지 초기화되지 않는 프로퍼티
📌 특징
- lazy var 키워드를 사용
- 초기값을 나중에 설정할 수 있음
- 클로저({})를 이용해 초기화 가능
- 구조체에서는 mutating 없이는 변경 불가능
📌 예제
class DataLoader {
lazy var data: String = { // 실제로 접근할 때 실행됨
print("데이터 로드 중...")
return "로딩된 데이터"
}()
}
let loader = DataLoader()
print("초기화 완료") // 여기서는 data 값이 아직 로딩되지 않음
print(loader.data) // 이 순간에 data가 로드됨!
/*
출력:
초기화 완료
데이터 로드 중...
로딩된 데이터
*/
🔹 언제 유용할까?
- 메모리를 절약하고 싶을 때 (큰 데이터를 미리 로드하지 않음)
- 네트워크 요청이 필요할 때
- 초기화 비용이 큰 객체를 지연 생성할 때
✅ 4. Property Wrapper (속성 래퍼)
변수의 동작을 커스텀할 수 있는 기능
📌 특징
- @propertyWrapper 키워드를 사용
- 프로퍼티의 저장 방식과 동작을 커스텀 가능
📌 예제
@propertyWrapper
struct UpperCase {
private var text: String = ""
var wrappedValue: String {
get { text }
set { text = newValue.uppercased() } // 값을 대문자로 변환
}
}
struct User {
@UpperCase var name: String
}
var user = User()
user.name = "alice"
print(user.name) // "ALICE"
✅ wrappedValue를 통해 값을 자동으로 변환하거나, 특정 로직을 추가할 수 있음!
🔹 언제 유용할까?
- 입력 값을 자동 변환하고 싶을 때 (@UpperCase, @Trimmed)
- UserDefaults, Keychain 같은 저장 기능을 래핑할 때
✅ 5. Key-Value Observing (KVO)
객체의 속성 변화를 감지하는 기능 (@objc dynamic)
📌 특징
- Objective-C의 KVO(Key-Value Observing) 기능 활용
- @objc dynamic을 사용하여 감지 가능
- addObserver(_:forKeyPath:options:context:)를 사용
📌 예제
import Foundation
class Person: NSObject {
@objc dynamic var name: String = "Unknown"
}
class Observer: NSObject {
var person: Person
init(person: Person) {
self.person = person
super.init()
person.addObserver(self, forKeyPath: "name", options: [.new, .old], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "name" {
print("이름 변경됨: \(change?[.oldKey] as? String ?? "") → \(change?[.newKey] as? String ?? "")")
}
}
}
let person = Person()
let observer = Observer(person: person)
person.name = "Alice" // "이름 변경됨: Unknown → Alice"
✅ KVO는 SwiftUI와 Combine 사용 시 대체되는 경우가 많음!
✅ 마무리
기능설명언제 유용할까?
willSet / didSet | 값 변경 전/후 실행 | UI 업데이트, 이벤트 감지 |
계산 프로퍼티 | 저장 없이 연산하여 값 반환 | 실시간 값 계산 |
lazy var | 필요할 때만 초기화 | 메모리 절약, 네트워크 요청 |
Property Wrapper | 변수를 커스텀 | 자동 변환, UserDefaults |
KVO (@objc dynamic) | 속성 변경 감지 | SwiftUI, Combine |
각 기능을 적절히 사용하면 더 깔끔하고 성능 좋은 코드를 만들 수 있어! 🚀
'Swift' 카테고리의 다른 글
Swift_ intrinsicContentSize / Frame / bounds (0) | 2025.02.08 |
---|---|
Swift_ CoreData Model , extension , Struct (0) | 2024.08.02 |
Swift_ CoreData 적용기... (0) | 2024.07.24 |