- Objective-C 내부의 데이터 변화로 인해 더 빠른 성능을 누릴 수 있게 된다.
- 이런 구조를 직접 접근하는 코드를 짰다면 크래쉬가 날수도 있다.
- 스위프트와 Obj-C의 클래스는 같은 메모리 구조를 가진다.
- Dirty영역
- Metaclass: 클래스 자신에 대한 정보
- Superclass: 슈퍼클래스를 가리키는 포인터
- Flags
- Method cache
- Clean 영역(class_ro_t) -> read-only
- Flag
- Size
- Name
- Methdos
- Protocols
- Ivars
- Properties
- rw 영역 - 런타임에 클래스를 추적하기 위해 사용하는 구조
- dirty-memory를 사용하는 것은 성능에 좋지 못하다.
- clean 메모리는 없앴다가, 시스템이 필요로 하면 디스크에서 다시 가져올 수 있다.
- 하지만 dirty 메모리는 그럴수 없다. macOS는 스왑이라도 되지만, iOS는 그럴수도 없다.
- 따라서 clean 영역을 늘릴수록 성능 향상에 도움이 된다.
- rw 영역에서 잘 쓰이지 않는 영역을 별도로 분리했다!
- 대략 10% 정도만 이를 쓴다.
- 다만 Objective-C 런타임을 써서 이를 바꾸는 클래스들은 여전히 이 영역을 사용할 것이다.
- 다만, 이에 직접 접근하는 API들은 더이상 새 OS에서 동작하지 않을 것이다.
- 그러니 직접 하지말고, API써라. (ObjectiveC Runtime 문서 참조)
- Relative Method lists
- 메소드 리스트 구조 -> 총 24바이트
- 메소드 명칭(셀렉터)
- 타입 인코딩(인자와 반환 값을 인코딩해놓은 것)
- 메소드 구현체에 대한 포인터
- 이들은 로드가 끝나고 나면 클린 메모리일 것이다. 하지만 클린 메모리라도 모두 적재하는 것은 비용이다. 그래서 사이즈를 줄이고 싶다.
- 그래서 물리주소 대신 라이브러리 내의 상대주소를 32비트로 사용하기로 했다.
- 덕분에 메소드 리스트의 엔트리 크기가 12바이트로 줄었다.
- 하지만 스위즐링이 일어난다면?
- 스위즐링은 어디에 정의가 있을지 모르니, 4바이트 주소 체계로는 제대로 지목할 수 없다.
- 그래서 별도의 테이블을 두기로 한다.
- iOS 14, macOS Big Sur 이상이 최소 타겟이면 자동으로 최적화된 바이너리를 만든다
- 대신 최신 OS에서 빌드한 라이브러리가 이전 런타임에서 실행된다면(보통은 Xcode가 막지만) 이러한 구조를직접 접근해서 건드렸다면 크래시가 날 것이다. 그러니 API 써라.
- Tagged Pointer Format Changes
- Tagged Pointer가 뭔데?
- 우리는 모든 비트를 사용하지 않는다. alignmet와 메모리 크기 한계 때문에
- 그래서 align으로 인해 사용하지 않는 비트를 포인터의 식별용도로 사용한 포인터가 Tagged Pointer다.
- Tag를 넣으면 포인터만 가지고 특정 타입을 유추해낼 수 있다. (일부 타입 한정)
- 스위프트 프로그래머라면, 클래스 타입의 associatedValue를 가진 enum은 런타임에 자동으로 tagged Pointer 처럼 기능한다.(associatedValue의 남는 비트에 자신을 심는다.)
- 또한 스위프트는 값타임 중심이기 때문에 이런 게 중요하지는 않다.
- Tag 구성
- flag: 해당 포인터가 Tagged, 혹은 nil인지 알려주는 포인터
- Tag: 태그 값
- extended: 태그 값이 모자라는 경우에 사용하는 확장 비트
- Intel에서는 하위 비트를 사용하고, ARM에서는 상위 비트를 사용한다.
- 이는 판단 분기를 간결하게 만들기 위함이다. (상위 비트가 1이면 음수기 때문에)
- iOS 14 부터는 나머지는 그대로 앞에 있지만, tag 가 하위 비트로 이동한다.
- 이는 arm이 최상위 바이트를 자동으로 무시하는 기능을 가지고 있고, 아래 비트는 align으로 안쓰기 때문이다.
- 비트 검사를 직접 하고 있었다면 크래시가 나겠지만, API를 사용했다면 무사할 것이다.