前语

由于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

装备依靠库目录
  1. 解压objc4-838.1并作为主工程运用

  2. 在主工程根目录下,新建名为PrivateHeaders的文件夹

Objective-C runtime 源码调试

  1. 翻开objc工程

Objective-C runtime 源码调试

  1. 首要,点击左边项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【search paths】。最终,在Search Paths栏目下,双击翻开Header Search Paths,新增【$(SRCROOT)/PrivateHeaders】。

Objective-C runtime 源码调试

装备依靠

首要先将Scheme挑选objc,设备挑选My Mac。

Objective-C runtime 源码调试

Objective-C runtime 源码调试

然后运用快捷键command + B,即可进行编译。不出意料的话,编译器会报错。

Objective-C runtime 源码调试

虽然直接进行编译会报错,可是咱们接下来的装备操作都基于Error的信息来操作。首要,点击左边项目导航并排的感叹号图标。然后,点击Buildtime。最终,在下方的Filter栏中点击最右侧的叉号图标,即可筛选出Error信息。

Objective-C runtime 源码调试

接下来的装备操作,依靠于编译中发生的Error的信息。实际操作过程中,报错顺序可能跟下文有些差异,主张通过全局检索关键字的方式来查找处理Error对应的办法。以下是可能会发生Error信息以及对应的处理办法:

  1. unable to find sdk ‘macosx.internal’ (target: objc)

点击左边项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【Architectures】。最终,在Architectures栏目下,将Base SDK由macosx.internal改为macOS。

Objective-C runtime 源码调试

  1. unable to find sdk ‘macosx.internal’ (target: objc-trampolines)

跟(1)的操作类似,仅仅Step 2中TARGETS挑选objc-trampolines

  1. ‘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。

  1. ‘sys/reason.h’ file not found (target: objc ; file: objc-os.h)

将xnu-8019.41.5根目录下的bsd/sys/reason.h文件拷贝到主工程依靠库文件夹PrivateHeaders的sys/(如没有对应的文件夹自行新建)。

  1. ‘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/(如没有对应的文件夹自行新建)。

  1. Expected ‘,’ (target: objc ; file: dyld_priv.h)

在dyld_priv.h中检索一切的bridgeos(3.0),并删去。

  1. ‘os/lock_private.h’ file not found (target: objc ; file: objc-os.h)

将libplatform-273.40.1根目录下的private/os/lock_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. ‘os/base_private.h’ file not found (target: objc ; file: lock_private.h)

将xnu-8019.41.5根目录下的libkern/os/base_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. ‘pthread/tsd_private.h’ file not found (target: objc ; file: lock_private.h)

将libpthread-485.60.2根目录下的private/pthread/tsd_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。

  1. ‘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/(如没有对应的文件夹自行新建)。

  1. Expected ‘,’ (target: objc ; file: lock_private.h)

在lock_private.h中检索一切的bridgeos(4.0),并删去。

  1. ‘os/tsd.h’ file not found (target: objc ; file: tsd_private.h)

将xnu-8019.41.5根目录下的libsyscall/os/tsd.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. ‘pthread/spinlock_private.h’ file not found (target: objc ; file: tsd_private.h)

将libpthread-485.60.2根目录下的private/pthread/spinlock_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。

  1. ‘System/pthread_machdep.h’ file not found (target: objc ; file: objc-os.h)

将Libc-825.40.1根目录下的pthreads/pthread_machdep.h文件拷贝到主工程依靠库文件夹PrivateHeaders的System/(如没有对应的文件夹自行新建)。

  1. ‘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】。

Objective-C runtime 源码调试

  1. ‘objc-shared-cache.h’ file not found (target: objc ; file: objc-opt.h)

将dyld-940根目录下的include/objc-shared-cache.h文件拷贝到主工程依靠库文件夹PrivateHeaders。

  1. 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。

  1. Use of undeclared identifier ‘dyld_platform_version_macOS_10_13’ (target: objc ; file: objc-os.mm)

注释objc-os.mm Line 568-575。

  1. ‘_simple.h’ file not found (target: objc ; file: objc-errors.mm)

将libplatform-273.40.1根目录下的private/_simple.h文件拷贝到主工程依靠库文件夹PrivateHeaders。

  1. ‘Cambria/Traps.h’ file not found (target: objc ; file: objc-cache.mm)

注释objc-cache.mm Line 87-88。

  1. ‘os/linker_set.h’ file not found (target: objc ; file: objc-class.mm)

将Libc-1506.40.4根目录下的os/linker_set.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. ‘kern/restartable.h’ file not found (target: objc ; file: objc-cache.mm)

