새벽의 기록

[SOPT] 문법 스터디 #9 구조체와 클래스, 타입 캐스팅 본문

SOPT/문법 스터디

[SOPT] 문법 스터디 #9 구조체와 클래스, 타입 캐스팅

OneTen 2024. 11. 19. 13:04
구조체와 클래스
    - 구조체와 클래스의 차이점 (값 타입 vs 참조 타입)과 언제 무엇을 선택해야 하는지에 대해 알아봅시다 !
    - 구조체가 안전한 멀티스레드 접근이 가능한 이유를 알아봅시다.
    - 참조 타입을 활용한 `싱글톤 패턴` 에 대해 알아봅시다.
타입 캐스팅
    - 업캐스팅(as)와 다운캐스팅 (as? as!) 을 예시와 함께 알아봅시다.
    - Any와 AnyObject의 차이와 사용법에 대해 알아봅시다.

 

구조체와 클래스

 

1. 구조체와 클래스의 차이점 (값 타입 vs 참조 타입)과 언제 무엇을 선택해야 하는지에 대해 알아봅시다 ! (1주차 콜렉션 타입 참고)

 
값 타입 (Value Type) 
• 특징: 데이터 복사, 독립적 인스턴스 생성
• 예시: struct, enum, 배열 등 기본 자료형
• 장점: 안정성, 예측 가능성, 스레드 안전성
• 단점: 큰 데이터의 복사 비용, 추가 메모리 사용
→ 값 자체를 복사하므로 다른 곳에서 원본이 변경되는 걱정을 할 필요가 없으나, 복사할 때마다 메모리를 추가로 사용하므로 비용이 큰 편
 
 
참조 타입 (Reference Type) 
• 특징: 데이터 전달, 인스턴스 공유
• 예시: class, closure
• 장점: 성능 최적화, 데이터 공유 용이
• 단점: 예측 불가성, 스레드 안전성 문제, 메모리 누수 가능성
→ 동일한 인스턴스를 여러 곳에서 참조할 수 있어 데이터 공유가 쉽고 참조를 전달하기 때문에 복사 비용이 들지 않는다.
→ 여러 곳에서 같은 인스턴스를 참조할 수 있어 데이터의 변경이 예측하기 어려울 수 있고, 강한 순환 참조(strong reference cycle) 등으로 인해 메모리 누수가 발생할 수 있다.
 
 
값 타입은 독립적인 복사본을 필요로 할 때 유용
→ 데이터의 불변성이 중요할 때
→ 복사 안전성을 확보하고자 할 때
 
 
참조타입은 공유와 참조가 필요할 때 유용
→ 데이터를 공유하거나 공동으로 접근해야 할 때
→ 싱글톤 패턴을 활용할 때
→ 복잡한 객체 구조를 다룰 때
 

 

2. 구조체가 안전한 멀티스레드 접근이 가능한 이유를 알아봅시다.

 
구조체가 안전한 멀티스레드 접근이 가능한 이유는 값 타입이기 때문이다.
 
값 타입은 참조되지 않고 복사되므로, 각 스레드가 구조체의 독립적인 복사본을 가지게 된다.
→ 이를 통해 상호 간섭이 없는 메모리 접근이 이루어지기 때문에 스레드 간 안전하게 사용할 수 있다.
 
 

3. 참조 타입을 활용한 싱글톤 패턴 에 대해 알아봅시다.

싱글톤 패턴은
→ 특정 용도로 객체를 하나만 생성하여 공용으로 사용하고 싶을 때 사용하는 디자인 패턴
→ 클래스가 여러차례 호출되더라도, 인스턴스는 딱 한번만 생성되므로 전역변수처럼 코드의 어느 곳에서 접근하던 간에 같은 메모리에 접근이 가능
→ 프로그램이 끝날 때까지 메모리에 유지
 

class UserInfo {
    var name: String?
    var phone: String?
    var address: String?
}

