저는 주로 백준 온라인 저지의 알고리즘 문제를 풀 때, split 메서드를 사용하여 인풋을 공백 단위로 받아오곤 했는데, components를 사용하는 방법도 있어 아래와 같이 두 가지 코드를 번갈아가며 사용했었습니다.
let input = readLine()?.split( ... )
let input = readLine()?.components( ... )
이번 포스팅을 통해 두 메서드의 차이점을 한번 알아보도록 하겠습니다.
components return [String] split return [Substring]
가장 눈에 띄게 확인할 수 있었던 차이점은 리턴타입입니다.
func components<T>(separatedBy separator: T) -> [String] where T : StringProtocol
components는 StringProtocol 의 타입인 T를 받아 [String]을 반환합니다.
func split(
separator: Character,
maxSplits: Int = Int.max,
omittingEmptySubsequences: Bool = true) -> [Substring]
반면에 split은 [Substring]을 반환해주고 있는 모습을 확인할 수 있습니다.
String and Substring
Substring을 잘 이해하면, split을 사용할 때, components를 사용할 때의 이점을 잘 살릴 수 있겠다는 생각을 가지고 Substring에 대해서 알아보겠습니다.
Substrings in Swift have most of the same methods as strings, which means you can work with substrings the same way you work with strings.
Swift.org 의 Substrings 부분을 보면 Substring에 대한 설명이 나옵니다. Swift의 Substrings은 string과 거의 동일한 메서드를 가지고 있고, 이는 string과 똑같이 활용할 수 있다는 의미라고 합니다.
String과 동일하게 활용할 수 있는 Substring! 차이점도 아래에 나와있는 것을 확인할 수 있습니다.
The difference between strings and substrings is that, as a performance optimization, a substring can reuse part of the memory that’s used to store the original string, or part of the memory that’s used to store another substring.
여기서 제가 가장 흥미로웠던 구문은 'a substring can reuse part of the memory that's used to store the original stirng' 입니다. 원래의 string이 저장된 메모리를 재사용한다!
바로 확인해보겠습니다.
let base: String = "This is base string"
let componentString = base.components(separatedBy: " ")
let splitString = base.split(separator: " ")
base라는 String을 하나는 components로, 하나는 split으로 나누었습니다.
우선 base에 대한 정볼르 눈에 담아두겠습니다.
그 다음은 componentString으로 가보겠습니다.
components 메서드로 String을 나눈 결과는 base라는 String과 아무런 관련이 없습니다. 새로운 메모리 공간에 "This", "is", "base", "string" 각각이 할당되고, 그것들의 배열을 만들어 낸 것을 확인할 수 있습니다.
하지만 splitString! 이녀석을 한번 확인해보겠습니다.
어디서 많이 본 주소값이 있습니다. 바로 저 0x8000000100003eb0 이 주소! 아까 base의 StringObject가 담긴 주소값과 동일한 것을 확인할 수 있습니다. 이를 startIndex와 endIndex를 활용하여 Slice 즉, 잘라서 표시해주는 것을 알 수 있습니다.
이 부분에서 위에서 확인했던 Original String의 메모리 공간을 재사용 한다는것을 확인할 수 있습니다.
그림으로 확인해보면 (그림을 잘 못그려서 미리 죄송합니다. ㅎㅎ)
이런 차이점이 있습니다. 위와 같은 특징 때문에 공식 문서에서 Substring은 String과 달리 해당 String을 활용하고 있을 때만 짧은 시간 그 String을 활용하여 어떤 작업을 할 때 사용할 수 있다고 합니다.
However, unlike strings, you use substrings for only a short amount of time while performing actions on a string.
그럼 이제 componentString과 splitString을 활용하여 새로운 String을 생성하면 어떤 결과가 나오는지 한번 확인해보겠습니다.
let newString = String(splitString[0])
let componentsNewString = String(componentString[0])
newString은 splitString[0] 즉, "This" 라는 String을 새로운 String으로 가지게 됩니다.
그림으로 그려보면
newString이라는 새로운 String을 만들어 "This"를 참조하게 만드는 순간 Original String에서 벗어나 새로운 공간을 참조하게됩니다.
정리
Substring은 기존의 String의 메모리 공간을 재사용 하여 문자열을 표현한다는 사실을 알게되었습니다.
하지만 실제 백준에서 사용하는 방법인 아래의 코드들
let componentInput = base.components(separatedBy: " ")
let splitInput = base.split(separator: " ").map({ String($0) })
이 둘 중 어떤 것이 더 성능에 좋은지? 에 대한 정답은 없는것 같다는 것이 저의 생각입니다. 결국 split도 Substring을 그대로 사용하지 않고 다시 map을 통해 String으로 변환하는 작업이 들어가므로 결국 똑같지 않을까? 라는 것이 저의 생각입니다. ㅎㅎㅎ
참고
https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html
(Substrings 파트)
* 혼자 공부하면서 이해한 부분이니 혹시나 틀리거나 잘못 이해하고 있는 부분이 있다면 댓글로 알려주시면 감사하겠습니다!!
'Swift' 카테고리의 다른 글
Swift ) Two-Phase Initialization? (0) | 2022.05.27 |
---|---|
Swift ) Designated Initializer, Convenience Initializer (0) | 2022.05.02 |
Swift ) What is AnyObject? (0) | 2022.04.06 |
Swift ) URL에 한글이 들어갈때? (addingPercentEncoding) (0) | 2022.03.29 |
Type Methods (static func & class func) (0) | 2022.03.23 |