将xnu-8019.41.5根目录下的osfmk/kern/restartable.h文件拷贝到主工程依靠库文件夹PrivateHeaders的kern/(如没有对应的文件夹自行新建)。

  1. ‘Block_private.h’ file not found (target: objc ; file: objc-block-trampolines.mm)

将libclosure-79根目录下的Block_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders。

  1. Use of undeclared identifier ‘oah_is_current_process_translated’ (target: objc ; file: objc-cache.mm)

注释objc-cache.mm Line 1123-1130。

  1. Use of undeclared identifier ‘dyld_fall_2020_os_versions’ (target: objc ; file: objc-runtime.mm)

注释objc-runtime.mm Line 379-380。

  1. ‘os/reason_private.h’ file not found (target: objc ; file: NSObject.mm)

将xnu-8019.41.5根目录下的libkern/os/reason_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. ‘os/variant_private.h’ file not found (target: objc ; file: NSObject.mm)

将Libc-1506.40.4根目录下的os/variant_private.h文件拷贝到主工程依靠库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。

  1. 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。

  1. Expected ‘,’ (target: objc ; file: variant_private.h)

在variant_private.h中检索一切的bridgeos和bridgeos(4.0),并删去。

  1. Mismatch in debug-ness macros (target: objc ; file: objc-runtime.mm)

注释objc-runtime.mm Line 128。

  1. Use of undeclared identifier ‘dyld_platform_version_macOS_10_11’ (target: objc ; file: objc-runtime-new.mm)

注释objc-runtime-new.mm Line 3528-3534。

  1. ‘_static_assert’ declared as an array with a negative size (target: objc ; file: objc-runtime-new.mm)

注释objc-runtime-new.mm Line 176-177。

  1. Use of undeclared identifier ‘dyld_fall_2018_os_versions’ (target: objc ; file: objc-runtime-new.mm)

注释objc-runtime-new.mm Line 8381-8403。

  1. Library not found for -lCrashReporterClient (target: objc)

点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【other linker】。最终,在Other Linker Flags栏目下,双击翻开Any macOS SDK,删去【-lCrashReporterClient】。

Objective-C runtime 源码调试

(同样的操作删去Release下Any macOS SDK中的【-lCrashReporterClient】)

  1. Library not found for -loah (target: objc)

与(34)相同的操作删去【-loah】。

  1. SDK “macosx.internal” cannot be located (target: objc)

点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Phases。最终,打开Run Script (markgc)栏目,将脚本中的【macosx.internal】改为【macosx】。

Objective-C runtime 源码调试

附上完好脚本内容:

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"
源码编译成功

当编译后出现以下图标,代表编译成功:

Objective-C runtime 源码调试

至此,关于objc4-838.1源码前期装备以及编译的过程就完毕了。

源码调试工程

通过上述的操作,咱们成功编译了objc4-838.1。可是,直接学习objc4的源码难度还是太高了。此时,咱们通过创立调试工程来达到学习源码的意图。

创立源码调试工程

首要咱们需要创立调试工程:

  1. 点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目底部点击加号。然后,在弹出的项目创立框中,选中macOS,在查找栏中输入【Command Line Tool】。接着,挑选Command Line Tool,点击右下角的Next。

Objective-C runtime 源码调试

  1. 在跳转的项目信息框中,填入Product Name(本文比如填写的是【objc_test】,能够自行命名)。Language挑选Objective-C。点击Finish。

Objective-C runtime 源码调试

  1. 点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击General。接着,在Frameworks and Libraries栏目下点击加号。

Objective-C runtime 源码调试

  1. 挑选libobjc.A.dylib,点击Add。

Objective-C runtime 源码调试

  1. 在Frameworks and Libraries栏目下,选中libobjc.A.dylib,点击Embed,挑选Do Not Embed。

Objective-C runtime 源码调试

  1. 点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Phases。接着,打开Dependencies栏目,点击下方的加号。最终选中objc,点击Add。

Objective-C runtime 源码调试

  1. 点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【Signing】。最终,点击Enable Hardened Runtime,挑选No。

Objective-C runtime 源码调试

  1. 点击左边项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并挑选All和Combined,在右上侧查找栏中输入【objc_msgSend】。最终,点击Enable Strict Checking of objc_msgSend Calls,挑选No。

Objective-C runtime 源码调试

运转源码调试工程

打开左边项目导航窗口中的工程文件objc_test,点击main.m。接着,将Scheme挑选objc_test,设备挑选My Mac。快捷键Command+R即可编译且运转工程。

Objective-C runtime 源码调试

至此,源码调试工程创立并运转成功。咱们能够在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…