├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── objc_dupclass.h └── tests ├── empty_main.c ├── foo.h ├── foo.m ├── main_with_macro.m ├── main_with_string_macro.c └── run.sh /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | name: Test 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | - name: Test 18 | run: ./tests/run.sh 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.dylib 3 | *.o 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Keith Smiley (http://keith.so) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the 'Software'), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # objc_dupclass 2 | 3 | Tired of this warning? 4 | 5 | ``` 6 | objc[65171]: Class CDPCAReporter is implemented in both A.framework and B.framework. 7 | One of the two will be used. Which one is undefined. 8 | ``` 9 | 10 | Luckily, as I discovered from [this 11 | tweet](https://twitter.com/_saagarjha/status/1509140471104241665), Apple 12 | [added](https://github.com/apple-oss-distributions/objc4/commit/62b60ba0e56440e265ca576cc9f197e9af54c1bd#diff-510a2060e3422b44e197951716a1f6bb257728a9c61efd7ae0f8c16364212f89R174) 13 | a special way to silence these warnings for classes you specify. This 14 | repo provides a C macro for adding classes to this special list. 15 | 16 | ## Usage 17 | 18 | Call the macro in any Objective-C / C / C++ file in your project, 19 | passing the class you want to silence the warning for: 20 | 21 | ```c 22 | #include "objc_dupclass.h" 23 | 24 | OBJC_DUPCLASS(AMSupportURLConnectionDelegate); 25 | ``` 26 | -------------------------------------------------------------------------------- /objc_dupclass.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // TODO: This isn't entirely accurate, but I'm not sure how to more accurately determine 4 | #if (defined(__arm64__) || defined(DUPCLASS_FORCE_DATA_CONST)) && !defined(DUPCLASS_FORCE_DATA) 5 | #define SECTION "__DATA_CONST" 6 | #else 7 | #define SECTION "__DATA" 8 | #endif 9 | 10 | // Struct layout from https://github.com/apple-oss-distributions/objc4/blob/8701d5672d3fd3cd817aeb84db1077aafe1a1604/runtime/objc-abi.h#L175-L183 11 | #define OBJC_DUPCLASS(kclass) \ 12 | __attribute__((used)) __attribute__((visibility("hidden"))) \ 13 | static struct { uint32_t version; uint32_t flags; const char name[64]; } \ 14 | const __duplicate_class_##kclass = { 0, 0, #kclass }; \ 15 | \ 16 | __attribute__((used)) __attribute__((visibility("hidden"))) \ 17 | __attribute__((section (SECTION",__objc_dupclass"))) \ 18 | const void* __set___objc_dupclass_sym___duplicate_class_##kclass = &__duplicate_class_##kclass 19 | -------------------------------------------------------------------------------- /tests/empty_main.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /tests/foo.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface Foo : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /tests/foo.m: -------------------------------------------------------------------------------- 1 | #import "foo.h" 2 | #import 3 | 4 | @implementation Foo 5 | @end 6 | -------------------------------------------------------------------------------- /tests/main_with_macro.m: -------------------------------------------------------------------------------- 1 | #include "foo.h" 2 | #include "objc_dupclass.h" 3 | 4 | OBJC_DUPCLASS(Foo); 5 | 6 | int main() { 7 | NSLog(@"%@", [Foo new]); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/main_with_string_macro.c: -------------------------------------------------------------------------------- 1 | #include "objc_dupclass.h" 2 | 3 | OBJC_DUPCLASS(Foo); 4 | 5 | int main() { 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | clang tests/foo.m -o tests/libfoo.dylib -dynamiclib -framework Foundation 6 | clang tests/foo.m -c -o tests/foo.o 7 | clang tests/empty_main.c -c -o tests/empty_main.o 8 | 9 | ld \ 10 | -arch "$(uname -m)" \ 11 | -framework Foundation \ 12 | -lfoo -Ltests \ 13 | -lSystem \ 14 | -syslibroot "$(xcrun --show-sdk-path)" \ 15 | tests/empty_main.o tests/foo.o \ 16 | -o tests/dupclass.bin 17 | 18 | output=$(./tests/dupclass.bin 2>&1) 19 | if [[ "$output" != *"Class Foo is implemented in both"* ]]; then 20 | echo "error: missing duplicate class warning: $output" >&2 21 | exit 1 22 | fi 23 | 24 | clang tests/main_with_macro.m -c -o tests/main_with_macro.o -I . 25 | ld \ 26 | -arch "$(uname -m)" \ 27 | -framework Foundation \ 28 | -lfoo -Ltests \ 29 | -lSystem \ 30 | -syslibroot "$(xcrun --show-sdk-path)" \ 31 | tests/main_with_macro.o tests/foo.o \ 32 | -o tests/nowarning.bin 33 | 34 | output=$(./tests/nowarning.bin 2>&1) 35 | if [[ "$output" == *"Class Foo is implemented in both"* ]]; then 36 | echo "error: had unexpected duplicate class warning: $output" >&2 37 | exit 1 38 | elif [[ "$output" != *"&2 40 | exit 1 41 | fi 42 | 43 | clang tests/main_with_string_macro.c -c -o tests/main_with_string_macro.o -I . 44 | ld \ 45 | -arch "$(uname -m)" \ 46 | -framework Foundation \ 47 | -lfoo -Ltests \ 48 | -lSystem \ 49 | -syslibroot "$(xcrun --show-sdk-path)" \ 50 | tests/main_with_string_macro.o tests/foo.o \ 51 | -o tests/nowarningfromstring.bin 52 | 53 | output=$(./tests/nowarningfromstring.bin 2>&1) 54 | if [[ "$output" == *"Class Foo is implemented in both"* ]]; then 55 | echo "error: had unexpected duplicate class warning: $output" >&2 56 | exit 1 57 | fi 58 | 59 | clang tests/main_with_macro.m -c -o tests/force_const.o -I . -arch x86_64 -DDUPCLASS_FORCE_DATA_CONST 60 | output=$(size -m tests/force_const.o) 61 | if [[ "$output" != *"__DATA_CONST, __objc_dupclass"* ]]; then 62 | echo "error: missing __DATA_CONST version" >&2 63 | exit 1 64 | fi 65 | 66 | clang tests/main_with_macro.m -c -o tests/force_data.o -I . -arch arm64 -DDUPCLASS_FORCE_DATA 67 | output=$(size -m tests/force_data.o) 68 | if [[ "$output" != *"__DATA, __objc_dupclass"* ]]; then 69 | echo "error: missing __DATA version" >&2 70 | exit 1 71 | fi 72 | --------------------------------------------------------------------------------