@objc๋ ์ธ์ ์ฌ์ฉํ๋์?
@objc ์ดํธ๋ฆฌ๋ทฐํธ๋ Swift ์ฝ๋๊ฐ Objective-C ๊ธฐ๋ฐ API์์ ๋์ํ ์ ์๊ฒ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ์ฃผ๋ก UIKit์์ Target-Action ํจํด์ #selector์ ์ฌ์ฉ๋๋ ๋ฉ์๋์ ๋ถํ์ง๋๋ค. ๋ํ, KVC์์ ์ฌ์ฉ๋๊ณ dynamic ํค์๋์ ํจ๊ป KVO๋ฅผ ํ์ฉํ ๋์๋ ์ฌ์ฉ๋ฉ๋๋ค.
๐ Target-Action ํจํด
Target-Action ํจํด์ Apple์ UI ํ๋ ์์ํฌ์์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ์ฉ๋๋ ๋์์ธ ํจํด์ ๋๋ค. ์๋ ์ฝ๋์ ๊ฐ์ด ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋, ๋ฏธ๋ฆฌ ์ง์ ๋ ๊ฐ์ฒด(ํ๊ฒ)์ ๋ฉ์๋(์ก์ )๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ์๋ํฉ๋๋ค. ์ฆ, ์ด๋ฒคํธ์ ๋ฐ์๊ณผ ๊ทธ์ ๋ํ ๋ฐ์์ ๋ถ๋ฆฌํ์ฌ ๊ด๋ฆฌํ ์ ์์ด ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ฐ์ฑ์ด ํฅ์๋ฉ๋๋ค.
let button = UIButton()
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
@objc func buttonTapped() {
print("Action!")
}
๐ Selector
Objective-C๋ Message Dispatch ๋ฐฉ์์ ์ฌ์ฉํ์ฌ ๋ฐํ์์ ํธ์ถ๋ ๋ฉ์๋๋ฅผ ์ฐพ๋๋ฐ, ์ด๋ ํธ์ถํ ๋ฉ์๋๋ฅผ ์๋ณํ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด Selector์ ๋๋ค. UIKit์ Objective-C ๊ธฐ๋ฐ ํ๋ ์์ํฌ์ด๋ค ๋ณด๋ Objective-C ๊ธฐ๋ฐ API๋ค์ด ์กด์ฌํ์ฌ ๋ฉ์๋ ํธ์ถ์ ์ํ Selector๊ฐ Swift์๋ ํ์ํฉ๋๋ค.
Swift 2 ์์ Selector
์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋ฌธ์์ด ๊ธฐ๋ฐ Selector(NSStringFromSelector)๋ฅผ ์ง์ํ์์ต๋๋ค.
Swift 3 ์ดํ Selector
๋ฌธ์์ด ๊ธฐ๋ฐ Selector์ ๋ฌธ์ ์ ์ ๋ฌธ์์ด์ด๊ธฐ ๋๋ฌธ์ ์คํ์ผ ๊ฒฝ์ฐ ์ปดํ์ผ ํ์์ ์๋ฌ๋ฅผ ์ก์์ฃผ์ง ๋ชปํด ๋ฐํ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๋ ์ ์ ๋๋ค. ๋ฐ๋ผ์, #selector() ํค์๋๋ก ์ปดํ์ผ ์์ ์ค๋ฅ๋ฅผ ์ก์ ์ ์๊ฒ ๋ณ๊ฒฝ๋์์ต๋๋ค.
@objc์ dynamic์ ์ฐจ์ด์ ์ด ๋ฌด์์ธ๊ฐ์?
@objc๋ Swift ์ฝ๋๊ฐ Objective-C ๋ฐํ์์์ ์ธ์๋ ์ ์๋๋ก ํด์ฃผ๋ ์ดํธ๋ฆฌ๋ทฐํธ์ ๋๋ค. ์ด ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ๋ฉด Swift ์ฝ๋์ ํด๋์ค, ๋ฉ์๋, ์์ฑ ๋ฑ์ Objective-C ์ฝ๋์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ฃผ๋ก Selector ๊ฐ์ Objective-C ๋ฐํ์ ๊ธฐ๋ฅ(Message Dispatch)์ ์ด์ฉํด์ผ ํ ๋ ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค. ํ์ง๋ง, Objective-C ๋ฐํ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์์ ๋์๋ Swift์ Static Dispatch๋ Table Dispatch ๋ฐฉ์์ด ์ ์ฉ๋ฉ๋๋ค.
dynamic ํค์๋๋ Swift3 ๊น์ง๋ dynamic ํค์๋๋ง ์ ์ด๋ ์๋์ผ๋ก @objc ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ํฌํจํ์ผ๋ Swift4๋ถํฐ @objc dynamic ๊ฐ์ด ๋ช ์์ ์ผ๋ก ์์ฑํด์ผ ํ๋ ๊ฒ์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค. ํด๋น ํค์๋๋ @objc ์ดํธ๋ฆฌ๋ทฐํธ ๊ธฐ๋ฅ์ ๋ํด, Swift ์์๊ฐ Objective-C์ Message Dispatch ๋ฐฉ์์ ์ฌ์ฉํ๋๋ก ๊ฐ์ ํ๋ ์ญํ ์ ํฉ๋๋ค. ์ด์ ๋ฐ๋ผ ๋ฐํ์์ ๋ฉ์๋๋ฅผ ๊ต์ฒดํ๋ Method Swizzling์ด๋ KVO์ ์ฌ์ฉ๋ฉ๋๋ค.
๐ Method Swizzling
Method Swizzling์ด๋ ๋ฐํ์์ ๋ฉ์๋๋ฅผ ๊ต์ฒดํ๋ ๊ฒ์ ๋๋ค. Swift์์๋ ๋ฐํ์์ ๋ฉ์๋๋ฅผ ๊ต์ฒดํ ์ ์์ด ๊ต์ฒด๊ฐ ํ์ํ ์ํฉ์ Objectiv-C ๋ฐํ์ ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ์ํํ ์ ์์ต๋๋ค. ๋ฉ์๋ ๊ต์ฒด๊ฐ ํ์ํ ์ํฉ์ ๋ํ ์ค๋ช ์ ์๋ ๋งํฌ๋ก ๋์ฒดํ๊ฒ ์ต๋๋ค.
KVC๋?
KVC๋ Key-Value Coding์ ์ฝ์๋ก ๊ฐ์ฒด์ ํ๋กํผํฐ์ '. ์ฐ์ฐ์'๋ฅผ ์ฌ์ฉํด ์ง์ ์ ์ผ๋ก ์ ๊ทผํ๋ ๊ฒ์ด ์๋๋ผ ํ๋กํผํฐ๋ช ์ ๋ฌธ์์ด ํค๋ก ํ์ฌ ๊ฐ์ ์ ์ผ๋ก ์ ๊ทผํ๋ ๋งค์ปค๋์ฆ์ ๋๋ค. Objective-C์ ์ ์ฐ์ผ๋ก์ Swift์์ @objc ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํ ๊ฐ๋ฅํฉ๋๋ค.
KVC๋ ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ ์์ ์๋ ์ด๋ค ํ๋กํผํฐ์ ๊ฐ์ ๊ฐ์ ธ์ค๊ณ ์ถ์์ง ์ ํ์ง ์๊ณ ํ๋ก๊ทธ๋จ ์คํ ์ค์ ์ํ๋ ํ๋กํผํฐ์ ๊ฐ์ ๊ฐ์ ธ์ค๊ฒ ํ ์ ์๋ ๋ฑ ์กฐ๊ธ ๋ ๋์ ์ธ ํ๋ก๊ทธ๋๋ฐ์ ๊ฐ๋ฅํ๊ฒ ํด์ค๋๋ค.
Swift 2 ์์ Selector์ ๋ง์ฐฌ๊ฐ์ง๋ก Key๊ฐ ๋ฌธ์์ด์ด๋ฏ๋ก ์คํ๋ ์๋ชป๋ ํค๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ์๋ ๋ฐํ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๋ฌธ์ ์ ์ด ์กด์ฌํฉ๋๋ค. ๋ฐ๋ผ์, ์ปดํ์ผ ํ์์ ์์ ์ฑ ๊ฒ์ฌ๋ฅผ ํ ์ ์๋ Swift์ KVC์ธ KeyPath๊ฐ ๋ฑ์ฅํฉ๋๋ค. ์์ธํ ์ค๋ช ์ ๋งํฌ๋ฅผ ์ฐธ์กฐํด์ฃผ์ธ์.
KVO๋?
KVO๋ Key-Value Observing์ ์ฝ์๋ก Objective-C ๋ฐํ์์ ํ์ฉํ์ฌ ๊ฐ์ฒด์ ํน์ ํ๋กํผํฐ์ ๋ณํ๋ฅผ ๋ค๋ฅธ ๊ฐ์ฒด์๊ฒ ์๋ฆด ์ ์๋ ์ปค๋ฎค๋์ผ์ด์ ํจํด์ ๋๋ค. NSObject๋ฅผ ์์ํ ๊ฐ์ฒด์ @objc dynamic ํค์๋๋ฅผ ๋ถํ ํ๋กํผํฐ์ฌ์ผ KVO๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
์ฌ์ฉ ์์
new, old ์ต์ ์ฌ์ฉ
import Foundation
class Person: NSObject {
@objc dynamic var name: String
init(name: String) {
self.name = name
}
}
var jinyoo = Person(name: "jinyoo")
jinyoo.observe(\.name, options: [.new, .old]) { (object, change) in
print(object.name)
print(change.oldValue, change.newValue)
}
jinyoo.name = "Jinyoo"
// "Jinyoo"
// Optional("jinyoo") Optional("Jinyoo")
initial ์ต์ ์ฌ์ฉ -> ์ด๊ธฐํ ๊ณผ์ ๋ ๋ณํ๋ก ์ธ์ง
var jinyoo = Person(name: "jinyoo")
jinyoo.observe(\.name, options: [.new, .old, .initial]) { (object, change) in
print(object.name)
print(change.oldValue, change.newValue)
}
jinyoo.name = "Jinyoo"
// jinyoo
// nil Optional("jinyoo")
// Jinyoo
// Optional("jinyoo") Optional("Jinyoo")
prior ์ต์ -> ๋ณํ ์ด์ ๊ณผ ๋ณํ ์ดํ ์ํ์ ๋ชจ๋ ํธ์ถ
var jinyoo = Person(name: "jinyoo")
jinyoo.observe(\.name, options: [.new, .old, .prior]) { (object, change) in
print(object.name)
print(change.oldValue, change.newValue)
}
jinyoo.name = "Jinyoo"
//jinyoo
//Optional("jinyoo") nil
//Jinyoo
//Optional("jinyoo") Optional("Jinyoo")
๐ก KVO, ํ๋กํผํฐ ์ต์ ๋ฒ, @Published, NotificationCenter ๋น๊ต (์ต์ ๋น์ ํ์ฉํ ์ปค๋ฎค๋์ผ์ด์ ํจํด๋ค์ ๋น๊ต)
KVO
• Objective-C ๋ฐํ์ ์ฌ์ฉ
• ๊ฐ์ฒด ์ธ๋ถ์์ ํ๋กํผํฐ ์ต์ ๋น ๊ด๋ จ ์ฝ๋ ์์ฑ
• ํ๋กํผํฐ ๋ณ๊ฒฝ์ ๋ํ ์ฒ๋ฆฌ ์ฝ๋๊ฐ ์ฆ์ ์คํ(๋๊ธฐ์ )
• ์ด๊ธฐํ์ ๋ํ ์๋ฆผ์ ๋ฐ์ ์ ์๋ ์ต์ ์กด์ฌ
ํ๋กํผํฐ ์ต์ ๋ฒ
• ๊ฐ์ฒด ๋ด๋ถ์์ ํ๋กํผํฐ ์ต์ ๋น ๊ด๋ จ ์ฝ๋ ์์ฑ
• ํ๋กํผํฐ ๋ณ๊ฒฝ์ ๋ํ ์ฒ๋ฆฌ ์ฝ๋๊ฐ ์ฆ์ ์คํ(๋๊ธฐ์ )
• ์ด๊ธฐํ์ ๋ํ ์๋ฆผ์ ๋ฐ์ง ๋ชปํจ
Combine์ @Published ํ๋กํผํฐ ๋ํผ
• ๊ฐ์ฒด ์ธ๋ถ์์ ํ๋กํผํฐ ์ต์ ๋น ๊ด๋ จ ์ฝ๋ ์์ฑ ๊ฐ๋ฅ
• receive(on:) ์คํผ๋ ์ดํฐ ์ฌ์ฉ ์ ์ฆ์ ์คํ๋์ง ์์(๋น๋๊ธฐ์ )
• ์ด๊ธฐํ์ ๋ํ ์๋ฆผ์ด ๋ํดํธ (dropFirst ์คํผ๋ ์ดํฐ ์ฌ์ฉ ์ ์ด๊ธฐํ์ ๋ํ ์๋ฆผ์ ๋ฌด์ํ ์ ์์)
๐ receive(on:) ์คํผ๋ ์ดํฐ์ ๋น๋๊ธฐ์ ์คํ์ ํ์ธํด๋ณผ ์ ์๋ ์ฝ๋
import Foundation
import Combine
class A {
@Published var name: String
init(name: String) { self.name = name }
}
class B {
var name: String {
didSet {
print("B")
}
}
init(name: String) { self.name = name }
}
class Observing {
var a: A
var b: B
var cancellables = Set<AnyCancellable>()
init(a: A, b: B) {
self.a = a
self.b = b
self.useCombine()
}
private func useCombine() {
self.a.$name
.dropFirst()
.receive(on: RunLoop.main)
.sink { new in
print("A")
}
.store(in: &cancellables)
}
}
var a = A(name: "jinyoo") // @Published ํ๋กํผํฐ ๋ํผ ์ฌ์ฉ ๊ฐ์ฒด
var b = B(name: "jinyoo") // ํ๋กํผํฐ ์ต์ ๋ฒ ์ฌ์ฉ ๊ฐ์ฒด
var obs = Observing(a: a, b: b)
obs.a.name = "Jinyoo"
obs.b.name = "Jinyoo"
// B
// A
๐ receive(on:) ์คํผ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด?
receive(on:) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ, Combine์ @Published ํ๋กํผํฐ์ ๋ณํ ๊ฐ์ง๋ ๋ค๋ฅธ Combine ์คํธ๋ฆผ์ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ receive(on:)์ ์ฌ์ฉํ์ฌ ์ง์ ํ ์ค์ผ์ค๋ฌ์ ์์กดํ์ง ์๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์, ์ด๋ค ์ค์ผ์ค๋ฌ์์ ์คํ๋ ์ง๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐํ(publish)ํ๋ ์ชฝ์ ์ปจํ ์คํธ๋ฅผ ๋ฐ๋ฅด๊ฒ ๋ฉ๋๋ค. ์ฌ๊ธฐ์์ ์ฃผ์ ์ฐจ์ด์ ์ ๊ฐ๋ตํ ์ค๋ช ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ํดํธ ๋์: receive(on:) ์์ด, Combine์ ์คํผ๋ ์ดํฐ๋ค์ upstream์์ ๋ฐ์ ์์ ์ ๊ทธ๋ค์ด ๋ฐ์ ๊ฐ์ ์ค๋ ๋ ๋๋ ์คํ ์ปจํ ์คํธ์์ ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ ์์ค ๊ฐ์ด ๋ณ๊ฒฝ๋์ด ๋ฐํ๋๋ ์ค๋ ๋์ ๋์ผํ ์ค๋ ๋์์ ๊ตฌ๋ ์์ ์ฝ๋๊ฐ ์คํ๋๋ ๊ฒ์ ์๋ฏธํ ์ ์์ต๋๋ค.
์ฆ๊ฐ์ ์คํ: ํน๋ณํ ์ค์ผ์ค๋ฌ๋ฅผ ์ง์ ํ์ง ์์ผ๋ฏ๋ก, ๋ณ๊ฒฝ ์ฌํญ ๊ฐ์ง ํ ํด๋น ๋ณํ๋ฅผ ์ฒ๋ฆฌํ๋ ํด๋ก์ ๋ ๊ฐ๋ฅํ ๋น ๋ฅด๊ฒ ์คํ๋ ์ ์์ต๋๋ค. ํ์ง๋ง, ์ด๋ ์๋์ ์์ ์ค๋ ๋๊ฐ ๊ฐ๋ฒผ์ด ์์ ์ ์ํํ๊ณ ์์ด์ผ ๊ฐ๋ฅํ ์ผ์ ๋๋ค.
๋ฉ์ธ ์ค๋ ๋์์ ๊ด๊ณ: UI๋ฅผ ์ ๋ฐ์ดํธํ๋ ์์ ๊ณผ ๊ฐ์ด ๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋์ด์ผ ํ๋ ์ฝ๋์ผ ๊ฒฝ์ฐ, receive(on:)์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๋ฐํ(publish)๋๋ ์ปจํ ์คํธ๊ฐ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์๋ ๋ UI ์ ๋ฐ์ดํธ ์ฝ๋๊ฐ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์๋ ๊ณณ์์ ์คํ๋ ์ ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ, ๊ธฐ๋ํ์ง ์์ ๋์์ด๋ ์ฑ ํฌ๋์๋ก ์ด์ด์ง ์ ์๋ ์ํ์ด ์์ต๋๋ค.
NotificationCenter
• ๋ค๋ฅธ ๊ฒ๋ค๊ณผ ๋ค๋ฅด๊ฒ ํ๋กํผํฐ ๋ณํ๋ณด๋ค ๊ฐ์ฒด ๊ฐ ์ปค๋ฎค๋์ผ์ด์ ์ ๋ ์ค์ ์ ๋ ์ต์ ๋น์ ๊ฐ๊น์
• ๋ฐ๋ผ์, ๋ค๋ฅธ ์ธ ๊ฐ๋ณด๋ค Delegate ํจํด๊ณผ ๋น๊ตํด์ ๊ณต๋ถํ๋ ๊ฒ์ด ๋ ํจ๊ณผ์
'iOS > Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] ARC (1) | 2024.10.30 |
---|---|
[Swift] Extension (1) | 2024.10.29 |
[Swift] - ์ฝ๋ ์ ํ์ (Collection Types) (0) | 2024.10.27 |
[Swift] - ๊ธฐ๋ณธ ์ฐ์ฐ์ (Basic Operators) (0) | 2024.10.26 |
[Swift] - ๋ฌธ์์ด๊ณผ ๋ฌธ์ (Strings and Characters) (0) | 2024.10.25 |
๋๊ธ