🔥 부분문자열

333자
5분

Swift에서 문자열(String)의 일부분을 가져오는 경우, 예를 들어 서브스크립트나 prefix(_:) 같은 메서드를 사용할 때, 그 결과는 또 다른 문자열이 아닌 Substring의 인스턴스라는 점을 알아두시면 좋겠어요. Substring은 String과 대부분의 메서드를 공유하기 때문에, 문자열을 다루는 것과 동일한 방식으로 substring을 다룰 수 있답니다. 그러나 문자열과는 달리, substring은 문자열에 대한 작업을 수행하는 동안 짧은 시간 동안만 사용된다는 것을 기억해 주세요. 결과를 더 오랫동안 저장하려면 substring을 String의 인스턴스로 변환해야 합니다. 아래 예시를 살펴볼까요?

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning은 "Hello"입니다.
 
// 장기 저장을 위해 결과를 String으로 변환합니다.
let newString = String(beginning)
swift

위 코드에서는 다음과 같은 일이 일어나요:

  1. greeting이라는 문자열을 정의합니다. 내용은 "Hello, world!"이에요.
  2. index라는 상수에 greeting 문자열에서 ","의 인덱스를 찾아 저장합니다. 만약 ","를 찾지 못하면 greeting의 끝 인덱스를 사용하도록 ?? 연산자로 지정했어요.
  3. beginning이라는 상수에 greeting의 처음부터 index까지의 substring을 저장합니다. 이때 substring은 "Hello"가 됩니다.
  4. 마지막으로 beginning substring을 String으로 변환하여 newString이라는 새로운 문자열 상수에 저장합니다. 이제 newString은 substring과는 별개의 문자열이 되었어요.

문자열과 마찬가지로 각 substring에는 substring을 구성하는 문자가 저장되는 메모리 영역이 있어요. 문자열과 substring의 차이점은 성능 최적화를 위해 substring이 원래 문자열의 메모리 일부 또는 다른 substring의 메모리 일부를 재사용할 수 있다는 점이에요. (문자열도 비슷한 최적화가 있지만, 두 문자열이 메모리를 공유하는 경우 두 문자열은 동일합니다.) 이러한 성능 최적화는 문자열이나 substring을 수정할 때까지 메모리 복사에 드는 성능 비용을 치르지 않아도 된다는 것을 의미해요. 앞서 언급했듯이 substring은 장기 저장에 적합하지 않습니다. 원래 문자열의 저장소를 재사용하기 때문에 substring이 사용되는 한 전체 원래 문자열을 메모리에 보관해야 하기 때문이죠.

위 예제에서 greeting은 문자열이므로 문자열을 구성하는 문자가 저장되는 메모리 영역을 가지고 있어요. beginninggreeting의 substring이기 때문에 greeting이 사용하는 메모리를 재사용합니다. 반대로 newString은 문자열이에요. substring에서 생성될 때 자체 저장소를 가집니다. 아래 그림은 이러한 관계를 보여줍니다:

lecture image

위 그림에서 보듯이, beginninggreeting과 메모리를 공유하고 있어요. 반면 newString은 새로운 메모리 공간을 가지고 있죠. 따라서 substring을 오래 사용해야 한다면 반드시 새로운 문자열로 변환해주는 게 좋답니다. 그래야 원본 문자열의 메모리를 해제할 수 있으니까요!

이렇게 substring은 원본 문자열의 일부를 효율적으로 가져올 수 있게 해주는 강력한 기능이에요. 하지만 메모리 관리에 주의해야 한다는 점, 잊지 마세요!