// A ViewController
let userInfo = UserInfo()
userInfo.name = "김한열"

// B ViewController
let userInfo = UserInfo()
userInfo.phone = "12345678"

// C ViewController
let userInfo = UserInfo()
userInfo.address = "Suwon"

A 에서는 사용자의 이름을, B 에서는 사용자의 전화번호를, C 에서는 사용자의 주소를 추가하기 위해 각각 UserInfo 인스턴스를 생성하여 값을 넣어줬다.
→ 하지만 이럴 경우, UserInfo 인스턴스를 총 3개 생성하기 때문에 인스턴스들은 모두 다른 메모리를 가리키게 된다.
→ 이는 곧, 메모리 낭비로 이어짐
 

class UserInfo {
    static let shared = UserInfo()
    
    var name: String?
    var phone: String?
    var address: String?
    
    private init() {}
}

// A ViewController
let userInfo = UserInfo.shared
userInfo.name = "김한열"

// B ViewController
let userInfo = UserInfo.shared
userInfo.phone = "12345678"

// C ViewController
let userInfo = UserInfo.shared
userInfo.address = "Suwon"

static let 프로퍼티와 private init() 생성자를 추가
→ 인스턴스는 외부에서 재생성될 수 없음.
 
shared 프로퍼티는 static 으로 선언되었기 때문에 프로그램이 실행될 때 메모리에 올라가고 UserInfo 인스턴스가 딱 1번 생성
userInfo.shared 를 통해 생성된 인스턴스에 접근할 수 있다.
→ 인스턴스 내부의 프로퍼티 값을 공유
 
또한 생성자가 private 으로 선언되었기 때문에 외부에서 생성자를 다시 호출하여 인스턴스를 재생성하는 것을 방지할 수 있다.
 
즉, 싱글톤 패턴을 활용했을 때의 장점은
→ 인스턴스가 1번만 생성되기 때문에 메모리 낭비를 방지할 수 있다
→ 전역 인스턴스로 사용할 수 있기 때문에 메모리 및 자원 관리가 쉽다
→ 메모리를 할당하고 초기화하는 시간이 줄어들어 객체 접근 시간이 줄어든다

 

 

타입 캐스팅

1. 업캐스팅(as)와 다운캐스팅 (as? as!) 을 예시와 함께 알아봅시다.

업캐스팅 (as)
→ 하위 클래스 인스턴스를 상위 클래스 타입으로 변환한다.
→ 업캐스팅은 항상 안전하기 때문에 강제(as)로 사용하며, 암시적으로 수행될 수 있어 코드에서 생략되는 경우가 많다.
 
다운캐스팅 (as?와 as!)
→ 상위 클래스 인스턴스를 하위 클래스 타입으로 변환한다.
→ 다운캐스팅은 실패할 가능성이 있기 때문에 주의가 필요하며, 조건부(as?)와 강제(as!) 다운캐스팅 두 가지가 있다.
 

2. Any와 AnyObject의 차이와 사용법에 대해 알아봅시다.

Any
→ 모든 타입을 포괄하는 타입.
→ 클래스, 구조체, 열거형, 함수 등 모든 데이터 타입을 담을 수 있다.
→ 일반적으로는 어떤 타입이 올지 모를 때 사용하지만, 강한 타입 안전성을 잃게 되므로 최소한으로 사용하는 것이 좋다.

var someValue: Any = "Hello, Swift!"
someValue = 1234 // Int도 가능
someValue = true // Bool도 가능

 
 
AnyObject
→ 클래스 타입의 인스턴스만 저장할 수 있는 타입.
→ 프로토콜과 결합하여 클래스를 따르는 특정 프로토콜의 객체만 가리키도록 제한할 수 있다.

class SampleClass {}
var someClassInstance: AnyObject = SampleClass()

// 구조체는 AnyObject로 선언 불가
struct SampleStruct {}
// var someStructInstance: AnyObject = SampleStruct() // 오류 발생
Comments