소중한 야근의 결실 ☻
를 제외한 다른 언어들의 화면이 뜨지 않는다!!!
Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray objectAtIndex:effectiveRange:: Out of bounds
오류는 Out of range라고 뜨는데 이게 어디서 뜨는건지... 당최 알 수가 없었다.
무엇의 범위가 바깥으로 나갔는지,
베트남어나 다른언어일때 화면이 그려지지 않는다!!
let text = CustomLocalizeHelper.localizedString(forKey: "key")
let attributedString = NSMutableAttributedString.init(string: text)
attributedString.addAttribute(
NSAttributedString.Key.underlineStyle,
value: 1,
range: NSRange.init(
location: text.startIndex.encodedOffset,
length: text.endIndex.encodedOffset
)
)
attributedString.addAttribute(
NSAttributedString.Key.foregroundColor,
value: UIColor.white,
range: NSRange.init(
location: text.startIndex.encodedOffset,
length: text.endIndex.encodedOffset
)
)
attributedString.addAttribute(
NSAttributedString.Key.font,
value: UIFont.systemFont(ofSize: 12, weight: .regular),
range: NSRange.init(
location: text.startIndex.encodedOffset,
length: text.endIndex.encodedOffset
)
)
let range = NSRange(location: 0, length: (text as NSString).length)
attributedString.addAttribute(
NSAttributedString.Key.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: range
)
attributedString.addAttribute(
NSAttributedString.Key.foregroundColor,
value: UIColor.white,
range: range
)
attributedString.addAttribute(
NSAttributedString.Key.font,
value: UIFont.systemFont(
ofSize: 12,
weight: .regular),
range: range
)
여기서 중요한 부분은 문자열의 길이를 계산하는 데 text.startIndex.encodedOffset
와 text.endIndex.encodedOffset
대신 text as NSString
을 사용하여 문자열의 길이를 얻는 것입니다. NSRange(location:length:)
초기화에 이 값을 사용하면 범위가 정확히 지정됩니다.
text.startIndex.encodedOffset
과 text.endIndex.encodedOffset
을 사용하는 것 자체는 문법적으로 문제가 없지만, 다국어 문자열에서 발생하는 문제는 인덱싱 방식과 관련이 있습니다.
특히, 다국어 문자열의 경우 단순히 startIndex와 endIndex를 사용하는 것이 올바르지 않을 수 있습니다. 이유는 다음과 같습니다:
Swift의 String은 유니코드 스칼라 값을 사용하여 인덱싱되기 때문에, 단순히 startIndex
와 endIndex
를 사용하면 예상치 못한 결과를 초래할 수 있습니다.
특히 다국어 문자열에서는 유니코드 문자들이 여러 개의 코드 포인트로 구성될 수 있기 때문입니다.
NSString
의 길이 사용: NSString
의 length
속성은 NSString
의 바이트 길이를 반환하며,
이는 다국어 문자열에서도 올바르게 작동합니다.
이는 특히 NSRange와 같은 NS 기반 API를 사용할 때 중요합니다.
유니코드 스칼라 값을 사용하여 인덱싱되기 때문에, 단순히 startIndex
와 endIndex
를 사용하면 예상치 못한 결과를 초래할 수 있습니다?
예를 들어, 특정 유니코드 문자는 여러 유니코드 스칼라 값으로 구성될 수 있으며, 이 경우 문자열의 실제 길이와 문자 수가 일치하지 않을 수 있습니다.
let text = "こんにちは"
let startIndex = text.startIndex
let endIndex = text.endIndex
print(text[startIndex]) // こ
print(text[endIndex]) // Error
여기서 text.endIndex는 문자열의 끝 이후를 가리키기 때문에, 직접 접근하려고 하면 오류가 발생합니다. 정확히 말하면 text.endIndex는 문자열의 마지막 유효 인덱스보다 하나 더 큰 인덱스를 가리킵니다. 따라서 이 인덱스로 문자열에 접근할 수 없습니다.
let endIndex = text.index(before: text.endIndex)
이렇게 사용해야 마지막 글자 가지고 올 수 있음.
또한, Swift 문자열에서 encodedOffset은 문자열의 UTF-8 인코딩에서의 바이트 오프셋을 의미하며, 이는 문자열의 길이와 항상 일치하지 않습니다. 특히 다국어 문자열에서는 더욱 그렇습니다.
encodedOffset은 문자열의 UTF-8 인코딩에서의 바이트 오프셋을 의미하며, 이는 문자열의 길이와 항상 일치하지 않습니다.
예를 들어, 영어 문자열과 다국어 문자열의 encodedOffset과 길이 차이를 살펴보겠습니다.
예시 1: 영어 문자열
let englishText = "hello"
let startIndex = englishText.startIndex
let endIndex = englishText.endIndex
print(englishText[startIndex]) // h
print(englishText[englishText.index(before: endIndex)]) // o
print(startIndex.encodedOffset) // 0
print(endIndex.encodedOffset) // 5
예시 2: 다국어 문자열 (일본어)
let japaneseText = "こんにちは"
let startIndex = japaneseText.startIndex
let endIndex = japaneseText.endIndex
print(japaneseText[startIndex]) // こ
print(japaneseText[japaneseText.index(before: endIndex)]) // は
print(startIndex.encodedOffset) // 0
print(endIndex.encodedOffset) // 15
영어 문자열 (hello):
문자열의 길이는 5입니다.
각 문자는 1 바이트로 인코딩됩니다.
startIndex.encodedOffset은 0이고, endIndex.encodedOffset은 5입니다.
다국어 문자열 (こんにちは):
문자열의 길이는 5입니다.
각 일본어 문자는 3 바이트로 인코딩됩니다.
startIndex.encodedOffset은 0이고, endIndex.encodedOffset은 15입니다.
영어 문자열의 경우, 문자열의 길이와 encodedOffset이 일치합니다. 이는 각 문자가 1 바이트로 인코딩되기 때문입니다.
다국어 문자열의 경우, 문자열의 길이와 encodedOffset이 일치하지 않습니다. 이는 각 문자가 여러 바이트(여기서는 3 바이트)로 인코딩되기 때문입니다.
이 차이는 특히 NSRange와 같은 범위를 설정할 때 중요합니다. encodedOffset을 잘못 사용하면 범위 오류가 발생할 수 있습니다. 따라서 문자열의 길이를 계산할 때는 UTF-8 바이트 오프셋 대신 NSString의 length 속성을 사용하는 것이 더 안전합니다.
다시 원래 코드로 돌아가면, 문자열의 범위를 설정할 때 Swift 문자열의 인덱스를 직접 사용하지 않고 NSString을 사용하여 범위를 계산하는 이유를 이해할 수 있습니다.
이는 특히 다국어 문자열에서 인덱스가 정확히 맞지 않아 발생하는 오류를 방지하기 위함입니다.
다국어를 사용할땐 length를 이용해 text 범위로 사용하자!