안녕하세요 :) 소들입니다
오랜만의 포스팅이네요 엉엉 😱
퇴사 후 하루도 못 쉬다가 처음으로 4일 간 휴식을 취하고 왔읍니다..!
영양가 없는 TMI는 집어치우고
오늘 공부할 것은 바로바로
Method Swizzling
이구요!
제가 모르는 개념이라 포스팅하며 공부해보려 합니다 :)
모든 포스팅은 편의 말투로 합니다~!!
1. Method Swizzling이란?
자, 먼저 Swizzling(스위즐링)이란 뒤섞다 라는 뜻임!
그럼 앞에 Method가 붙었으니,
뭔가 메서드를 뒤섞는다는 것일 거란 느낌적인 느낌 👀
맞음 Method Swizzling이란,
Runtime 시점에 기존 메서드를 다른 메서드로 바꾸어 실행하는 것
을 말함!!!!
ㅎ... 뭔말인지 이해가 안 가고 왜 사용하는지도 당장은 모르시겠지만
일단 뭐 런타임 시점에 메서드를 교체할 수 있구먼? 정도로 생각하면 될 것 같음!!
일단 정의만 알아봤음 :)
2. 언제 사용할까
음 그럼 언제 사용할까부터 생각을 해보고, 그 뒤에 어떻게 구현하는지 보겠음!!!
찾아보니까 보통 어떨 때 사용하냐면
이미 정해진 iOS의 특정 메소드가 실행될 때,
해당 메서드 대신 다른 메서드가 실행되도록 바꿀 때
뭔 소리야.. 🌚
자, 우리가 ViewController를 만들다 보면 viewDidLoad, viewWillAppear 등등
프레임워크단에서 자동으로 호출되는 특정 메서드들이 있잖음?
근데 만약 나는 ViewWillAppear가 호출될 때마다,
이를 로그로 출력하는 기능을 넣고 싶음!!!
그러면 일반적인 방법으로 어떻게 구현할 수 있냐면,
먼저, ViewController마다 ViewWillAppear 메서드를 오버라이딩 해서 할 수도 있음
근데 이런 경우 ViewController가 많아지면 많아질 수록, 똑같은 내용의 코드를 매번 작성해주어야 함
또는, UIViewController를 상속받는 서브 클래스를 만들어주고,
거기서 viewWillAppear를 오버라이딩 한 후, 이후 ViewController에서 이 서브 클래스를 상속받게 할 수도 있음!
근데 ViewWillAppear 하나 바꾸겠다고, 서브 클래싱을 하고, 앞으로의 ViewController의 상속을 다 바꿔주고..
서브 클래싱 하면 클래스 간 가계도도 커지는데 말이지...
따라서, 이럴 때 사용하는 것이 Method Swizzling임!!
말 그대로, 기존에 있는 메서드를 런.타.임에 내가 원하는 메서드로 바꿔주는 것임!!!
단순히 메서드를 바꿔주는 것이기 때문에, 굳이 서브 클래스를 만들 필요도 없음!!! 간편간편
또 찾아보니까 일반적으로는
앱에 분석기능을 통합할 때
특정 기능을 클래스&서브 클래스 모두 한 번에 적용시키고 싶을 때
뭐 등등에 사용한다고 함
예를 들어 모든 ViewController가 뭐 얼만큼 불렸는지..를 분석하고 싶다면
Method Swizzling을 사용하여, 모든 ViewController가 불릴 때마다 분석 기능을 적용시키면 편할 거 같음 :)
자, 그럼 언제, 왜 사용하는지도 알았으니
어떻게 사용하는지 보자 :)
3. 사용 방법
위에서 설명 그대로,
viewWillAppear가 불릴 때마다 swizzleViewWillAppear로 바꿔주는 코드로 예를 들어보겠음
먼저 UIViewController의 기능을 바꿔주는 것이니,
UIViewController를 확장하고 다음과 같이
extension UIViewController {
@objc public func swizzleViewWillAppear(animated: Bool) {
print("🌞 swizzleViewWillAppear")
}
}
|
바꿔줄 메서드인 swizzleViewWillAppear 메서드를 추가해줌
추가 했으면, 이제 이 Method Swizzling을 적용하는
extension UIViewController {
class func swizzleMethod() {
}
@objc public func swizzleViewWillAppear(animated: Bool) {
print("🌞 swizzleViewWillAppear")
}
}
|
타입 메서드를 다음과 같이 하나 만들어줌
그 다음으론, 기존 메서드와, 바꿔줄 메서드를 다음과 같이
extension UIViewController {
class func swizzleMethod() {
let originalSelector = #selector(viewWillAppear)
let swizzleSelector = #selector(swizzleViewWillAppear)
}
@objc public func swizzleViewWillAppear(animated: Bool) {
print("🌞 swizzleViewWillAppear")
}
}
|
Selector를 이용해 가져옴!
그리고 다음과 같이 인스턴스 메서드를 반환하게 해주는 메서드를 guard문을 통해 작성하는데,
extension UIViewController {
class func swizzleMethod() {
let originalSelector = #selector(viewWillAppear)
let swizzleSelector = #selector(swizzleViewWillAppear)
guard
let originMethod = class_getInstanceMethod(UIViewController.self, originalSelector),
let swizzleMethod = class_getInstanceMethod(UIViewController.self, swizzleSelector)
else { return }
}
@objc public func swizzleViewWillAppear(animated: Bool) {
print("🌞 swizzleViewWillAppear")
}
}
|
여기서 이해가 안 갈 것이라고 생각함 :)
왜냐? 이게 뭔 코드인지 내가 이해가 안 됐었으니까 하하!!!!!
먼저 class_getInstanceMethod는 다음과 같이 두 개의 파라미터를 가지는데,
첫 번째 파라미터로 클래스 자체를 가짐
즉, 우리는 바꿔주고 싶은 메서드를 가진 클래스 자신을 넣어주는 것이고,
두 번째 파라미터론 해당 클래스에서 어떤 메서드를 가져올 것인지, Selector로 넣어주는 것임
자, 여기서 Selector라는 것에 대해 인지를 정확히 해야 이해가 감!!
Selector라는 것은 단순히 런타임 시점에 함수 테이블에서 (내가 지정한)함수 이름을 찾아 실행하는 것이지,
함수 자체를 가리키는 함수 포인터가 아님!!
(따라서 Objective-C에선 없는 함수명을 넣어도 컴파일 시점엔 에러가 나지 않고,
런타임 시점에 함수 테이블에서 함수 이름을 찾을 때 에러가 남!!
근데 Swift는 없는 함수명 넣는 거 자체가 컴파일 에러로 빠지는 듯 하다..?
이는 나중에 Selector에 대해 자세히 공부할 때 다뤄보겠움..;;)
쨌든 이러나 저러나 Selector라는 것은 함수 자체를 가리키는 것이 아니기 때문에,
진짜 UIViewController 클래스 안의 ViewWillAppear, swizzleViewWillAppear라는
인스턴스 메서드를 받는 것이 class_getInstanceMethod 라는 함수임
Selector에 대응하는 메서드가 함수 테이블에 없을 수도 있으니 반환값은 Optional Type이고,
따라서 guard let 구문을 이용한 것임!!!!
이해가 갔..으려나,.. /??? 👀
자, 마지막으로 이제 swizzling 하는 메서드인
extension UIViewController {
class func swizzleMethod() {
let originalSelector = #selector(viewWillAppear)
let swizzleSelector = #selector(swizzleViewWillAppear)
guard
let originMethod = class_getInstanceMethod(UIViewController.self, originalSelector),
let swizzleMethod = class_getInstanceMethod(UIViewController.self, swizzleSelector)
else { return }
method_exchangeImplementations(originMethod, swizzleMethod)
}
@objc public func swizzleViewWillAppear(animated: Bool) {
print("🌞 swizzleViewWillAppear")
}
}
|
method_exchangeImplementations를 실행해 주면,
Swizzling 작업은 끝!!!!!!!
그럼, 이제 이 메서드를 실행시켜 주어야 하는데
이렇게 AppDelegate에서 최초 한번 실행시켜주면
쨘! ViewWillAppear가 실행될 때마다,
우리가 Swizzling 해준 메서드로 바껴서 실행됨!!!!
+ 참고로 서브 클래스에서 Swizzling 한 메서드를 오버라이딩 할 경우,
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("🌑 override viewWillAppear")
}
|
이런 경우
이렇게 Swizzling 된 메서드가 오버라이딩 됨다 :)
4. Method Swizzling은 꼭 필요할 때만 쓰자
Method Swizzling은 앞서 봤듯이 런타임 시점에
메서드를 임의로 바꾸는 것이기 때문에 에러가 날 가능성도 있고...
iOS 버전이 올라가며 우리가 Swizzling한 메서드가 바뀌면 문제가 될 수 있다함
때문에 Method Swizzling은 꼭 필요할 때 잘 사용하면
정말 최적의 기능인 건 맞으나,
절대 남발해선 안 되고 웬만해선 사용을 자제하라!!!
.
.
.
오늘은 내가 모르는 내용 공부 끝!
하지만 무엇이든 '완벽히' 이해하기까진 참 먼 것 같다 ㅠ ㅠ
'iOS > Swift' 카테고리의 다른 글
Swift) 함수(Function) 정복하기 (2/3) - inout / 가변 파라미터 / 기본값 / 중첩 함수 (5) | 2020.12.15 |
---|---|
Swift) 함수(Function) 정복하기 (1/3) - 함수? 메서드? 클로저? (13) | 2020.12.15 |
Swift) JSON이 도대체 뭘까 (4) | 2020.12.03 |
Swift) 싱글톤 패턴(Singleton Pattern) (23) | 2020.12.02 |
Swift) Codable - JSON을 쉽게 Encoding / Decoding 하자 (9) | 2020.11.30 |