개체가 변경되기 전에 내보내는 게시자가 있는 개체 형식이다.?
@Published와 ObservableObject는 스위프트의 Combine 프레임워크에서 사용되는 속성 래퍼와 프로토콜이다.
✅ 주로 SwiftUI와 함꼐 사용되어 상태 관리를 쉽게 하고, 데이터 변경 시 UI가 자동으로 업데이트 되도록 한다.
🟥 @Published
ObservableObject에 속하는 프로퍼티가 변경될 때마다 자동으로 변경 사항을 알리는 역할을 한다.
이를 통해, 프로퍼티를 구독(Subscribe)하고 있는 UI 컴포넌트들이 자동으로 업데이트 된다.
🟥 ObservableObject
Combine 프레임워크의 프로토콜로, 객체가 변경될 수 있음을 나타낸다.
이 프로토콜을 준수하는 클래스는 프로퍼티가 변경될 때 Combine 프레임워크를 통해 변경 사항을 알릴 수 있다.
🟥 사용 예제
1️⃣ ViewModel
- ObservableObject 프로토콜을 준수하는 CounterViewModel 클래스 정의
- @Published var count는 count 프로퍼티가 변경될 때마다 ViewModel을 구독하고 있는 모든 뷰 들에게 알림을 보낸다.
import Combine
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
}
2️⃣ ViewController
- setupBindings() 메서드를 통해 viewModel.$count를 구독하여 count 값이 변경될 때마다 countLabel의 텍스트를 업데이트한다.
import UIKit
import Combine
class CounterViewController: UIViewController {
private var viewModel = CounterViewModel()
private var cancellables = Set<AnyCancellable>()
private let countLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let incrementButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Increment", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupViews()
setupBindings()
}
private func setupViews() {
view.addSubview(countLabel)
view.addSubview(incrementButton)
NSLayoutConstraint.activate([
countLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
countLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
incrementButton.topAnchor.constraint(equalTo: countLabel.bottomAnchor, constant: 20),
incrementButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
incrementButton.addTarget(self, action: #selector(incrementButtonTapped), for: .touchUpInside)
}
private func setupBindings() {
viewModel.$count
.receive(on: RunLoop.main)
.sink { [weak self] count in
self?.countLabel.text = "Count: \(count)"
}
.store(in: &cancellables)
}
@objc private func incrementButtonTapped() {
viewModel.count += 1
}
}
✅ RunLoop.main 이란?
메인 스레드에서 실행되는 런 루프를 뜻한다.
런 루프는 실행 루프라고도 하며, 이벤트를 처리하고 애플리케이션의 지속적인 실행을 유지하는 역할을 한다.
'RunLoop.main'을 사용하면 특정 작업을 메인 스레드에서 실행하도록 보장할 수 있다.
'viewModel.$count.receive(on: RunLoop.main)' 은 Combine 프레임워크에서 데이터 스트림의 변경사항을 수신할 때, 해당 작업을 메인 스레드에서 수행하도록 보장하는 것이다. 이는 UI 업데이트가 메인 스레드에서 이루어져야 하는 iOS 애플리케이션에서 중요하다.
viewModel.$count
.receive(on: RunLoop.main)
.sink { [weak self] count in
self?.countLabel.text = "Count: \(count)"
}
.store(in: &cancellables)
이 코드는 다음과 같은 의미를 갖는다.
- viewModel.$count:
- @Published 속성인 count의 변경 사항을 퍼블리셔로 받는다.
- receive(on: RunLoop.main):
- 데이터 변경 사항을 수신할 때, 해당 작업이 메인 스레드에서 실행한다.
- 이는 UI 업데이트가 메인 스레드에서만 안전하게 이루어질 수 있기 때문에 중요하다
- sink { [weak self] count in ... }:
- 퍼블리셔에서 방출된 값을 처리하는 클로저로, 여기서는 countLabel의 텍스트를 업데이트한다.
- .store(in: &cancellables):
- sink 구독을 cancellables에 저장하여 메모리 관리를 용이하게 한다.
✅ RunLoop.main 외에도 Combine에서 데이터 스트림을 처리할 때 사용할 수 있는 다양한 방법이 있다.
✅ 대표적인 예로는 DispatchQueue.main과 OperationQueue.main 등이 있고, 이들은 모두 작업을 메인 스레드에서 실행하도록 한다.
✅ DispatchQueue.main
viewModel.$count
.receive(on: DispatchQueue.main)
.sink { [weak self] count in
self?.countLabel.text = "Count: \(count)"
}
.store(in: &cancellables)
✅ OperationQueue.main
viewModel.$count
.receive(on: OperationQueue.main)
.sink { [weak self] count in
self?.countLabel.text = "Count: \(count)"
}
.store(in: &cancellables)
- RunLoop.main:
- 런 루프 기반으로, 주로 이벤트 처리와 관련된 작업을 메인 스레드에서 수행하는 데 사용된다.
- 특정 런 루프 모드에서 작업을 수행할 수 있다.
- DispatchQueue.main:
- GCD 기반으로, 메인 스레드에서 작업을 처리하는 디스패치 큐이다.
- 작업이 비동기적으로 수행되며, 주로 동시성 처리와 관련된 작업에 사용된다.
- OperationQueue.main:
- 운영 큐 기반으로, 메인 스레드에서 작업을 처리하는 운영 큐다.
- Operation 객체를 사용하여 작업을 관리하고 실행한다.
⭐ 주로 DispatchQueue.main이 가장 많이 사용되고, 비동기 작업과 메인 스레드 간의 간단한 전환을 위해 활용된다.
3️⃣ SceneDelegate
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = CounterViewController()
window?.makeKeyAndVisible()
}
}
'iOS > Swift' 카테고리의 다른 글
델리게이트 패턴이란? (0) | 2024.07.08 |
---|---|
영어 단어 중에 첫 번째 글자를 대문자로 하고 나머지는 소문자로 처리하는 함수 (0) | 2024.07.04 |
Combine (MVVM 패턴) (0) | 2024.05.30 |
MVVM 패턴 - Binding 개념 (0) | 2024.05.30 |
MVVM 패턴 (Model - View - ViewModel) (0) | 2024.05.30 |