Swift에서 메모리 효율적인 코딩 스타일을 나타내는 용어는 "Value Semantics"이라고 한다. 객체 참조를 최소화하여 메모리 사용량을 줄이는 프로그래밍 패턴이다.
Value Semantics를 따르는 타입은 값을 복사하여 새로운 인스턴스를 생성하고, 해당 인스턴스의 값이 변경되어도 다른 인스턴스에 영향을 미치지 않는다. 메모리 사용량을 줄이는 것뿐만 아니라, 예기치 않은 동작을 방지하는데도 도움된다.
Swift에서 기본 타입들(Int, Double, Bool, 등)은 모두 Value Semantics를 따르며, struct나 enum으로 선언된 사용자 정의 타입 역시 Value Semantics를 따르도록 설계할 수 있다.
Value Semantics를 따르는 코딩 스타일은 메모리 효율성뿐만 아니라 코드 안정성, 가독성, 유지보수성 등에도 긍정적인 영향을 미치므로, Swift에서 가장 권장하는 코딩 스타일이다.
기본적으로 코딩을 할 때 앱의 사용성을 개선하기 위해 메모리 효율적인 방법으로 코드를 설계해야한다. 사수님께서 항상 강조하시는 부분이 있는데, Class 보단, Struct 사용을 습관화하라고 말해주셨다.
Struct의 경우 Value Type, Class의경우 Reference Type을 따른다. 뿐만 아니라 디스패치 메서드도 다른데, Struct의 경우 Static Dispatch(정적 디스패치)를, Class의 경우 Dynamic Dispatch(동적 디스패치)를 따르므로 속도 차이가 있다.
Class의 경우 메서드 호출 시에는 인스턴스의 타입을 고려함
즉, 메서드가 호출되는 시점에 인스턴스의 실제 타입에 따라 실행될 메서드가 결정되며, 이를 동적 디스패치(dynamic dispatch)라고 함
Struct의 경우 메서드 호출 시에는 인스턴스의 실제 타입에 따라 메서드를 실행
이를 정적 디스패치(static dispatch)라고 하며, 컴파일 타임에 메서드가 어떤 타입에서 호출될지 결정되기 때문에 런타임에 타입 체크나 가상 테이블을 사용할 필요가 없어서 더 빠름
즉, Struct의 경우 컴파일러가 메서드를 실행할 타입을 미리 알고 있기 때문에 메서드 호출 시간이 빠르지만, Class의 경우 컴파일 타임에는 메서드가 어떤 타입에서 호출될 지 모르며, 런타임에서 인스턴스의 타입을 체크해야 하기 때문에 메서드 호출 시간이 더 많이 걸릴 수 있다.
일반적으로 값 타입인 구조체는 레퍼런스 타입인 클래스보다 메모리 효율성이 높다고 알려져 있다. 그러나 구조체 내부 프로퍼티에 문자열이 긴 스트링이 몇 개 들어있을 때, 메모리 효율성이 떨어지는 경우가 있다. 문자열은 기본적으로 참조 타입이므로, 구조체 프로퍼티에 문자열이 포함되면 해당 구조체 인스턴스를 복사할 때 해당 문자열도 함께 복사되므로 값이 복사되는 것보다 메모리를 더 많이 사용할 수도 있기 때문이다. 결론적으로 문자열이 긴 스트링이 몇 개 들어있는 경우, 레퍼런스 타입인 클래스보다 메모리 사용량이 많아질 수 있으므로 final class를 사용하는 것이 좋다.