swift 알고리즘 풀이로 몇 번 문자열을 다룰 때마다 substring 때문에 빨간 오류 메시지를 봐야 했다.
오늘 짚고 넘어가며, String, Substring, Character에 대해 확실히 이해하고자 한다.
Substring은 기존 String의 메모리를 공유하는 부분 문자열이다. Swift에서 문자열의 일부분을 효율적으로 다루기 위해 설계되었다.
다음과 같은 String 메서드들을 사용할 때 Substring이 반환된다.
let hello = "Hello, World!"
let prefix = hello.prefix(5) // "Hello"는 Substring 타입
let sentence = "Apple Banana Cherry"
let words = sentence.split(separator: " ") // [Substring] 타입 배열 반환
let text = "Swift Programming"
let dropped = text.dropFirst(6) // "Programming"은 Substring 타입
let str = "Hello, World!"
if let range = str.range(of: "World") {
let substring = str[range] // "World"는 Substring 타입
}
가장 큰 차이는 메모리 관리 방식이다. Substring은 원본의 메모리만 참조하므로 빠르게 생성할 수 있다.
하지만 Substring이 존재하는 한, 원본 String의 전체 메모리는 해제되지 않는다.
원본 String의 메모리를 계속 유지한다는 것은 Substring이 참조하는 메모리가 갑자기 사라지는 것을 방지한다는 의미이다.
따라서 Substring은 임시 작업에는 효율적이지만, 장기간 저장할 필요가 있는 데이터는 반드시 String으로 변환해야 한다. let newString = Stirng(substring)
그렇다면 Substring이 String을 참조하고 있을 때, String을 변경하면 어떻게 될까?
여기서 중요한 점은, Swift에서 String은 값 타입이라는 것이다. 값 타입은 변수에 할당될 때나 함수에 전달될 때 복사된다.
var original = "Hello, Swift Programming!"
let sub = original.dropFirst(7) // "Swift Programming!" - Substring
// original 값을 변경
original = "Changed string"
print(sub) // 여전히 "Swift Programming!" 출력
따라서 위 코드에서 original
변수의 값을 변경해도 sub
은 여전히 원래 참조하던 "Swift Programming!" 부분을 가리킨다.
이는 Swift가 변수 original
의 값을 변경할 때 새로운 메모리 공간을 할당하고 original
변수가 그 새 공간을 가리키게 만들기 때문이다.
원래의 메모리 공간("Hello, Swift Programming!")은 sub
이 여전히 참조하고 있으므로 해제되지 않는다.
Swift의 Substring은 C 언어의 포인터 개념을 생각하면 이해가 쉽겠다. (C언어에 대한 지식이 있다면 말이지)
다만 포인터와는 달리 원본 값이 바뀌면 바뀐 값은 새로운 메모리에 할당되고, 원본을 참조하던 Substring의 값은 바뀌지 않는다는 것이 큰 차이이겠다.