Swift에서 같은 이름의 Method를 호출하면서 상황에 따라 기능을 다르게 할 수 있다.
이를 Method Swizzling이라고 하는데, 간단히 알아보자.
예를들어,
calFunction을 호출하는데 어쩔 때는 + 기능을 수행하고, 어쩔 때는 - 기능을 수행하는 셈이다.
같은 함수를 호출하지만 다른 기능을 하는데, 전환을 런타임 중에 할 수 있다는 것이 특징이다.
이걸 왜 써야하는지 본다면, 이미 정의된 Swift 또는 iOS 함수를 다른 기능으로 호출하고 싶을 때 사용한다.
대중적이고 쉬운 예시로 UIKit의 ViewContoller Life Cycle이 있다.
내가 만든 모든 ViewController의 viewDidAppear, viewWillAppear 등의 기능을 한번에 변경할 수 있다.
특정 시점에 모든 ViewController의 viewDidAppear가 어떤 기능을 수행했다가
다시 특정 시점에 원래대로 돌아오도록 하고 싶다면, Method Swizzling을 하면 된다.
이제 코드로 알아보자.
class SwzzlingClass {
@objc dynamic func originFunction() -> String{
return #function
}
}
예시를 위해 class를 하나 만든다.
swizzling 될 함수는 @objc dynamic 키워드를 써야하는데, @objc 키워드는 struct에서 못쓴다.
두 키워드는 뒤에서 알아보자.
extension SwizzlingClass {
class func methodSwzzling() {
// Swizzling할 두 함수를 #selector로 지정한다.
let originFunctionSelector = #selector(SwizzlingClass.originFunction)
let swizzlingFunctionSelector = #selector(SwizzlingClass.swizzlingFunction)
// 각 selector의 포인터를 class_getInstanceMethod 사용해 받는다.
guard
let originalMethod = class_getInstanceMethod(SwizzlingClass.self, originFunctionSelector),
let swizzledMethod = class_getInstanceMethod(SwizzlingClass.self, swizzlingFunctionSelector)
else { return }
// 두 함수의 포인터를 바꾼다.
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@objc func swizzlingFunction() -> String{
return #function
}
}
꼭 extension으로 하지 않아도 괜찮다.
코드를 나누기 위해 extension을 썼을 뿐이다.
let swizzlingClass = SwizzlingClass()
...
Button("Switch Method") {
SwizzlingClass.methodSwzzling()
self.switchMethodString = self.swizzlingClass.originFunction()
}
이제 버튼을 누를때마다 originFunction() 메소드가 다른게 나올 것이다.
아주 잘 작동한다!
왜 가능할까?
코드의 키워드에서 봤듯 Objective-C와 연관이 있다.
Swift는 Objective-C의 영향을 받아 많들어졌고, Objective-C는 메소드 실행이 런타임에 결정이 된다는 특징이 있다.
쉽게 말하자면 메소드가 실행되기 전에 메소드가 기능을 가리키는 포인터의 방향을 바꾸면 다른 내용으로 함수가 실행되는 것이다.
이를 이용해서 Swizzling이 가능하다.
dynamic 키워드도 이런 의미에서 출발한다.
메소드가 런타임에 위치(포인터)가 바뀔 수 있다는 걸 명시해주는 것이다.
최근 SwiftUI를 공부하고 있어 사실 해당 기법을 어떻게 활용할 수 있을지 잘 모르겠다.
'iOS > Swift' 카테고리의 다른 글
[Swift] 기본 데이터 타입 (0) | 2022.06.04 |
---|---|
[Swift] Protocol (0) | 2022.05.27 |
[Swift] Closures (2) (0) | 2022.05.10 |
[Swift] Closures (1) (0) | 2022.05.10 |
[Swift] DispatchQueue (0) | 2022.04.19 |