iOS 코드 다이어트 실천
본 고 는 실행 가능 한 파일 Mach-o 에서 사용 되 지 않 은 클래스,소스 링크 를 찾 는 데 정적 분석 방식 을 제공 할 것 입 니 다.xuezhulian/classunref ( 로 컬 다운로드 )。
Mach-o 파일 중DATA __objc_classrefs 세그먼트 에 인용 류 의 주 소 를 기록 하 였 습 니 다.DATA __objc_classlist 세그먼트 는 모든 종류의 주 소 를 기록 하고 차 집합 을 취하 면 사용 되 지 않 은 종류의 주 소 를 얻 을 수 있 으 며 기호 화 를 하면 인용 되 지 않 은 클래스 정 보 를 얻 을 수 있 습 니 다.
참조 클래스 주소
Mac 자체 가 가지 고 있 는 도구 인 otool 을 통 해 Mach-o 의 세그먼트 정 보 를 인쇄 할 수 있 습 니 다.주의해 야 할 것 은 시 뮬 레이 터 와 실제 컴퓨터 에 대응 하 는 실행 가능 한 파일 입 니 다.데이터 의 저장 방식 이 다 르 기 때문에 구분 해 야 합 니 다.
file 명령 을 통 해 arch 를 가 져 올 수 있 습 니 다.
#binary_file_arch: distinguish Big-Endian and Little-Endian
#file -b output example: Mach-O 64-bit executable arm64
binary_file_arch = os.popen('file -b ' + path).read().split(' ')[-1].strip()
클래스 주 소 를 찾 을 때 x86 구분64 와 arm.
def pointers_from_binary(line, binary_file_arch):
line = line[16:].strip().split(' ')
pointers = set()
if binary_file_arch == 'x86_64':
#untreated line example:00000001030cec80 d8 75 15 03 01 00 00 00 68 77 15 03 01 00 00 00
pointers.add(''.join(line[4:8][::-1] + line[0:4][::-1]))
pointers.add(''.join(line[12:16][::-1] + line[8:12][::-1]))
return pointers
#arm64 confirmed,armv7 arm7s unconfirmed
if binary_file_arch.startswith('arm'):
#untreated line example:00000001030bcd20 03138580 00000001 03138878 00000001
pointers.add(line[1] + line[0])
pointers.add(line[3] + line[2])
return pointers
return None
otool-v-s 를 통 해DATA __objc_classrefs 에서 인용 류 의 주 소 를 가 져 옵 니 다.
def class_ref_pointers(path, binary_file_arch):
ref_pointers = set()
lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classrefs %s' % path).readlines()
for line in lines:
pointers = pointers_from_binary(line, binary_file_arch)
ref_pointers = ref_pointers.union(pointers)
return ref_pointers
모든 클래스 주소otool-v-s 를 통 해DATA __objc_classlist 에서 모든 종류의 주 소 를 가 져 옵 니 다.
def class_list_pointers(path, binary_file_arch):
list_pointers = set()
lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classlist %s' % path).readlines()
for line in lines:
pointers = pointers_from_binary(line, binary_file_arch)
list_pointers = list_pointers.union(pointers)
return list_pointers
차출 집모든 종류의 정보 로 인용 류 의 정 보 를 빼 면 사용 하지 않 은 종류의 주소 정 보 를 얻 을 수 있 습 니 다.
unref_pointers = class_list_pointers(path, binary_file_arch) - class_ref_pointers(path, binary_file_arch)
기호 화nm-nm 명령 을 통 해 주소 와 대응 하 는 클래스 이름 을 얻 을 수 있 습 니 다.
def class_symbols(path):
symbols = {}
#class symbol format from nm: 0000000103113f68 (__DATA,__objc_data) external _OBJC_CLASS_$_EpisodeStatusDetailItemView
re_class_name = re.compile('(\w{16}) .* _OBJC_CLASS_\$_(.+)')
lines = os.popen('nm -nm %s' % path).readlines()
for line in lines:
result = re_class_name.findall(line)
if result:
(address, symbol) = result[0]
symbols[address] = symbol
return symbols
여과실제 분석 과정 에서 만약 에 한 유형의 하위 클래스 가 실례 화 되 고 부류 가 실례 화 되 지 않 으 면 부류 가 에 나타 나 지 않 는 다 는 것 을 발견 했다.objc_classrefs 이 단락 에 서 는 사용 되 지 않 은 클래스 에서 이 부분 부모 클래스 를 걸 러 내야 합 니 다.otool-oV 를 사용 하면 클래스 의 계승 관 계 를 얻 을 수 있 습 니 다.
def filter_super_class(unref_symbols):
re_subclass_name = re.compile("\w{16} 0x\w{9} _OBJC_CLASS_\$_(.+)")
re_superclass_name = re.compile("\s*superclass 0x\w{9} _OBJC_CLASS_\$_(.+)")
#subclass example: 0000000102bd8070 0x103113f68 _OBJC_CLASS_$_TTEpisodeStatusDetailItemView
#superclass example: superclass 0x10313bb80 _OBJC_CLASS_$_TTBaseControl
lines = os.popen("/usr/bin/otool -oV %s" % path).readlines()
subclass_name = ""
superclass_name = ""
for line in lines:
subclass_match_result = re_subclass_name.findall(line)
if subclass_match_result:
subclass_name = subclass_match_result[0]
superclass_match_result = re_superclass_name.findall(line)
if superclass_match_result:
superclass_name = superclass_match_result[0]
if len(subclass_name) > 0 and len(superclass_name) > 0:
if superclass_name in unref_symbols and subclass_name not in unref_symbols:
unref_symbols.remove(superclass_name)
superclass_name = ""
subclass_name = ""
return unref_symbols
일부 3 자 라 이브 러 리 의 오 해 를 방지 하기 위해 서 는 접두사 나 접두사 만 있 는 클래스 를 걸 러 낼 수 있 습 니 다.
for unref_pointer in unref_pointers:
if unref_pointer in symbols:
unref_symbol = symbols[unref_pointer]
if len(reserved_prefix) > 0 and not unref_symbol.startswith(reserved_prefix):
continue
if len(filter_prefix) > 0 and unref_symbol.startswith(filter_prefix):
continue
unref_symbols.add(unref_symbol)
최종 결 과 는 스 크 립 트 디 렉 터 리 에 저 장 됩 니 다.
script_path = sys.path[0].strip()
f = open(script_path+"/result.txt","w")
f.write( "unref class number: %d
" % len(unref_symbles))
f.write("
")
for unref_symble in unref_symbles:
f.write(unref_symble+"
")
f.close()
이 사 고 는 어느 정도 에 코드 의 번 거 로 움 을 줄 이 고 가방 의 부 피 를 줄 일 수 있다.정적 분석 이기 때문에 동적 호출 상황 을 포함 할 수 없고 삭제 해 야 할 클래스 에 대해 더 확인 해 야 합 니 다.총결산
이상 은 이 글 의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가 치 를 가지 기 를 바 랍 니 다.여러분 의 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.