前语
由于Apple官方现已将Objective-C runtime的源码开源,所以在学习Objective-C runtime的过程中能够运用开源的代码一边调试一边学习。可是因为Apple官方没很清晰地罗列依靠以及本地装备方法,故写下本文档作为参阅。
Objc4源码Xcode调试方法
首要判别macOS的版别,若是macOS Big Sur (macOS 11)及以下,参阅 github.com/hubupc/objc… 进行装备,并在 opensource.apple.com/tarballs/ 中下载所需依靠的库。
若是macOS Monterey (macOS 12),则能够参阅接下来的装备方法。如果怕麻烦不想手动装备各种繁琐的操作,能够跳转至结尾【附录】下载现已装备好的工程直接上手调试。
objc4-838.1源码编译
以下装备操作,以Xcode 13.2.1面向Mac平台的编译操作为例。
下载objc4-838.1源码及其依靠
最新的objc4-838.1的源代码没有同步发到Apple官方的开源网站( opensource.apple.com/ )上,故需要到Apple官方的GitHub仓库( github.com/apple-oss-d… )上下载最新的源代码。
主源码库:
- objc4-838.1
依靠库:
-
xnu-8019.41.5
-
dyld-940
-
Libc-1506.40.4
-
Libc-825.40.1
-
libclosure-79
-
libplatform-273.40.1
-
libpthread-485.60.2
装备依靠库目录
-
解压objc4-838.1并作为主工程运用
-
在主工程根目录下,新建名为PrivateHeaders的文件夹
-
翻开objc工程
-
首要,点击左边项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【search paths】。最终,在Search Paths栏目下,双击翻开Header Search Paths,新增【$(SRCROOT)/PrivateHeaders】。
装备依靠
首要先将Scheme挑选objc,设备挑选My Mac。
然后运用快捷键command + B,即可进行编译。不出意料的话,编译器会报错。
虽然直接进行编译会报错,可是咱们接下来的装备操作都基于Error的信息来操作。首要,点击左边项目导航并排的感叹号图标。然后,点击Buildtime。最终,在下方的Filter栏中点击最右侧的叉号图标,即可筛选出Error信息。
接下来的装备操作,依靠于编译中发生的Error的信息。实际操作过程中,报错顺序可能跟下文有些差异,主张通过全局检索关键字的方式来查找处理Error对应的办法。以下是可能会发生Error信息以及对应的处理办法:
- unable to find sdk ‘macosx.internal’ (target: objc)
点击左边项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【Architectures】。最终,在Architectures栏目下,将Base SDK由macosx.internal改为macOS。
- unable to find sdk ‘macosx.internal’ (target: objc-trampolines)
跟(1)的操作类似,仅仅Step 2中TARGETS挑选objc-trampolines
- ‘os/feature_private.h’ file not found (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 36、Line 444-446。
注释NSObject.mm Line 43。
- ‘sys/reason.h’ file not found (target: objc ; file: objc-os.h)
将xnu-8019.41.5根目录下的bsd/sys/reason.h文件拷贝到主工程依靠库文件夹PrivateHeaders的sys/(如没有对应的文件夹自行新建)。
- ‘mach-o/dyld_priv.h’ file not found (target: objc ; file: objc-os.h)
将dyld-940根目录下的include/mach-o/dyld_priv.h文件拷贝到主工程依靠库文件夹PrivateHeaders的mach-o/(如没有对应的文件夹自行新建)。
- Expected ‘,’ (target: objc ; file: dyld_priv.h)
在dyld_priv.h中检索一切的bridgeos(3.0),并删去。
- ‘os/lock_private.h’ file not found (target: objc ; file: objc-os.h)
将libplatform-273.40.1根目录下的private/os/lock_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘os/base_private.h’ file not found (target: objc ; file: lock_private.h)
将xnu-8019.41.5根目录下的libkern/os/base_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘pthread/tsd_private.h’ file not found (target: objc ; file: lock_private.h)
将libpthread-485.60.2根目录下的private/pthread/tsd_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。
- ‘System/machine/cpu_capabilities.h’ file not found (target: objc ; file: tsd_private.h)
将xnu-8019.41.5根目录下的osfmk/machine/cpu_capabilities.h文件拷贝到主工程依靠库文件夹PrivateHeaders的System/machine/(如没有对应的文件夹自行新建)。
- Expected ‘,’ (target: objc ; file: lock_private.h)
在lock_private.h中检索一切的bridgeos(4.0),并删去。
- ‘os/tsd.h’ file not found (target: objc ; file: tsd_private.h)
将xnu-8019.41.5根目录下的libsyscall/os/tsd.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘pthread/spinlock_private.h’ file not found (target: objc ; file: tsd_private.h)
将libpthread-485.60.2根目录下的private/pthread/spinlock_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。
- ‘System/pthread_machdep.h’ file not found (target: objc ; file: objc-os.h)
将Libc-825.40.1根目录下的pthreads/pthread_machdep.h文件拷贝到主工程依靠库文件夹PrivateHeaders的System/(如没有对应的文件夹自行新建)。
- ‘CrashReporterClient.h’ file not found (target: objc ; file: objc-os.h)
将Libc-825.40.1根目录下的include/CrashReporterClient.h文件拷贝到主工程依靠库文件夹PrivateHeaders。
接着,点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【Preprocessor Macros】。最终,在Apple Clang – Preprocessing栏目下,双击翻开Preprocessor Macros,新增【LIBC_NO_LIBCRASHREPORTERCLIENT】。
- ‘objc-shared-cache.h’ file not found (target: objc ; file: objc-opt.h)
将dyld-940根目录下的include/objc-shared-cache.h文件拷贝到主工程依靠库文件夹PrivateHeaders。
- Typedef redefinition with different types (‘int’ vs ‘volatile OSSpinLock’ (aka ‘volatile int’)) Static & declaration of ‘_pthread_has_direct_tsd’ follows non-static declaration & Static declaration of ‘_pthread_getspecific_direct’ follows non-static declaration & Static declaration of ‘_pthread_setspecific_direct’ follows non-static declaration (target: objc ; file: pthread_machdep.h)
注释pthread_machdep.h Line 214-299。
- Use of undeclared identifier ‘dyld_platform_version_macOS_10_13’ (target: objc ; file: objc-os.mm)
注释objc-os.mm Line 568-575。
- ‘_simple.h’ file not found (target: objc ; file: objc-errors.mm)
将libplatform-273.40.1根目录下的private/_simple.h文件拷贝到主工程依靠库文件夹PrivateHeaders。
- ‘Cambria/Traps.h’ file not found (target: objc ; file: objc-cache.mm)
注释objc-cache.mm Line 87-88。
- ‘os/linker_set.h’ file not found (target: objc ; file: objc-class.mm)
将Libc-1506.40.4根目录下的os/linker_set.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘kern/restartable.h’ file not found (target: objc ; file: objc-cache.mm)
将xnu-8019.41.5根目录下的osfmk/kern/restartable.h文件拷贝到主工程依靠库文件夹PrivateHeaders的kern/(如没有对应的文件夹自行新建)。
- ‘Block_private.h’ file not found (target: objc ; file: objc-block-trampolines.mm)
将libclosure-79根目录下的Block_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders。
- Use of undeclared identifier ‘oah_is_current_process_translated’ (target: objc ; file: objc-cache.mm)
注释objc-cache.mm Line 1123-1130。
- Use of undeclared identifier ‘dyld_fall_2020_os_versions’ (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 379-380。
- ‘os/reason_private.h’ file not found (target: objc ; file: NSObject.mm)
将xnu-8019.41.5根目录下的libkern/os/reason_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘os/variant_private.h’ file not found (target: objc ; file: NSObject.mm)
将Libc-1506.40.4根目录下的os/variant_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- Use of undeclared identifier ‘dyld_platform_version_bridgeOS_2_0’ & Use of undeclared identifier ‘dyld_platform_version_iOS_10_0’ & Use of undeclared identifier ‘dyld_platform_version_macOS_10_12’ & Use of undeclared identifier ‘dyld_platform_version_tvOS_10_0’ & Use of undeclared identifier ‘dyld_platform_version_watchOS_3_0’ (target: objc ; file: NSObject.mm)
注释NSObject.mm Line 1147-1151。
- Expected ‘,’ (target: objc ; file: variant_private.h)
在variant_private.h中检索一切的bridgeos和bridgeos(4.0),并删去。
- Mismatch in debug-ness macros (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 128。
- Use of undeclared identifier ‘dyld_platform_version_macOS_10_11’ (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 3528-3534。
- ‘_static_assert’ declared as an array with a negative size (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 176-177。
- Use of undeclared identifier ‘dyld_fall_2018_os_versions’ (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 8381-8403。
- Library not found for -lCrashReporterClient (target: objc)
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【other linker】。最终,在Other Linker Flags栏目下,双击翻开Any macOS SDK,删去【-lCrashReporterClient】。
(同样的操作删去Release下Any macOS SDK中的【-lCrashReporterClient】)
- Library not found for -loah (target: objc)
与(34)相同的操作删去【-loah】。
- SDK “macosx.internal” cannot be located (target: objc)
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Phases。最终,打开Run Script (markgc)栏目,将脚本中的【macosx.internal】改为【macosx】。
附上完好脚本内容:
set -x
/usr/bin/xcrun -sdk macosx clang++ -Wall -mmacosx-version-min=10.12 -arch x86_64 -std=c++11 "${SRCROOT}/markgc.cpp" -o "${BUILT_PRODUCTS_DIR}/markgc"
"${BUILT_PRODUCTS_DIR}/markgc" "${BUILT_PRODUCTS_DIR}/libobjc.A.dylib"
源码编译成功
当编译后出现以下图标,代表编译成功:
至此,关于objc4-838.1源码前期装备以及编译的过程就完毕了。
源码调试工程
通过上述的操作,咱们成功编译了objc4-838.1。可是,直接学习objc4的源码难度还是太高了。此时,咱们通过创立调试工程来达到学习源码的意图。
创立源码调试工程
首要咱们需要创立调试工程:
-
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目底部点击加号。然后,在弹出的项目创立框中,选中macOS,在查找栏中输入【Command Line Tool】。接着,挑选Command Line Tool,点击右下角的Next。
-
在跳转的项目信息框中,填入Product Name(本文比如填写的是【objc_test】,能够自行命名)。Language挑选Objective-C。点击Finish。
-
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击General。接着,在Frameworks and Libraries栏目下点击加号。
-
挑选libobjc.A.dylib,点击Add。
-
在Frameworks and Libraries栏目下,选中libobjc.A.dylib,点击Embed,挑选Do Not Embed。
-
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Phases。接着,打开Dependencies栏目,点击下方的加号。最终选中objc,点击Add。
-
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【Signing】。最终,点击Enable Hardened Runtime,挑选No。
-
点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【objc_msgSend】。最终,点击Enable Strict Checking of objc_msgSend Calls,挑选No。
运转源码调试工程
打开左边项目导航窗口中的工程文件objc_test,点击main.m。接着,将Scheme挑选objc_test,设备挑选My Mac。快捷键Command+R即可编译且运转工程。
至此,源码调试工程创立并运转成功。咱们能够在objc_test工程中编写代码调用objc runtime相关的的API,并且能够一起在objc工程中添加断点来调试。
过错排查
- objc工程无法触发断点
Scheme挑选objc,快捷键command + B保证objc工程编译成功。若仍未能成功,参阅【创立源码调试工程】和【运转源码调试工程】的操作,保证工程装备文件的设置契合预期。
附录
macOS Monterey (macOS 12)能够直接在 code.byted.org/caiyixian/o… 下载现已彻底装备好的工程。
参阅链接
- github.com/hubupc/objc…
- github.com/hubupc/objc…