지난 글에서 크래시를 해결하면서 작성했던 코드의 동작 원리 즉, NSString 에서 String 으로 변환될 때 왜 nil 이 아니라 빈 문자열로 대체되서 반환이 되는가에 대해 원리를 파악하고자 합니다.
일단 우리가 전화번호를 통해 본인 인증을 하고자 할 때 iOS 13 에서는 OS 가 메시지에서 인증 번호를 가져와 사용자에게 자동 완성을 제공합니다. 이때 자동 완성 영역을 터치하면 OS 는 NSString.replacingCharacters(in range: NSRange, with replacement: String) 메서드를 호출합니다. 일단 호출하는 클래스가 NSString 이기 때문에 "Objective-C 코드 베이스에서 작동하는 게 아닐까?" 라고 추론했습니다.
NSString+Extension.swift
extension NSString {
class func swizzleReplacingCharacters() {
let originalMethod = class_getInstanceMethod(
NSString.self, #selector(NSString.replacingCharacters(in:with:)))
let swizzledMethod = class_getInstanceMethod(
NSString.self, #selector(NSString.swizzledReplacingCharacters(in:with:)))
guard let original = originalMethod, let swizzled = swizzledMethod else {
return
}
method_exchangeImplementations(original, swizzled)
}
func swizzledReplacingCharacters(in range: NSRange, with replacement: String) -> String {
return self.swizzledReplacingCharacters(in: range, with: replacement)
}
}
그리고 해당 메서드는 Swift 에서 Swizzling 될 때 String 형태로 변환되는 것 같습니다.
해당 내용은 Foundation Framework 의 NSString.swift 파일에서 찾을 수 있었습니다.
Foundation/NSString.swift
extension NSString : _StructTypeBridgeable {
public typealias _StructType = String
public func _bridgeToSwift() -> _StructType {
return _StructType._unconditionallyBridgeFromObjectiveC(self)
}
}
Objective-C 의 NSString 에서 Swift 로 브릿징을 하면 unconditionallyBridgeFromObjectiveC 를 호출하는 것 같습니다.
Foundation/String.swift
static public func _unconditionallyBridgeFromObjectiveC(_ source: _ObjectType?) -> String {
if let object = source {
var value: String?
_conditionallyBridgeFromObjectiveC(object, result: &value)
return value!
} else {
return ""
}
}
unconditionallyBridgeFromObjectiveC 메서드 내부를 보면 인자로 받은 source 라는 객체를 if let 을 통해 내부에서 옵셔널 바인딩을 하고 있습니다. 그래서 NSString 으로부터 전달받은 인자가 nil 이라면 내부의 옵셔널 바인딩을 통해 빈 문자열을 반환하고 nil 이 아니라면 String.swift 내부에 정의된 _contionallyBridgeFromObjectiveC 메서드에 의해서 String 타입으로 변환된 value 를 반환합니다.
만약 틀린 내용이 있으면 지적해주세요. 해당 내용을 포스팅에 반영하도록 하겠습니다.
오늘 포스팅을 작성하는 데 도움을 주신 두 분이 계십니다.
- 야곰 님께서 NSString 이 Swift 의 String 으로 브릿징될 때 빈 문자열로 넘어갈 것이라고 힌트를 주셨습니다.
- 라이노 님께서 Foundation Framework 내부를 살펴보라 조언해 주셨습니다.
- 출처 : Foundation Framework