μλ νμΈμ. μ€λμ Swiftμ 'νλ‘ν μ½(Protocols)'μ λν΄ μμλ³΄κ² μ΅λλ€.
π νλ‘ν μ½μ΄λ?
νλ‘ν μ½μ νΉμ μμ μ΄λ κΈ°λ₯μ νμν λ©μλ, νλ‘νΌν°, κΈ°ν μꡬμ¬νμ μ²μ¬μ§μ μ μν©λλ€. ν΄λμ€, ꡬ쑰체, μ΄κ±°νμ μ΄λ¬ν νλ‘ν μ½μ 'μ±ν(adopt)'νμ¬ μꡬμ¬νμ 'μ€μ(conform)'ν μ μμ΅λλ€.
νλ‘ν μ½μ 무μμ ν΄μΌ νλμ§λ§ μ μνκ³ , μ΄λ»κ² ꡬνν΄μΌ νλμ§λ κ° νμ μ λ§‘κΉλλ€. μ΄λ₯Ό ν΅ν΄ μλ‘ λ€λ₯Έ νμ λ€μ΄ 곡ν΅λ κΈ°λ₯μ μ 곡νλ©΄μλ κ°μμ, μ΄λ₯Ό μ μ°νκ² κ΅¬νν μ μλ λ€νμ±μ μ 곡ν©λλ€.
π νλ‘ν μ½ κ΅¬λ¬Έ (Protocol Syntax)
νλ‘ν μ½μ λ€μκ³Ό κ°μ΄ μ μν©λλ€:
protocol SomeProtocol {
// νλ‘ν μ½ μꡬμ¬ν μ μ
}
νμ μ΄ νλ‘ν μ½μ μ±νν λλ νμ μ΄λ¦ λ€μ μ½λ‘ (:)μ λΆμ΄κ³ νλ‘ν μ½ μ΄λ¦μ λͺ μν©λλ€:
struct SomeStructure: SomeProtocol {
// ꡬ쑰체 μ μ
}
// μ¬λ¬ νλ‘ν μ½ μ±ν μ μ½€λ§λ‘ ꡬλΆ
struct AnotherStructure: FirstProtocol, AnotherProtocol {
// ꡬ쑰체 μ μ
}
// ν΄λμ€κ° μμ ν΄λμ€λ₯Ό κ°μ§ κ²½μ°, μμ ν΄λμ€λ₯Ό λ¨Όμ λͺ
μ
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// ν΄λμ€ μ μ
}
μ°Έκ³ : νλ‘ν μ½ μ΄λ¦μ λ€λ₯Έ Swift νμ μ²λΌ λλ¬Έμλ‘ μμν©λλ€(μ: Equatable, Hashable).
π νλ‘νΌν° μꡬμ¬ν (Property Requirements)
νλ‘ν μ½μ νΉμ μ΄λ¦κ³Ό νμ μ νλ‘νΌν°λ₯Ό μꡬν μ μμΌλ©°, ν΄λΉ νλ‘νΌν°κ° μ μ₯ νλ‘νΌν°μΈμ§ κ³μ° νλ‘νΌν°μΈμ§λ μ§μ νμ§ μμ΅λλ€. λ¨, νλ‘νΌν°κ° μ½κΈ° μ μ©μΈμ§(gettable) λλ μ½κΈ°-μ°κΈ° κ°λ₯νμ§(gettable & settable)λ λͺ μν΄μΌ ν©λλ€.
protocol SomeProtocol {
var mustBeSettable: Int { get set } // μ½κΈ°-μ°κΈ° κ°λ₯
var doesNotNeedToBeSettable: Int { get } // μ½κΈ° μ μ©
static var someTypeProperty: Int { get set } // νμ
νλ‘νΌν°
}
μμ : νλ‘νΌν° μꡬμ¬ν ꡬν
protocol FullyNamed {
var fullName: String { get }
}
// κ΅¬μ‘°μ²΄λ‘ κ΅¬ν
struct Person: FullyNamed {
var fullName: String
}
// ν΄λμ€λ‘ ꡬν (κ³μ° νλ‘νΌν° μ¬μ©)
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
print(ncc1701.fullName) // "USS Enterprise" μΆλ ₯
μ€μ: μ½κΈ°-μ°κΈ° μꡬμ¬ν(get set)μ΄ μλ νλ‘νΌν°λ μμ μ μ₯ νλ‘νΌν°λ μ½κΈ° μ μ© κ³μ° νλ‘νΌν°λ‘ ꡬνν μ μμ΅λλ€.
π λ©μλ μꡬμ¬ν (Method Requirements)
νλ‘ν μ½μ νΉμ μΈμ€ν΄μ€ λ©μλμ νμ λ©μλ ꡬνμ μꡬν μ μμ΅λλ€:
protocol RandomNumberGenerator {
func random() -> Double
}
λ³κ²½ λ©μλ μꡬμ¬ν (Mutating Method Requirements)
ꡬ쑰체λ μ΄κ±°νμ λ©μλκ° μΈμ€ν΄μ€ μ체λ₯Ό μμ ν΄μΌ νλ κ²½μ°, mutating ν€μλλ₯Ό μ¬μ©ν©λλ€:
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
μ°Έκ³ : ν΄λμ€ λ©μλλ νμ μΈμ€ν΄μ€λ₯Ό μμ ν μ μμΌλ―λ‘ ν΄λμ€ κ΅¬νμμλ mutating ν€μλλ₯Ό λͺ μνμ§ μμ΅λλ€.
π μ΄κΈ°ν ꡬ문 μꡬμ¬ν (Initializer Requirements)
νλ‘ν μ½μ νΉμ μ΄κΈ°ν ꡬ문μ μꡬν μ μμ΅λλ€:
protocol SomeProtocol {
init(someParameter: Int)
}
ν΄λμ€μ μ΄κΈ°ν ꡬ문 ꡬν
ν΄λμ€κ° νλ‘ν μ½μ μ΄κΈ°ν ꡬ문 μꡬμ¬νμ ꡬνν λλ required ν€μλλ₯Ό μ¬μ©ν΄μΌ ν©λλ€:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// μ΄κΈ°ν ꡬν
}
}
μ°Έκ³ : final ν΄λμ€λ μμλ μ μμΌλ―λ‘ required ν€μλκ° νμ μμ΅λλ€.
μμ ν΄λμ€μ μ§μ μ΄κΈ°ν ꡬ문μ μ¬μ μνκ³ λμμ νλ‘ν μ½ μꡬμ¬νμ μΆ©μ‘±νλ κ²½μ°, requiredμ override ν€μλλ₯Ό λͺ¨λ μ¬μ©ν©λλ€:
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
// μ΄κΈ°ν ꡬν
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required"λ SomeProtocol μ€μλ₯Ό μν΄, "override"λ SomeSuperClassμμ μμ
required override init() {
// μ΄κΈ°ν ꡬν
}
}
π νμ μΌλ‘μμ νλ‘ν μ½ (Protocols as Types)
νλ‘ν μ½μ λ€μκ³Ό κ°μ μν©μμ νμ μΌλ‘ μ¬μ©λ μ μμ΅λλ€:
- ν¨μ, λ©μλ, μ΄κΈ°ν ꡬ문μ λ§€κ°λ³μ νμ λλ λ°ν νμ
- μμ, λ³μ, νλ‘νΌν°μ νμ
- λ°°μ΄, λμ λ리 λ± μ»¬λ μ μ μμ νμ
protocol TextRepresentable {
var textualDescription: String { get }
}
// νλ‘ν μ½ νμ
μ λ°°μ΄
let textRepresentableThings: [TextRepresentable] = [someTextRepresentableInstance, anotherTextRepresentableInstance]
// νλ‘ν μ½ νμ
μ λ§€κ°λ³μλ‘ λ°λ ν¨μ
func printDescription(_ thing: TextRepresentable) {
print(thing.textualDescription)
}
π μμ (Delegation)
μμμ ν΄λμ€λ κ΅¬μ‘°μ²΄κ° μ± μμ μΌλΆλ₯Ό λ€λ₯Έ νμ (λ리μ)μκ² λκΈ°λ λμμΈ ν¨ν΄μΌλ‘, νλ‘ν μ½μ ν΅ν΄ μ½κ² ꡬνν μ μμ΅λλ€:
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didEndRound round: Int, winner: Int?)
func gameDidEnd(_ game: DiceGame)
}
class DiceGame {
weak var delegate: DiceGameDelegate?
func play() {
delegate?.gameDidStart(self)
// κ²μ λ‘μ§...
delegate?.gameDidEnd(self)
}
}
// μμ ν¨ν΄ ꡬν
class DiceGameTracker: DiceGameDelegate {
func gameDidStart(_ game: DiceGame) {
print("κ²μμ΄ μμλμμ΅λλ€")
}
func game(_ game: DiceGame, didEndRound round: Int, winner: Int?) {
// λΌμ΄λ μ’
λ£ μ²λ¦¬
}
func gameDidEnd(_ game: DiceGame) {
print("κ²μμ΄ μ’
λ£λμμ΅λλ€")
}
}
μ°Έκ³ : λ©λͺ¨λ¦¬ λμ(κ°ν μ°Έμ‘° μν)λ₯Ό λ°©μ§νκΈ° μν΄ λ리μ νλ‘νΌν°λ λ³΄ν΅ weak μ°Έμ‘°λ‘ μ μΈν©λλ€.
π νμ₯μ ν΅ν νλ‘ν μ½ μ±ν (Adding Protocol Conformance with Extensions)
μ΄λ―Έ μ‘΄μ¬νλ νμ μ νλ‘ν μ½μ μ±ννκΈ° μν΄ νμ₯(extension)μ μ¬μ©ν μ μμ΅λλ€:
protocol TextRepresentable {
var textualDescription: String { get }
}
// κΈ°μ‘΄ νμ
μ νλ‘ν μ½ μ±ν μΆκ°
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
μ‘°κ±΄λΆ νλ‘ν μ½ μ€μ (Conditional Conformance)
νΉμ 쑰건μμλ§ νλ‘ν μ½μ μ€μνλλ‘ λ§λ€ μ μμ΅λλ€:
extension Array: TextRepresentable where Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
μ μμ μμλ λ°°μ΄μ μμκ° TextRepresentableμ μ€μν λλ§ λ°°μ΄λ TextRepresentableμ μ€μν©λλ€.
π νλ‘ν μ½ μμ (Protocol Inheritance)
νλ‘ν μ½μ νλ μ΄μμ λ€λ₯Έ νλ‘ν μ½μ μμν μ μμ΅λλ€:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// μΆκ° μꡬμ¬ν
}
μλ₯Ό λ€μ΄:
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
PrettyTextRepresentableμ μ±ννλ νμ μ TextRepresentableμ μꡬμ¬νκ³Ό PrettyTextRepresentableμ μΆκ° μꡬμ¬νμ λͺ¨λ μΆ©μ‘±ν΄μΌ ν©λλ€.
π ν΄λμ€ μ μ© νλ‘ν μ½ (Class-Only Protocols)
νλ‘ν μ½μ΄ ν΄λμ€μλ§ μ±νλλλ‘ μ ννλ €λ©΄ AnyObject νλ‘ν μ½μ μμν©λλ€:
protocol SomeClassOnlyProtocol: AnyObject {
// ν΄λμ€ μ μ© νλ‘ν μ½ μ μ
}
μ¬μ© μκΈ°: μ°Έμ‘° μλ―Έλ‘ (reference semantics)μ΄ νμν κ²½μ°λ μ½ν μ°Έμ‘°(weak reference)κ° νμν κ²½μ°μ ν΄λμ€ μ μ© νλ‘ν μ½μ μ¬μ©ν©λλ€.
π νλ‘ν μ½ ν©μ± (Protocol Composition)
νλμ νμ μ΄ μ¬λ¬ νλ‘ν μ½μ λμμ μ€μνλλ‘ μꡬν μ μμ΅λλ€:
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
// Namedμ Aged νλ‘ν μ½μ λͺ¨λ μ€μνλ νμ
λ§ νμ©
func wishHappyBirthday(to celebrator: Named & Aged) {
print("μμΌ μΆνν©λλ€, \(celebrator.name)λ! λΉμ μ \(celebrator.age)μ΄μ΄ λμκ΅°μ!")
}
ν΄λμ€ νμ κ³Ό νλ‘ν μ½λ ν¨κ» μ‘°ν©ν μ μμ΅λλ€:
func beginConcert(in location: Location & Named) {
// Locationμ νμ ν΄λμ€μ΄λ©΄μ Named νλ‘ν μ½μ μ€μνλ νμ
λ§ νμ©
}
π νλ‘ν μ½ μ€μμ± κ²μ¬ (Checking Protocol Conformance)
νμ μ΄ νΉμ νλ‘ν μ½μ μ€μνλμ§ νμΈνκΈ° μν΄ is λ° as μ°μ°μλ₯Ό μ¬μ©ν μ μμ΅λλ€:
if someObject is HasArea {
// someObjectλ HasArea νλ‘ν μ½μ μ€μν¨
}
// λ€μ΄μΊμ€ν
(μμ ν λ°©μ)
if let objectWithArea = someObject as? HasArea {
print("λ©΄μ μ \(objectWithArea.area)μ
λλ€")
}
π μ΅μ λ νλ‘ν μ½ μꡬμ¬ν (Optional Protocol Requirements)
νλ‘ν μ½μ μΌλΆ μꡬμ¬νμ μ νμ μΌλ‘ ꡬνν μ μκ² νλ €λ©΄ @objc μμ±κ³Ό optional ν€μλλ₯Ό μ¬μ©ν©λλ€:
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
μ΅μ λ μꡬμ¬νμ κ°μ§ νλ‘ν μ½μ Objective-C ν΄λμ€λ @objc μμ±μ΄ νμλ ν΄λμ€μμλ§ μ±νν μ μμ΅λλ€.
μ΅μ λ μꡬμ¬νμ μ¬μ©ν λλ μ΅μ λ 체μ΄λμ ν΅ν΄ μμ νκ² μ κ·Όν©λλ€:
if let amount = dataSource?.increment?(forCount: count) {
count += amount
}
π νλ‘ν μ½ νμ₯ (Protocol Extensions)
νλ‘ν μ½μ κΈ°λ³Έ ꡬνμ μ 곡νκΈ° μν΄ νμ₯μ μ¬μ©ν μ μμ΅λλ€:
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
μ΄μ RandomNumberGenerator νλ‘ν μ½μ μ€μνλ, λͺ¨λ νμ μ μλμΌλ‘ randomBool() λ©μλλ₯Ό μ¬μ©ν μ μμ΅λλ€.
μ μ½ μ‘°κ±΄μ΄ μλ νλ‘ν μ½ νμ₯
νΉμ 쑰건μ λ§μ‘±νλ κ²½μ°μλ§ νλ‘ν μ½ νμ₯μ μ μ©ν μ μμ΅λλ€:
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
// μ¬μ© μ
let equalNumbers = [100, 100, 100, 100]
print(equalNumbers.allEqual()) // true
μ μμ μμλ Collectionμ μμκ° Equatableμ μ€μν λλ§ allEqual() λ©μλλ₯Ό μ¬μ©ν μ μμ΅λλ€.
π νλ‘ν μ½κ³Ό μλ ν©μ± (Synthesized Implementations)
Swiftλ νΉμ νλ‘ν μ½μ λν΄ μλμΌλ‘ ꡬνμ μ 곡ν©λλ€:
- Equatable: μ μ₯ νλ‘νΌν°λ§ μλ ꡬ쑰체λ μ΄κ±°ν
- Hashable: μ μ₯ νλ‘νΌν°λ§ μλ ꡬ쑰체λ μ΄κ±°ν
- Comparable: μμκ°μ΄ μλ μ΄κ±°ν
struct Vector3D: Equatable {
var x = 0.0, y = 0.0, z = 0.0
// == μ°μ°μκ° μλμΌλ‘ ν©μ±λ¨
}
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
// <, >, <=, >= μ°μ°μκ° μλμΌλ‘ ν©μ±λ¨
}
π μ 리
νλ‘ν μ½μ Swiftμ ν΅μ¬ κΈ°λ₯ μ€ νλλ‘, λ€μν νμ μ΄ κ³΅ν΅λ κΈ°λ₯μ ꡬνν μ μλλ‘ μ μ°ν μ²μ¬μ§μ μ 곡ν©λλ€. νλ‘ν μ½μ ν΅ν΄:
- μΈν°νμ΄μ€ λΆλ¦¬: ꡬνκ³Ό μΈν°νμ΄μ€λ₯Ό λΆλ¦¬νμ¬ μ½λμ λͺ¨λμ± ν₯μ
- λ€νμ± μ 곡: μλ‘ λ€λ₯Έ νμ μ΄ κ³΅ν΅λ λμμ ꡬν
- μ½λ μ¬μ¬μ©: νλ‘ν μ½ νμ₯μ ν΅ν κΈ°λ³Έ ꡬν μ 곡
- ν μ€νΈ μ©μ΄μ±: λͺ¨μ κ°μ²΄(mock)λ₯Ό μ½κ² λ§λ€μ΄ ν μ€νΈ κ°λ₯
νλ‘ν μ½μ κ°μ²΄ μ§ν₯ νλ‘κ·Έλλ°κ³Ό ν¨μν νλ‘κ·Έλλ°μ μ₯μ μ κ²°ν©ν νλ‘ν μ½ μ§ν₯ νλ‘κ·Έλλ°(Protocol-Oriented Programming)μ κΈ°λ°μ΄ λ©λλ€.
'π₯ Bread Basics > Swift' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
Swift 곡μ λ¬Έμ μ 리 - λ©λͺ¨λ¦¬ μμ μ± (Memory Safety) (0) | 2025.04.13 |
---|---|
Swift 곡μ λ¬Έμ μ 리 - μλ μ°Έμ‘° μΉ΄μ΄ν (Automatic Reference Counting) (0) | 2025.04.13 |
Swift 곡μ λ¬Έμ μ 리 - λΆν¬λͺ ν νμ (Opaque Types) (0) | 2025.04.13 |
Swift 곡μ λ¬Έμ μ 리 - μ λλ¦ (Generics) (0) | 2025.04.13 |
Swift 곡μ λ¬Έμ μ 리 - νμ₯ (Extensions) (0) | 2025.04.13 |
Swift 곡μ λ¬Έμ μ 리 - μ€μ²©λ νμ (Nested Types) (1) | 2025.04.12 |
Swift 곡μ λ¬Έμ μ 리 - νμ μΊμ€ν (Type Casting) (0) | 2025.04.12 |
Obj-C 곡μ λ¬Έμ μ 리 - κ°μ²΄, ν΄λμ€, λ©μμ§ (0) | 2025.04.11 |