├── .codecov.yml ├── .github └── workflows │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── Doc ├── 0.0.x │ ├── usage_en.md │ └── usage_zh_CN.md ├── 0.2.x │ ├── usage_en.md │ └── usage_zh_CN.md └── 1.0.x │ ├── usage_en.md │ └── usage_zh_CN.md ├── Example ├── NNPopObjcDynamicExample │ ├── NNPopObjcDynamicExample.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── NNPopObjcDynamicExample │ │ ├── Info.plist │ │ ├── NNDynamic.h │ │ ├── NNDynamic.m │ │ ├── NNDynamicProtocol.h │ │ ├── NNDynamicProtocol.m │ │ └── NNPopObjcDynamicExample.h │ └── NNPopObjcDynamicExampleTests │ │ ├── Info.plist │ │ └── NNPopObjcDynamicExampleTests.m ├── NNPopObjcExample │ ├── NNPopObjcExample.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── NNPopObjcExample.xcscheme │ ├── NNPopObjcExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Classes │ │ │ ├── NNCode.h │ │ │ ├── NNCode.m │ │ │ ├── NNCodeC.h │ │ │ ├── NNCodeC.m │ │ │ ├── NNCodeCpp.h │ │ │ ├── NNCodeCpp.m │ │ │ ├── NNCodeLog.h │ │ │ ├── NNCodeObjc.h │ │ │ ├── NNCodeObjc.m │ │ │ ├── NNCodeProtocol.h │ │ │ ├── NNCodeProtocol.m │ │ │ └── NNCodeSwift.swift │ │ ├── Supporting Files │ │ │ ├── Info.plist │ │ │ ├── NNPopObjcExample-Bridging-Header.h │ │ │ ├── PrefixHeader.pch │ │ │ └── main.m │ │ ├── ViewController.h │ │ └── ViewController.m │ ├── NNPopObjcExampleTests │ │ ├── Info.plist │ │ └── NNPopObjcExampleTests.m │ └── NNPopObjcExampleUITests │ │ ├── Info.plist │ │ └── NNPopObjcExampleUITests.m └── NNPopObjcStaticExample │ ├── NNPopObjcStaticExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── NNPopObjcStaticExample │ ├── Info.plist │ ├── NNPopObjcStaticExample.h │ ├── NNStatic.h │ ├── NNStatic.m │ ├── NNStaticProtocol.h │ └── NNStaticProtocol.m │ └── NNPopObjcStaticExampleTests │ ├── Info.plist │ └── NNPopObjcStaticExampleTests.m ├── Gemfile ├── LICENSE ├── NNPopObjc.podspec ├── NNPopObjc.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── NNPopObjc iOS.xcscheme │ ├── NNPopObjc macOS.xcscheme │ ├── NNPopObjc tvOS.xcscheme │ └── NNPopObjc watchOS.xcscheme ├── NNPopObjc.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── NNPopObjc ├── Info.plist ├── NNPopObjc.h ├── Public │ ├── NNPopObjcDefines.h │ ├── NNPopObjcDescription.h │ ├── NNPopObjcExtension.h │ ├── NNPopObjcMacros.h │ ├── NNPopObjcScope.h │ └── NNPopObjcWhere.h ├── Source │ ├── NNPopObjcInjection.h │ ├── NNPopObjcInjection.mm │ ├── NNPopObjcLogging.h │ ├── NNPopObjcLogging.mm │ ├── NNPopObjcMemory.h │ ├── NNPopObjcMemory.mm │ ├── NNPopObjcProtocol.h │ ├── NNPopObjcProtocol.mm │ ├── NNPopObjcTickTock.h │ └── NNPopObjcTickTock.mm └── extobjc │ └── NNPopObjcMetaMacros.h ├── NNPopObjcTests ├── Info.plist ├── NNPopObjcTests.mm ├── NNTestClasses │ ├── NNTestClassCase0.h │ ├── NNTestClassCase0.mm │ ├── NNTestClassCase1.h │ ├── NNTestClassCase1.mm │ ├── NNTestClassCase2.h │ ├── NNTestClassCase2.mm │ ├── NNTestClassCase3.h │ ├── NNTestClassCase3.mm │ ├── NNTestClassCase4+NNTestProtocol.mm │ ├── NNTestClassCase4.h │ ├── NNTestClassCase4.mm │ ├── NNTestClassCase5+NNTestProtocol.mm │ ├── NNTestClassCase5.h │ ├── NNTestClassCase5.mm │ ├── NNTestClassCase6+NNTestProtocol.mm │ ├── NNTestClassCase6.h │ └── NNTestClassCase6.mm ├── NNTestProtocol │ ├── NNTestProtocol.h │ └── NNTestProtocol.mm └── NNTestTrack │ ├── NNTestFunctionParse.h │ ├── NNTestFunctionParse.mm │ ├── NNTestTrack.h │ ├── NNTestTrack.mm │ ├── NSString+NNTestTrack.h │ └── NSString+NNTestTrack.mm ├── README.md └── README_zh_CN.md /.codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - NNPopObjcTests 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: 10 | - master 11 | - develop 12 | - develop-** 13 | pull_request: 14 | branches: 15 | - master 16 | - develop 17 | - develop-** 18 | 19 | env: 20 | LANG: en_US.UTF-8 21 | 22 | POP_OBJC_WORKSPACE: "NNPopObjc.xcworkspace" 23 | POP_OBJC_PROJECT: "NNPopObjc.xcodeproj" 24 | 25 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 26 | jobs: 27 | Cocoapods_Lint: 28 | runs-on: macos-10.15 29 | steps: 30 | - uses: actions/checkout@v2 31 | with: 32 | ref: ${{ github.sha }} 33 | - name: cocoapods_lint 34 | run: pod lib lint --allow-warnings --verbose 35 | 36 | Carthage_Lint: 37 | runs-on: macos-10.15 38 | steps: 39 | - uses: actions/checkout@v2 40 | with: 41 | ref: ${{ github.sha }} 42 | - name: carthage_lint 43 | run: carthage build --no-skip-current 44 | 45 | Build_Examples: 46 | runs-on: macos-10.15 47 | strategy: 48 | matrix: 49 | sdk: [ iOS13.3, iOS13.4 ] 50 | include: 51 | - sdk: iOS13.3 52 | developer_dir: /Applications/Xcode_11.3.1.app 53 | destination: OS=13.3,name=iPhone 11 Pro Max 54 | scheme: NNPopObjcExample 55 | - sdk: iOS13.4 56 | developer_dir: /Applications/Xcode_11.4.app 57 | destination: OS=13.4,name=iPhone 11 Pro Max 58 | scheme: NNPopObjcExample 59 | steps: 60 | - uses: actions/checkout@v2 61 | with: 62 | ref: ${{ github.sha }} 63 | - name: bundle_install 64 | run: bundle install 65 | - name: build_examples 66 | env: 67 | DEVELOPER_DIR: ${{ matrix.developer_dir }} 68 | run: | 69 | echo "" 70 | set -o pipefail 71 | xcodebuild -version -sdk 72 | xcodebuild build -workspace "${{ env.POP_OBJC_WORKSPACE }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO | bundle exec xcpretty -c; 73 | 74 | Unit_Tests: 75 | runs-on: macos-10.15 76 | strategy: 77 | matrix: 78 | sdk: [ macOS, iOS13.3, tvOS13.3, iOS13.4, tvOS13.4 ] 79 | include: 80 | - sdk: macOS 81 | developer_dir: /Applications/Xcode_11.3.1.app 82 | destination: arch=x86_64 83 | scheme: NNPopObjc macOS 84 | - sdk: iOS13.3 85 | developer_dir: /Applications/Xcode_11.3.1.app 86 | destination: OS=13.3,name=iPhone 11 Pro Max 87 | scheme: NNPopObjc iOS 88 | - sdk: tvOS13.3 89 | developer_dir: /Applications/Xcode_11.3.1.app 90 | destination: OS=13.3,name=Apple TV 4K 91 | scheme: NNPopObjc tvOS 92 | - sdk: iOS13.4 93 | developer_dir: /Applications/Xcode_11.4.app 94 | destination: OS=13.4,name=iPhone 11 Pro Max 95 | scheme: NNPopObjc iOS 96 | - sdk: tvOS13.4 97 | developer_dir: /Applications/Xcode_11.4.app 98 | destination: OS=13.4,name=Apple TV 4K 99 | scheme: NNPopObjc tvOS 100 | steps: 101 | - uses: actions/checkout@v2 102 | with: 103 | ref: ${{ github.sha }} 104 | - name: bundle_install 105 | run: bundle install 106 | - name: unit_tests 107 | env: 108 | DEVELOPER_DIR: ${{ matrix.developer_dir }} 109 | run: | 110 | set -o pipefail 111 | xcodebuild build build-for-testing -project "${{ env.POP_OBJC_PROJECT }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES ONLY_ACTIVE_ARCH=YES | bundle exec xcpretty -c; 112 | xcodebuild analyze test-without-building -project "${{ env.POP_OBJC_PROJECT }}" -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" -configuration Debug ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES ONLY_ACTIVE_ARCH=YES | bundle exec xcpretty -c; 113 | - uses: codecov/codecov-action@v1 114 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: DEPLOY 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | Deploy_To_Cocoapods: 10 | runs-on: macos-10.15 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: deploy_to_cocoapods 14 | env: 15 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 16 | run: | 17 | set -eo pipefail 18 | export BUMP_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) 19 | pod lib lint --allow-warnings --verbose 20 | pod trunk push --allow-warnings --verbose 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | # CocoaPods 34 | # 35 | # We recommend against adding the Pods directory to your .gitignore. However 36 | # you should judge for yourself, the pros and cons are mentioned at: 37 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 38 | # 39 | # Pods/ 40 | # 41 | # Add this line if you want to avoid checking in source code from the Xcode workspace 42 | # *.xcworkspace 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build/ 50 | 51 | # fastlane 52 | # 53 | # It is recommended to not store the screenshots in the git repo. 54 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 55 | # For more information about the recommended setup visit: 56 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 57 | 58 | fastlane/report.xml 59 | fastlane/Preview.html 60 | fastlane/screenshots/**/*.png 61 | fastlane/test_output 62 | 63 | # Code Injection 64 | # 65 | # After new code Injection tools there's a generated folder /iOSInjectionProject 66 | # https://github.com/johnno1962/injectionforxcode 67 | 68 | iOSInjectionProject/ 69 | 70 | .idea/ 71 | 72 | *.coverage.txt 73 | 74 | Gemfile.lock 75 | -------------------------------------------------------------------------------- /Doc/0.0.x/usage_en.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc usage 2 | 3 | This document is a guide for NNPopObjc (version 0.0.x) . 4 | 5 | ## @nn_extension 6 | 7 | In NNPopObjc, `nn_extension` has two parameters. The frist parameter is protocol to be extended, the second parameter is the class that the `extension` inherits. 8 | The key word `nn_extension` is just a macro, using the `nn_extension` macro, it will declare and implement a class as the procotol `extension`, so you can only use it in `.m` file, it is defined as follow: 9 | 10 | ``` 11 | #define nn_extension(protocol, clazz) \ 12 | \ 13 | interface __NNPopObjc##_##protocol##_##clazz : clazz \ 14 | \ 15 | @end \ 16 | \ 17 | @implementation __NNPopObjc##_##protocol##_##clazz \ 18 | \ 19 | ``` 20 | 21 | This is an example of using `nn_extension` to extend a procotol: 22 | 23 | Declare a protocol named "NNDemoProcotol": 24 | 25 | ``` 26 | @protocol NNDemoProtocol 27 | 28 | @optional 29 | - (void)sayHelloPop; 30 | 31 | @end 32 | ``` 33 | 34 | Use the `nn_extension` to extend procotol: 35 | 36 | ``` 37 | @nn_extension(NNDemoProtocol, NSObject) 38 | 39 | - (void)sayHelloPop { 40 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 41 | } 42 | 43 | @end 44 | ``` 45 | 46 | The following is the content after the macro is expanded: 47 | 48 | ``` 49 | @interface __NNPopObjc_NNDemoProtocol_NSObject : NSObject 50 | 51 | @end 52 | 53 | @implementation __NNPopObjc_NNDemoProtocol_NSObject 54 | 55 | - (void)sayHelloPop { 56 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 57 | } 58 | 59 | @end 60 | ``` 61 | 62 | ## Protocol Extensions 63 | 64 | ### Providing Default Implementations 65 | 66 | When the second parameter of `nn_extension` is `NSObject`, it is the default extension of the current protocol. All classes that adapting the protocol will use it as the default procotol extention implementation, for example: 67 | 68 | ``` 69 | @nn_extension(NNDemoProtocol, NSObject) 70 | 71 | - (void)sayHelloPop { 72 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 73 | } 74 | 75 | @end 76 | ``` 77 | 78 | All conforming classes automatically gain this method implementation without any additional modification. 79 | 80 | ``` 81 | @interface NNDemoClassA: NSObject @end 82 | @interface NNDemoClassAA: NNDemoClassA @end 83 | @interface NNDemoClassB: NSObject @end 84 | 85 | [[NNDemoClassA new] sayHelloPop]; 86 | [[NNDemoClassAA new] sayHelloPop]; 87 | [[NNDemoClassB new] sayHelloPop]; 88 | 89 | //Prints "-[NNDemoClassA sayHelloPop] say hello pop" 90 | //Prints "-[NNDemoClassAA sayHelloPop] say hello pop" 91 | //Prints "-[NNDemoClassB sayHelloPop] say hello pop" 92 | ``` 93 | 94 | ### Adding Class Constraints to Protocol Extensions 95 | 96 | Write the class constraints in the second parameter of `nn_extension`. For example, you can define an extension to the NNDemoProtocol protocol that applies to any class whose inherited from from NNDemoClassAA. 97 | 98 | ``` 99 | @nn_extension(NNDemoProtocol, NSObject) 100 | 101 | - (void)sayHelloPop { 102 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 103 | } 104 | 105 | @end 106 | 107 | @nn_extension(NNDemoProtocol, NNDemoClassAA) 108 | 109 | - (void)sayHelloPop { 110 | DLog(@"-[%@ %s] ClassAA say hello pop", [self class], sel_getName(_cmd)); 111 | } 112 | 113 | @end 114 | ``` 115 | 116 | Consider the NNDemoClassAA class constraint to NNDemoProtocol procotol extentsion, and the inheritance relationship between NNDemoClassA, NNDemoClassAA, NNDemoClassAAA, NNDemoClassAAB. 117 | 118 | ``` 119 | @interface NNDemoClassA: NSObject @end 120 | @interface NNDemoClassAA: NNDemoClassA @end 121 | @interface NNDemoClassAAA: NNDemoClassAA @end 122 | @interface NNDemoClassAAB: NNDemoClassAA @end 123 | @interface NNDemoClassB: NSObject @end 124 | 125 | [[NNDemoClassA new] sayHelloPop]; 126 | [[NNDemoClassAA new] sayHelloPop]; 127 | [[NNDemoClassAAA new] sayHelloPop]; 128 | [[NNDemoClassAAB new] sayHelloPop]; 129 | [[NNDemoClassB new] sayHelloPop]; 130 | 131 | //Prints "-[NNDemoClassA sayHelloPop] say hello pop" 132 | //Prints "-[NNDemoClassAA sayHelloPop] ClassAA say hello pop" 133 | //Prints "-[NNDemoClassAAA sayHelloPop] ClassAA say hello pop" 134 | //Prints "-[NNDemoClassAAB sayHelloPop] ClassAA say hello pop" 135 | //Prints "-[NNDemoClassB sayHelloPop] say hello pop" 136 | ``` 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /Doc/0.0.x/usage_zh_CN.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc 的使用 2 | 3 | 本文档是 NNPopObjc 的使用指南 (version 0.0.x) 。 4 | 5 | ## @nn_extension 6 | 7 | 在 NNPopObjc 中,`nn_extension` 有两个参数。第一个参数是要扩展的 `procotol`,第二个参数是 `procotol` 实现所要继承的类。 8 | 关键字 `nn_extension` 只是一个宏,使用 `nn_extension` 将声明并实现一个类作为 `procotol` 扩展名,因此 `nn_extension` 只能在 `.m` 文件中使用,其定义如下 : 9 | 10 | ``` 11 | #define nn_extension(protocol, clazz) \ 12 | \ 13 | interface __NNPopObjc##_##protocol##_##clazz : clazz \ 14 | \ 15 | @end \ 16 | \ 17 | @implementation __NNPopObjc##_##protocol##_##clazz \ 18 | \ 19 | ``` 20 | 21 | 这是一个使用 `nn_extension` 扩展 procotol 的示例: 22 | 23 | 声明一个名为 "NNDemoProcotol" 的 procotol: 24 | 25 | ``` 26 | @protocol NNDemoProtocol 27 | 28 | @optional 29 | - (void)sayHelloPop; 30 | 31 | @end 32 | ``` 33 | 34 | 使用 `nn_extension` 扩展 procotol: 35 | 36 | ``` 37 | @nn_extension(NNDemoProtocol, NSObject) 38 | 39 | - (void)sayHelloPop { 40 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 41 | } 42 | 43 | @end 44 | ``` 45 | 46 | 将宏展开以后: 47 | 48 | ``` 49 | @interface __NNPopObjc_NNDemoProtocol_NSObject : NSObject 50 | 51 | @end 52 | 53 | @implementation __NNPopObjc_NNDemoProtocol_NSObject 54 | 55 | - (void)sayHelloPop { 56 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 57 | } 58 | 59 | @end 60 | ``` 61 | 62 | ## Protocol 扩展 63 | 64 | ### 提供默认实现 65 | 66 | 当 `nn_extension` 的第二个参数是 `NSObject` 时,那么它将作为当前 `protocol` 的默认扩展,遵守该 `protocol` 的所有类都将它用作默认的 `protocol` 扩展实现,例如: 67 | 68 | ``` 69 | @nn_extension(NNDemoProtocol, NSObject) 70 | 71 | - (void)sayHelloPop { 72 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 73 | } 74 | 75 | @end 76 | ``` 77 | 78 | 所有符合条件的类都会自动获得此方法的实现,而无需进行任何其他修改。 79 | 80 | ``` 81 | @interface NNDemoClassA: NSObject @end 82 | @interface NNDemoClassAA: NNDemoClassA @end 83 | @interface NNDemoClassB: NSObject @end 84 | 85 | [[NNDemoClassA new] sayHelloPop]; 86 | [[NNDemoClassAA new] sayHelloPop]; 87 | [[NNDemoClassB new] sayHelloPop]; 88 | 89 | //Prints "-[NNDemoClassA sayHelloPop] say hello pop" 90 | //Prints "-[NNDemoClassAA sayHelloPop] say hello pop" 91 | //Prints "-[NNDemoClassB sayHelloPop] say hello pop" 92 | ``` 93 | 94 | ### 向 Protocol 扩展添加类约束 95 | 96 | 将类约束写在 `nn_extension` 的第二个参数中。 例如,定义一个 NNDemoProtocol 协议的扩展,该扩展适用于继承自 NNDemoClassAA 的所有子类。 97 | 98 | ``` 99 | @nn_extension(NNDemoProtocol, NSObject) 100 | 101 | - (void)sayHelloPop { 102 | DLog(@"-[%@ %s] say hello pop", [self class], sel_getName(_cmd)); 103 | } 104 | 105 | @end 106 | 107 | @nn_extension(NNDemoProtocol, NNDemoClassAA) 108 | 109 | - (void)sayHelloPop { 110 | DLog(@"-[%@ %s] ClassAA say hello pop", [self class], sel_getName(_cmd)); 111 | } 112 | 113 | @end 114 | ``` 115 | 116 | 考虑 NNDemoProtocol 扩展的 NNDemoClassAA 的类约束,以及 NNDemoClassA ,NNDemoClassAA , NNDemoClassAAA ,NNDemoClassAAB 之间的继承关系。 117 | 118 | ``` 119 | @interface NNDemoClassA: NSObject @end 120 | @interface NNDemoClassAA: NNDemoClassA @end 121 | @interface NNDemoClassAAA: NNDemoClassAA @end 122 | @interface NNDemoClassAAB: NNDemoClassAA @end 123 | @interface NNDemoClassB: NSObject @end 124 | 125 | [[NNDemoClassA new] sayHelloPop]; 126 | [[NNDemoClassAA new] sayHelloPop]; 127 | [[NNDemoClassAAA new] sayHelloPop]; 128 | [[NNDemoClassAAB new] sayHelloPop]; 129 | [[NNDemoClassB new] sayHelloPop]; 130 | 131 | //Prints "-[NNDemoClassA sayHelloPop] say hello pop" 132 | //Prints "-[NNDemoClassAA sayHelloPop] ClassAA say hello pop" 133 | //Prints "-[NNDemoClassAAA sayHelloPop] ClassAA say hello pop" 134 | //Prints "-[NNDemoClassAAB sayHelloPop] ClassAA say hello pop" 135 | //Prints "-[NNDemoClassB sayHelloPop] say hello pop" 136 | ``` 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /Doc/0.2.x/usage_en.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc usage 2 | 3 | This document is a guide for NNPopObjc (version 0.2.x) . 4 | 5 | ## @nn_extension (Protocol Extensions) 6 | 7 | 8 | ### Parameters 9 | 10 | The parameters of `nn_extension` include two parts, protocol and variable parameter. The protocol is required and the variable parameter is optional. The variable parameter can also be divided into two parts, @nn_where and confrom protocol list. 11 | 12 | ``` 13 | @nn_extension(protocol, @nn_where(...), confrom_protocol_0, confrom_protocol_1, ..., confrom_protocol_n) 14 | ``` 15 | 16 | * `protocol`: A protocol is extended. 17 | * `@nn_where`: A where clause for protocol extension, it is used to add constraints to the conforming classes. 18 | * `confrom_protocols`: A class that adopt protocol extension must confrom to all the protocols in this list. 19 | 20 | ### Examples 21 | 22 | #### A complete protocol extension 23 | 24 | ``` 25 | @nn_extension(protocol, @nn_where(...), confrom_protocol_a, confrom_protocol_b, ...) 26 | ``` 27 | 28 | 29 | #### An omitted confrom_protocols extension 30 | 31 | ``` 32 | @nn_extension(protocol, @nn_where(...)) 33 | ``` 34 | 35 | #### An omitted where clause and confrom protocol list extension 36 | 37 | ``` 38 | @nn_extension(protocol) 39 | ``` 40 | 41 | ### Discussion 42 | 43 | The key word `nn_extension` is just a macro, using the `nn_extension` macro, it will declare and implement a class as the procotol `extension`, so you can only use `nn_extension` in `.m` file. 44 | 45 | ## @nn_where (Where Clause for Protocol Extension) 46 | 47 | ### Parameters 48 | 49 | @nn_where provids where clause for Extension, the clause's variable parameter can pass up to two parameters. 50 | 51 | * `unique_id`: An unique id for where clause, When implementing multiple extensions for a protocol, the unique_id is used to differentiate extensions. The unique_id will be concat into the name of the extension struct variable in section, function and extension class. 52 | * `expression`: An expression that returns a bool value. You can use a variable named `self` in expression, the variable is the class that adopt to the extended protocol. 53 | 54 | 55 | ### Examples 56 | 57 | #### A complete where clause 58 | 59 | ``` 60 | @nn_where(unique_id, expression) 61 | ``` 62 | 63 | #### An omitted unique_id where clause 64 | 65 | ``` 66 | @nn_where(expression) 67 | ``` 68 | it is equivalent to @nn_where(_, expression) 69 | 70 | #### An omitted unique_id and expression where clause 71 | 72 | ``` 73 | @nn_where() 74 | ``` 75 | it is equivalent to @nn_where(_, nn_where_block_default_) 76 | 77 | 78 | ## Protocol Extensions 79 | 80 | 81 | ### Providing Default Implementations 82 | 83 | You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension. 84 | 85 | ### Adding Constraints to Protocol Extensions 86 | 87 | When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending by writing a where clause. 88 | -------------------------------------------------------------------------------- /Doc/0.2.x/usage_zh_CN.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc 的使用 2 | 3 | 本文档是 NNPopObjc 的使用指南 (0.2.x 版本) 。 4 | 5 | ## @nn_extension (协议扩展) 6 | 7 | 8 | ### 参数 9 | 10 | `nn_extension` 参数分为两部分: protocol 参数和可变参数。 protocol 参数为必须参数,可变参数为可选参数。 11 | 可变参数部分同样分为两部分:@nn_where 分句和符合协议列表。 12 | 13 | ``` 14 | @nn_extension(protocol, @nn_where(...), confrom_protocol_0, confrom_protocol_1, ..., confrom_protocol_n) 15 | ``` 16 | 17 | * `protocol`: 被扩展的协议 18 | * `@nn_where`: 协议扩展的 where 分句,用于增加对遵守协议类的约束。 19 | * `confrom_protocols`: 遵守该扩展协议的类,必须要遵守此列表总的所有协议。 20 | 21 | ### 示例 22 | 23 | #### 一个完整参数的协议扩展 24 | 25 | ``` 26 | @nn_extension(protocol, @nn_where(...), confrom_protocol_a, confrom_protocol_b, ...) 27 | ``` 28 | 29 | 30 | #### 省略 confrom_protocols 参数的协议扩展 31 | 32 | ``` 33 | @nn_extension(protocol, @nn_where(...)) 34 | ``` 35 | 36 | #### 省略 where 分句 和 confrom_protocols 参数的协议扩展 37 | 38 | ``` 39 | @nn_extension(protocol) 40 | ``` 41 | 42 | ### 讨论 43 | 44 | 关键字 `nn_extension` 只是一个宏,使用 `nn_extension` 将声明并实现一个类作为 `procotol` 扩展名,因此 `nn_extension` 只能在 `.m` 文件中使用。 45 | 46 | ## @nn_where (用于协议扩展的 where 分句) 47 | 48 | ### 参数 49 | 50 | 51 | @nn_where 为协议扩展提供 where 分句,分句的可变参数最多可接受两个参数。 52 | 53 | * `unique_id`: 一个 where 分句的唯一标示符,当为一个协议实现多个扩展时,`unique_id` 参数用来区分这些扩展。`unique_id` 会被拼接到扩展中变量,函数,以及类的命名当中。 54 | * `expression`: 一个返回布尔值的表达式。表达式中可以引用一个名为 `self` 的变量,该变量即为遵守扩展协议类的类对象。 55 | 56 | 57 | ### 实例 58 | 59 | #### 一个完整参数的 where 分句 60 | 61 | ``` 62 | @nn_where(unique_id, expression) 63 | ``` 64 | 65 | #### 省略 unique_id 参数的 where 分句 66 | 67 | ``` 68 | @nn_where(expression) 69 | ``` 70 | 等价于 @nn_where(_, expression) 71 | 72 | #### 省略 unique_id 和 expression 参数的 where 分句 73 | 74 | ``` 75 | @nn_where() 76 | ``` 77 | 等价于 @nn_where(_, nn_where_block_default_) 78 | 79 | 80 | ## 协议扩展 81 | 82 | 83 | ### 提供默认实现 84 | 85 | 你可以使用协议扩展为该协议提供任意默认实现或计算属性。如果符合扩展的类提供了自己的所需方法或属性的实现,则将使用该实现而不是扩展提供的实现。 86 | 87 | ### 为协议扩展增加约束 88 | 89 | 定义协议扩展时,可以指定符合扩展的类必须符合的特定的约束。可以通过在要扩展的协议名称后编写 where 分句实现这些约束。 90 | -------------------------------------------------------------------------------- /Doc/1.0.x/usage_en.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc usage 2 | 3 | This document is a guide for NNPopObjc. 4 | 5 | ## @nn_extension (Protocol Extensions) 6 | 7 | 8 | ### Parameters 9 | 10 | The parameters of `nn_extension` include two parts, protocol and variable parameter. The protocol is required and the variable parameter is optional. The variable parameter can also be divided into two parts, @nn_where and confrom protocol list. 11 | 12 | ``` 13 | @nn_extension(protocol, @nn_where(...), confrom_protocol_0, confrom_protocol_1, ..., confrom_protocol_n) 14 | ``` 15 | 16 | * `protocol`: A protocol is extended. 17 | * `@nn_where`: A where clause for protocol extension, it is used to add constraints to the conforming classes. 18 | * `confrom_protocols`: A class that adopt protocol extension must confrom to all the protocols in this list. 19 | 20 | ### Examples 21 | 22 | #### A complete protocol extension 23 | 24 | ``` 25 | @nn_extension(protocol, @nn_where(...), confrom_protocol_a, confrom_protocol_b, ...) 26 | ``` 27 | 28 | 29 | #### An omitted confrom_protocols extension 30 | 31 | ``` 32 | @nn_extension(protocol, @nn_where(...)) 33 | ``` 34 | 35 | #### An omitted where clause and confrom protocol list extension 36 | 37 | ``` 38 | @nn_extension(protocol) 39 | ``` 40 | 41 | ### Discussion 42 | 43 | The key word `nn_extension` is just a macro, using the `nn_extension` macro, it will declare and implement a class as the procotol `extension`, so you can only use `nn_extension` in `.m` file. 44 | 45 | ## @nn_where (Where Clause for Protocol Extension) 46 | 47 | ### Parameters 48 | 49 | @nn_where provids where clause for Extension, the clause's variable parameter can pass up to two parameters. 50 | 51 | * `unique_id`: An unique id for where clause, When implementing multiple extensions for a protocol, the unique_id is used to differentiate extensions. The unique_id will be concat into the name of the extension struct variable in section, function and extension class. 52 | * `expression`: An expression that returns a bool value. You can use a variable named `self` in expression, the variable is the class that adopt to the extended protocol. 53 | 54 | 55 | ### Examples 56 | 57 | #### A complete where clause 58 | 59 | ``` 60 | @nn_where(unique_id, expression) 61 | ``` 62 | 63 | #### An omitted unique_id where clause 64 | 65 | ``` 66 | @nn_where(expression) 67 | ``` 68 | it is equivalent to @nn_where(_, expression) 69 | 70 | #### An omitted unique_id and expression where clause 71 | 72 | ``` 73 | @nn_where() 74 | ``` 75 | it is equivalent to @nn_where(_, nn_where_block_default_) 76 | 77 | 78 | ## Protocol Extensions 79 | 80 | 81 | ### Providing Default Implementations 82 | 83 | You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension. 84 | 85 | ### Adding Constraints to Protocol Extensions 86 | 87 | When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending by writing a where clause. 88 | -------------------------------------------------------------------------------- /Doc/1.0.x/usage_zh_CN.md: -------------------------------------------------------------------------------- 1 | # NNPopObjc 的使用 2 | 3 | 本文档是 NNPopObjc 的使用指南。 4 | 5 | ## @nn_extension (协议扩展) 6 | 7 | 8 | ### 参数 9 | 10 | `nn_extension` 参数分为两部分: protocol 参数和可变参数。 protocol 参数为必须参数,可变参数为可选参数。 11 | 可变参数部分同样分为两部分:@nn_where 分句和符合协议列表。 12 | 13 | ``` 14 | @nn_extension(protocol, @nn_where(...), confrom_protocol_0, confrom_protocol_1, ..., confrom_protocol_n) 15 | ``` 16 | 17 | * `protocol`: 被扩展的协议 18 | * `@nn_where`: 协议扩展的 where 分句,用于增加对遵守协议类的约束。 19 | * `confrom_protocols`: 遵守该扩展协议的类,必须要遵守此列表总的所有协议。 20 | 21 | ### 示例 22 | 23 | #### 一个完整参数的协议扩展 24 | 25 | ``` 26 | @nn_extension(protocol, @nn_where(...), confrom_protocol_a, confrom_protocol_b, ...) 27 | ``` 28 | 29 | 30 | #### 省略 confrom_protocols 参数的协议扩展 31 | 32 | ``` 33 | @nn_extension(protocol, @nn_where(...)) 34 | ``` 35 | 36 | #### 省略 where 分句 和 confrom_protocols 参数的协议扩展 37 | 38 | ``` 39 | @nn_extension(protocol) 40 | ``` 41 | 42 | ### 讨论 43 | 44 | 关键字 `nn_extension` 只是一个宏,使用 `nn_extension` 将声明并实现一个类作为 `procotol` 扩展名,因此 `nn_extension` 只能在 `.m` 文件中使用。 45 | 46 | ## @nn_where (用于协议扩展的 where 分句) 47 | 48 | ### 参数 49 | 50 | 51 | @nn_where 为协议扩展提供 where 分句,分句的可变参数最多可接受两个参数。 52 | 53 | * `unique_id`: 一个 where 分句的唯一标示符,当为一个协议实现多个扩展时,`unique_id` 参数用来区分这些扩展。`unique_id` 会被拼接到扩展中变量,函数,以及类的命名当中。 54 | * `expression`: 一个返回布尔值的表达式。表达式中可以引用一个名为 `self` 的变量,该变量即为遵守扩展协议类的类对象。 55 | 56 | 57 | ### 实例 58 | 59 | #### 一个完整参数的 where 分句 60 | 61 | ``` 62 | @nn_where(unique_id, expression) 63 | ``` 64 | 65 | #### 省略 unique_id 参数的 where 分句 66 | 67 | ``` 68 | @nn_where(expression) 69 | ``` 70 | 等价于 @nn_where(_, expression) 71 | 72 | #### 省略 unique_id 和 expression 参数的 where 分句 73 | 74 | ``` 75 | @nn_where() 76 | ``` 77 | 等价于 @nn_where(_, nn_where_block_default_) 78 | 79 | 80 | ## 协议扩展 81 | 82 | 83 | ### 提供默认实现 84 | 85 | 你可以使用协议扩展为该协议提供任意默认实现或计算属性。如果符合扩展的类提供了自己的所需方法或属性的实现,则将使用该实现而不是扩展提供的实现。 86 | 87 | ### 为协议扩展增加约束 88 | 89 | 定义协议扩展时,可以指定符合扩展的类必须符合的特定的约束。可以通过在要扩展的协议名称后编写 where 分句实现这些约束。 90 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/NNDynamic.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNDynamic.h 3 | // NNPopObjcDynamicExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNDynamicProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NNDynamic : NSObject 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/NNDynamic.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNDynamic.m 3 | // NNPopObjcDynamicExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNDynamic.h" 10 | 11 | @implementation NNDynamic 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/NNDynamicProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNDynamicProtocol.h 3 | // NNPopObjcDynamicExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol NNDynamicProtocol 15 | 16 | @optional 17 | + (void)sayHelloPop; 18 | - (void)sayHelloPop; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/NNDynamicProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNDynamicProtocol.m 3 | // NNPopObjcDynamicExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNDynamicProtocol.h" 10 | 11 | #ifndef NNCodeLog_h 12 | #define NNCodeLog_h 13 | 14 | #define DLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String]); 15 | 16 | #endif /* NNCodeLog_h */ 17 | 18 | @nn_extension(NNDynamicProtocol) 19 | 20 | + (void)sayHelloPop { 21 | DLog(@"+[%@ %s] dynamic says hello pop", self, sel_getName(_cmd)); 22 | } 23 | 24 | - (void)sayHelloPop { 25 | DLog(@"-[%@ %s] dynamic says hello pop", [self class], sel_getName(_cmd)); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExample/NNPopObjcDynamicExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcDynamicExample.h 3 | // NNPopObjcDynamicExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NNPopObjcDynamicExample. 12 | FOUNDATION_EXPORT double NNPopObjcDynamicExampleVersionNumber; 13 | 14 | //! Project version string for NNPopObjcDynamicExample. 15 | FOUNDATION_EXPORT const unsigned char NNPopObjcDynamicExampleVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcDynamicExample/NNPopObjcDynamicExampleTests/NNPopObjcDynamicExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcDynamicExampleTests.m 3 | // NNPopObjcDynamicExampleTests 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NNPopObjcDynamicExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NNPopObjcDynamicExampleTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample.xcodeproj/xcshareddata/xcschemes/NNPopObjcExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 87 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCode.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCode.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2020/2/22. 6 | // Copyright © 2020 顾海军. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNCodeProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NNCode : NSObject 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCode.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNCode.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2020/2/22. 6 | // Copyright © 2020 顾海军. All rights reserved. 7 | // 8 | 9 | #import "NNCode.h" 10 | 11 | @implementation NNCode 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeC.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeC.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNCode.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NNCodeC : NNCode 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeC.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeC.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeC.h" 10 | 11 | @implementation NNCodeC 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeCpp.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeCpp.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeC.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NNCodeCpp : NNCodeC 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeCpp.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeCpp.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeCpp.h" 10 | 11 | @implementation NNCodeCpp 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeLog.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNCodeLog_h 10 | #define NNCodeLog_h 11 | 12 | #define DLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String]); 13 | 14 | #endif /* NNCodeLog_h */ 15 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeObjc.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeObjc.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeC.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NNCodeObjc : NNCodeC 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeObjc.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeObjc.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeObjc.h" 10 | 11 | @implementation NNCodeObjc 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeProtocol.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @protocol NNCodeProtocol 14 | 15 | @optional 16 | + (void)sayHelloPop; 17 | - (void)sayHelloPop; 18 | 19 | @end 20 | 21 | 22 | @protocol NNCodeNameProtocol 23 | 24 | @optional 25 | @property (nonatomic, strong) NSString* name; 26 | 27 | @end 28 | 29 | 30 | @protocol NNCodeWhoProtocol 31 | 32 | @optional 33 | @property (nonatomic, strong, readonly) NSString* who; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeProtocol.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNCodeProtocol.h" 10 | #import 11 | #import "NNCodeObjc.h" 12 | #import "NNCodeCpp.h" 13 | #import "NNPopObjcExample-Swift.h" 14 | #import "NNCodeLog.h" 15 | 16 | 17 | @nn_extension(NNCodeProtocol) 18 | 19 | + (void)sayHelloPop { 20 | DLog(@"+[%@ %s] code says hello pop", self, sel_getName(_cmd)); 21 | } 22 | 23 | - (void)sayHelloPop { 24 | DLog(@"-[%@ %s] code says hello pop", [self class], sel_getName(_cmd)); 25 | } 26 | 27 | @end 28 | 29 | 30 | @nn_extension(NNCodeNameProtocol) 31 | 32 | - (void)setName:(NSString *)name { 33 | objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 34 | } 35 | 36 | - (NSString *)name { 37 | return objc_getAssociatedObject(self, @selector(name)); 38 | } 39 | 40 | @end 41 | 42 | 43 | @nn_extension(NNCodeWhoProtocol, @nn_where(), NNCodeNameProtocol) 44 | 45 | - (NSString *)who { 46 | NSString *who = [NSString stringWithFormat:@"-[%@ %s] code says I am %@", [self class], sel_getName(_cmd), self.name]; 47 | return who; 48 | } 49 | 50 | @end 51 | 52 | 53 | @nn_extension(NNCodeWhoProtocol, @nn_where(NNCodeCpp, self == [NNCodeCpp class]), NNCodeNameProtocol) 54 | 55 | + (void)sayHelloPop { 56 | DLog(@"+[%@ %s] cpp says hello pop", self, sel_getName(_cmd)); 57 | } 58 | 59 | - (void)sayHelloPop { 60 | DLog(@"-[%@ %s] cpp says hello pop", [self class], sel_getName(_cmd)); 61 | } 62 | 63 | - (NSString *)who { 64 | NSString *who = [NSString stringWithFormat:@"-[%@ %s] cpp says I am %@", [self class], sel_getName(_cmd), self.name]; 65 | return who; 66 | } 67 | 68 | @end 69 | 70 | 71 | @nn_extension(NNCodeWhoProtocol, @nn_where(a_where_unique_id, self == [NNCodeObjc class]), NNCodeNameProtocol) 72 | 73 | + (void)sayHelloPop { 74 | DLog(@"+[%@ %s] objc says hello pop", self, sel_getName(_cmd)); 75 | } 76 | 77 | - (NSString *)who { 78 | NSString *who = [NSString stringWithFormat:@"-[%@ %s] objc says I am %@", [self class], sel_getName(_cmd), self.name]; 79 | return who; 80 | } 81 | 82 | - (void)setName:(NSString *)name { 83 | objc_setAssociatedObject(self, @selector(name), [NSString stringWithFormat:@"[%@]", name], OBJC_ASSOCIATION_RETAIN_NONATOMIC); 84 | } 85 | 86 | - (NSString *)name { 87 | NSString *_name = objc_getAssociatedObject(self, @selector(name)); 88 | return [NSString stringWithFormat:@"%@!", _name]; 89 | } 90 | 91 | @end 92 | 93 | 94 | @nn_extension(NNCodeWhoProtocol, @nn_where(NNCodeSwift, self == [NNCodeSwift class]), NNCodeNameProtocol) 95 | 96 | - (void)sayHelloPop { 97 | DLog(@"-[%@ %s] swift says hello pop", [self class], sel_getName(_cmd)); 98 | } 99 | 100 | - (NSString *)who { 101 | NSString *who = [NSString stringWithFormat:@"-[%@ %s] swift says I am %@", [self class], sel_getName(_cmd), self.name]; 102 | return who; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Classes/NNCodeSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NNCodeSwift.swift 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/11/21. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc class NNCodeSwift: NSObject, NNCodeWhoProtocol, NNCodeNameProtocol { 12 | @objc var name: String = "" 13 | } 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Supporting Files/NNPopObjcExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "NNCodeProtocol.h" 6 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Supporting Files/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef PrefixHeader_pch 10 | #define PrefixHeader_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | 15 | #import 16 | 17 | #endif /* PrefixHeader_pch */ 18 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/Supporting Files/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // NNPopObjcExample 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "NNCodeLog.h" 11 | #import "NNCodeProtocol.h" 12 | #import "NNCode.h" 13 | #import "NNCodeC.h" 14 | #import "NNCodeObjc.h" 15 | #import "NNCodeCpp.h" 16 | #import "NNPopObjcExample-Swift.h" 17 | 18 | #import 19 | #import 20 | 21 | @interface ViewController () 22 | 23 | @property (weak, nonatomic) IBOutlet UITextView *logTextView; 24 | 25 | @end 26 | 27 | @implementation ViewController 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | 33 | self.logTextView.text = @""; 34 | self.logTextView.editable = NO; 35 | 36 | [self redirectSTD:STDOUT_FILENO]; 37 | [self redirectSTD:STDERR_FILENO]; 38 | 39 | [self popExample]; 40 | } 41 | 42 | - (void)popExample { 43 | 44 | DLog(@""); 45 | 46 | [NNCode sayHelloPop]; 47 | [NNCodeC sayHelloPop]; 48 | [NNCodeCpp sayHelloPop]; 49 | [NNCodeObjc sayHelloPop]; 50 | [NNCodeSwift sayHelloPop]; 51 | 52 | DLog(@""); 53 | 54 | NNCode *code = [NNCode new]; 55 | NNCodeC *codeC = [NNCodeC new]; 56 | NNCodeCpp *codeCpp = [NNCodeCpp new]; 57 | NNCodeObjc *codeObjc = [NNCodeObjc new]; 58 | NNCodeSwift *codeSwift = [NNCodeSwift new]; 59 | 60 | [code sayHelloPop]; 61 | [codeC sayHelloPop]; 62 | [codeCpp sayHelloPop]; 63 | [codeObjc sayHelloPop]; 64 | [codeSwift sayHelloPop]; 65 | 66 | DLog(@""); 67 | 68 | codeC.name = @"c"; 69 | DLog(@"%@", codeC.who); 70 | codeCpp.name = @"cpp"; 71 | DLog(@"%@", codeCpp.who); 72 | codeObjc.name = @"objc"; 73 | DLog(@"%@", codeObjc.who); 74 | codeSwift.name = @"swift"; 75 | DLog(@"%@", codeSwift.who); 76 | 77 | DLog(@""); 78 | 79 | [NNDynamic sayHelloPop]; 80 | [[NNDynamic new] sayHelloPop]; 81 | 82 | [NNStatic sayHelloPop]; 83 | [[NNStatic new] sayHelloPop]; 84 | } 85 | 86 | - (void)redirectNotificationHandle:(NSNotification *)notification{ 87 | NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; 88 | NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 89 | 90 | self.logTextView.text = [NSString stringWithFormat:@"%@%@",self.logTextView.text, str]; 91 | NSRange range; 92 | range.location = [self.logTextView.text length] - 1; 93 | range.length = 0; 94 | [self.logTextView scrollRangeToVisible:range]; 95 | 96 | [[notification object] readInBackgroundAndNotify]; 97 | } 98 | 99 | - (void)redirectSTD:(int )fd{ 100 | NSPipe * pipe = [NSPipe pipe] ; 101 | NSFileHandle *pipeReadHandle = [pipe fileHandleForReading] ; 102 | dup2([[pipe fileHandleForWriting] fileDescriptor], fd) ; 103 | 104 | [[NSNotificationCenter defaultCenter] addObserver:self 105 | selector:@selector(redirectNotificationHandle:) 106 | name:NSFileHandleReadCompletionNotification 107 | object:pipeReadHandle] ; 108 | [pipeReadHandle readInBackgroundAndNotify]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExampleTests/NNPopObjcExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcExampleTests.m 3 | // NNPopObjcExampleTests 4 | // 5 | // Created by GuHaijun on 2019/12/5. 6 | // Copyright © 2019 GuHaijun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NNPopObjcExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NNPopObjcExampleTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcExample/NNPopObjcExampleUITests/NNPopObjcExampleUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcExampleUITests.m 3 | // NNPopObjcExampleUITests 4 | // 5 | // Created by GuHaijun on 2019/12/5. 6 | // Copyright © 2019 GuHaijun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NNPopObjcExampleUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NNPopObjcExampleUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | - (void)tearDown { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | } 29 | 30 | - (void)testExample { 31 | // UI tests must launch the application that they test. 32 | XCUIApplication *app = [[XCUIApplication alloc] init]; 33 | [app launch]; 34 | 35 | // Use recording to get started writing UI tests. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testLaunchPerformance { 40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 41 | // This measures how long it takes to launch your application. 42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{ 43 | [[[XCUIApplication alloc] init] launch]; 44 | }]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7A934ED62410E044000A8F1F /* NNPopObjcStaticExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A934ECC2410E043000A8F1F /* NNPopObjcStaticExample.framework */; }; 11 | 7A934EDB2410E044000A8F1F /* NNPopObjcStaticExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A934EDA2410E044000A8F1F /* NNPopObjcStaticExampleTests.m */; }; 12 | 7A934EDD2410E044000A8F1F /* NNPopObjcStaticExample.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A934ECF2410E043000A8F1F /* NNPopObjcStaticExample.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 7A934EEA2410E0B4000A8F1F /* NNStatic.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A934EE62410E0B4000A8F1F /* NNStatic.m */; }; 14 | 7A934EEB2410E0B4000A8F1F /* NNStatic.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A934EE72410E0B4000A8F1F /* NNStatic.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 7A934EEC2410E0B4000A8F1F /* NNStaticProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A934EE82410E0B4000A8F1F /* NNStaticProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 7A934EED2410E0B4000A8F1F /* NNStaticProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A934EE92410E0B4000A8F1F /* NNStaticProtocol.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | 7A934ED72410E044000A8F1F /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = 7A934EC32410E043000A8F1F /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = 7A934ECB2410E043000A8F1F; 25 | remoteInfo = NNPopObjcStaticExample; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 7A934ECC2410E043000A8F1F /* NNPopObjcStaticExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NNPopObjcStaticExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 7A934ECF2410E043000A8F1F /* NNPopObjcStaticExample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NNPopObjcStaticExample.h; sourceTree = ""; }; 32 | 7A934ED02410E043000A8F1F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 7A934ED52410E044000A8F1F /* NNPopObjcStaticExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NNPopObjcStaticExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 7A934EDA2410E044000A8F1F /* NNPopObjcStaticExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NNPopObjcStaticExampleTests.m; sourceTree = ""; }; 35 | 7A934EDC2410E044000A8F1F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 7A934EE62410E0B4000A8F1F /* NNStatic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNStatic.m; sourceTree = ""; }; 37 | 7A934EE72410E0B4000A8F1F /* NNStatic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NNStatic.h; sourceTree = ""; }; 38 | 7A934EE82410E0B4000A8F1F /* NNStaticProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NNStaticProtocol.h; sourceTree = ""; }; 39 | 7A934EE92410E0B4000A8F1F /* NNStaticProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNStaticProtocol.m; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 7A934EC92410E043000A8F1F /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 7A934ED22410E044000A8F1F /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 7A934ED62410E044000A8F1F /* NNPopObjcStaticExample.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 7A934EC22410E043000A8F1F = { 62 | isa = PBXGroup; 63 | children = ( 64 | 7A934ECE2410E043000A8F1F /* NNPopObjcStaticExample */, 65 | 7A934ED92410E044000A8F1F /* NNPopObjcStaticExampleTests */, 66 | 7A934ECD2410E043000A8F1F /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | 7A934ECD2410E043000A8F1F /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 7A934ECC2410E043000A8F1F /* NNPopObjcStaticExample.framework */, 74 | 7A934ED52410E044000A8F1F /* NNPopObjcStaticExampleTests.xctest */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | 7A934ECE2410E043000A8F1F /* NNPopObjcStaticExample */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 7A934ECF2410E043000A8F1F /* NNPopObjcStaticExample.h */, 83 | 7A934EE72410E0B4000A8F1F /* NNStatic.h */, 84 | 7A934EE62410E0B4000A8F1F /* NNStatic.m */, 85 | 7A934EE82410E0B4000A8F1F /* NNStaticProtocol.h */, 86 | 7A934EE92410E0B4000A8F1F /* NNStaticProtocol.m */, 87 | 7A934ED02410E043000A8F1F /* Info.plist */, 88 | ); 89 | path = NNPopObjcStaticExample; 90 | sourceTree = ""; 91 | }; 92 | 7A934ED92410E044000A8F1F /* NNPopObjcStaticExampleTests */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 7A934EDA2410E044000A8F1F /* NNPopObjcStaticExampleTests.m */, 96 | 7A934EDC2410E044000A8F1F /* Info.plist */, 97 | ); 98 | path = NNPopObjcStaticExampleTests; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXHeadersBuildPhase section */ 104 | 7A934EC72410E043000A8F1F /* Headers */ = { 105 | isa = PBXHeadersBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | 7A934EEC2410E0B4000A8F1F /* NNStaticProtocol.h in Headers */, 109 | 7A934EEB2410E0B4000A8F1F /* NNStatic.h in Headers */, 110 | 7A934EDD2410E044000A8F1F /* NNPopObjcStaticExample.h in Headers */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXHeadersBuildPhase section */ 115 | 116 | /* Begin PBXNativeTarget section */ 117 | 7A934ECB2410E043000A8F1F /* NNPopObjcStaticExample */ = { 118 | isa = PBXNativeTarget; 119 | buildConfigurationList = 7A934EE02410E044000A8F1F /* Build configuration list for PBXNativeTarget "NNPopObjcStaticExample" */; 120 | buildPhases = ( 121 | 7A934EC72410E043000A8F1F /* Headers */, 122 | 7A934EC82410E043000A8F1F /* Sources */, 123 | 7A934EC92410E043000A8F1F /* Frameworks */, 124 | 7A934ECA2410E043000A8F1F /* Resources */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = NNPopObjcStaticExample; 131 | productName = NNPopObjcStaticExample; 132 | productReference = 7A934ECC2410E043000A8F1F /* NNPopObjcStaticExample.framework */; 133 | productType = "com.apple.product-type.framework"; 134 | }; 135 | 7A934ED42410E044000A8F1F /* NNPopObjcStaticExampleTests */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = 7A934EE32410E044000A8F1F /* Build configuration list for PBXNativeTarget "NNPopObjcStaticExampleTests" */; 138 | buildPhases = ( 139 | 7A934ED12410E044000A8F1F /* Sources */, 140 | 7A934ED22410E044000A8F1F /* Frameworks */, 141 | 7A934ED32410E044000A8F1F /* Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | 7A934ED82410E044000A8F1F /* PBXTargetDependency */, 147 | ); 148 | name = NNPopObjcStaticExampleTests; 149 | productName = NNPopObjcStaticExampleTests; 150 | productReference = 7A934ED52410E044000A8F1F /* NNPopObjcStaticExampleTests.xctest */; 151 | productType = "com.apple.product-type.bundle.unit-test"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 7A934EC32410E043000A8F1F /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 1130; 160 | ORGANIZATIONNAME = "顾海军"; 161 | TargetAttributes = { 162 | 7A934ECB2410E043000A8F1F = { 163 | CreatedOnToolsVersion = 11.3.1; 164 | }; 165 | 7A934ED42410E044000A8F1F = { 166 | CreatedOnToolsVersion = 11.3.1; 167 | }; 168 | }; 169 | }; 170 | buildConfigurationList = 7A934EC62410E043000A8F1F /* Build configuration list for PBXProject "NNPopObjcStaticExample" */; 171 | compatibilityVersion = "Xcode 9.3"; 172 | developmentRegion = en; 173 | hasScannedForEncodings = 0; 174 | knownRegions = ( 175 | en, 176 | Base, 177 | ); 178 | mainGroup = 7A934EC22410E043000A8F1F; 179 | productRefGroup = 7A934ECD2410E043000A8F1F /* Products */; 180 | projectDirPath = ""; 181 | projectRoot = ""; 182 | targets = ( 183 | 7A934ECB2410E043000A8F1F /* NNPopObjcStaticExample */, 184 | 7A934ED42410E044000A8F1F /* NNPopObjcStaticExampleTests */, 185 | ); 186 | }; 187 | /* End PBXProject section */ 188 | 189 | /* Begin PBXResourcesBuildPhase section */ 190 | 7A934ECA2410E043000A8F1F /* Resources */ = { 191 | isa = PBXResourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | 7A934ED32410E044000A8F1F /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXResourcesBuildPhase section */ 205 | 206 | /* Begin PBXSourcesBuildPhase section */ 207 | 7A934EC82410E043000A8F1F /* Sources */ = { 208 | isa = PBXSourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 7A934EEA2410E0B4000A8F1F /* NNStatic.m in Sources */, 212 | 7A934EED2410E0B4000A8F1F /* NNStaticProtocol.m in Sources */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | 7A934ED12410E044000A8F1F /* Sources */ = { 217 | isa = PBXSourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 7A934EDB2410E044000A8F1F /* NNPopObjcStaticExampleTests.m in Sources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXSourcesBuildPhase section */ 225 | 226 | /* Begin PBXTargetDependency section */ 227 | 7A934ED82410E044000A8F1F /* PBXTargetDependency */ = { 228 | isa = PBXTargetDependency; 229 | target = 7A934ECB2410E043000A8F1F /* NNPopObjcStaticExample */; 230 | targetProxy = 7A934ED72410E044000A8F1F /* PBXContainerItemProxy */; 231 | }; 232 | /* End PBXTargetDependency section */ 233 | 234 | /* Begin XCBuildConfiguration section */ 235 | 7A934EDE2410E044000A8F1F /* Debug */ = { 236 | isa = XCBuildConfiguration; 237 | buildSettings = { 238 | ALWAYS_SEARCH_USER_PATHS = NO; 239 | CLANG_ANALYZER_NONNULL = YES; 240 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 242 | CLANG_CXX_LIBRARY = "libc++"; 243 | CLANG_ENABLE_MODULES = YES; 244 | CLANG_ENABLE_OBJC_ARC = YES; 245 | CLANG_ENABLE_OBJC_WEAK = YES; 246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_COMMA = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 253 | CLANG_WARN_EMPTY_BODY = YES; 254 | CLANG_WARN_ENUM_CONVERSION = YES; 255 | CLANG_WARN_INFINITE_RECURSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 258 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 259 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 261 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 262 | CLANG_WARN_STRICT_PROTOTYPES = YES; 263 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 264 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 265 | CLANG_WARN_UNREACHABLE_CODE = YES; 266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 267 | COPY_PHASE_STRIP = NO; 268 | CURRENT_PROJECT_VERSION = 1; 269 | DEBUG_INFORMATION_FORMAT = dwarf; 270 | ENABLE_STRICT_OBJC_MSGSEND = YES; 271 | ENABLE_TESTABILITY = YES; 272 | GCC_C_LANGUAGE_STANDARD = gnu11; 273 | GCC_DYNAMIC_NO_PIC = NO; 274 | GCC_NO_COMMON_BLOCKS = YES; 275 | GCC_OPTIMIZATION_LEVEL = 0; 276 | GCC_PREPROCESSOR_DEFINITIONS = ( 277 | "DEBUG=1", 278 | "$(inherited)", 279 | ); 280 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 281 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 282 | GCC_WARN_UNDECLARED_SELECTOR = YES; 283 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 284 | GCC_WARN_UNUSED_FUNCTION = YES; 285 | GCC_WARN_UNUSED_VARIABLE = YES; 286 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 287 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 288 | MTL_FAST_MATH = YES; 289 | ONLY_ACTIVE_ARCH = YES; 290 | SDKROOT = iphoneos; 291 | VERSIONING_SYSTEM = "apple-generic"; 292 | VERSION_INFO_PREFIX = ""; 293 | }; 294 | name = Debug; 295 | }; 296 | 7A934EDF2410E044000A8F1F /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ALWAYS_SEARCH_USER_PATHS = NO; 300 | CLANG_ANALYZER_NONNULL = YES; 301 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 302 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 303 | CLANG_CXX_LIBRARY = "libc++"; 304 | CLANG_ENABLE_MODULES = YES; 305 | CLANG_ENABLE_OBJC_ARC = YES; 306 | CLANG_ENABLE_OBJC_WEAK = YES; 307 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 308 | CLANG_WARN_BOOL_CONVERSION = YES; 309 | CLANG_WARN_COMMA = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INFINITE_RECURSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 323 | CLANG_WARN_STRICT_PROTOTYPES = YES; 324 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 325 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 326 | CLANG_WARN_UNREACHABLE_CODE = YES; 327 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 328 | COPY_PHASE_STRIP = NO; 329 | CURRENT_PROJECT_VERSION = 1; 330 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 331 | ENABLE_NS_ASSERTIONS = NO; 332 | ENABLE_STRICT_OBJC_MSGSEND = YES; 333 | GCC_C_LANGUAGE_STANDARD = gnu11; 334 | GCC_NO_COMMON_BLOCKS = YES; 335 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 336 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 337 | GCC_WARN_UNDECLARED_SELECTOR = YES; 338 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 339 | GCC_WARN_UNUSED_FUNCTION = YES; 340 | GCC_WARN_UNUSED_VARIABLE = YES; 341 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 342 | MTL_ENABLE_DEBUG_INFO = NO; 343 | MTL_FAST_MATH = YES; 344 | SDKROOT = iphoneos; 345 | VALIDATE_PRODUCT = YES; 346 | VERSIONING_SYSTEM = "apple-generic"; 347 | VERSION_INFO_PREFIX = ""; 348 | }; 349 | name = Release; 350 | }; 351 | 7A934EE12410E044000A8F1F /* Debug */ = { 352 | isa = XCBuildConfiguration; 353 | buildSettings = { 354 | CODE_SIGN_STYLE = Automatic; 355 | DEFINES_MODULE = YES; 356 | DYLIB_COMPATIBILITY_VERSION = 1; 357 | DYLIB_CURRENT_VERSION = 1; 358 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 359 | INFOPLIST_FILE = NNPopObjcStaticExample/Info.plist; 360 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 361 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@executable_path/Frameworks", 365 | "@loader_path/Frameworks", 366 | ); 367 | MACOSX_DEPLOYMENT_TARGET = 10.7; 368 | PRODUCT_BUNDLE_IDENTIFIER = com.nn.NNPopObjcStaticExample; 369 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 370 | SKIP_INSTALL = YES; 371 | TARGETED_DEVICE_FAMILY = "1,2"; 372 | TVOS_DEPLOYMENT_TARGET = 9.0; 373 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 374 | }; 375 | name = Debug; 376 | }; 377 | 7A934EE22410E044000A8F1F /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | CODE_SIGN_STYLE = Automatic; 381 | DEFINES_MODULE = YES; 382 | DYLIB_COMPATIBILITY_VERSION = 1; 383 | DYLIB_CURRENT_VERSION = 1; 384 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 385 | INFOPLIST_FILE = NNPopObjcStaticExample/Info.plist; 386 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 387 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 388 | LD_RUNPATH_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "@executable_path/Frameworks", 391 | "@loader_path/Frameworks", 392 | ); 393 | MACOSX_DEPLOYMENT_TARGET = 10.7; 394 | PRODUCT_BUNDLE_IDENTIFIER = com.nn.NNPopObjcStaticExample; 395 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 396 | SKIP_INSTALL = YES; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | TVOS_DEPLOYMENT_TARGET = 9.0; 399 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 400 | }; 401 | name = Release; 402 | }; 403 | 7A934EE42410E044000A8F1F /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | CODE_SIGN_STYLE = Automatic; 407 | INFOPLIST_FILE = NNPopObjcStaticExampleTests/Info.plist; 408 | LD_RUNPATH_SEARCH_PATHS = ( 409 | "$(inherited)", 410 | "@executable_path/Frameworks", 411 | "@loader_path/Frameworks", 412 | ); 413 | PRODUCT_BUNDLE_IDENTIFIER = com.nn.NNPopObjcStaticExampleTests; 414 | PRODUCT_NAME = "$(TARGET_NAME)"; 415 | TARGETED_DEVICE_FAMILY = "1,2"; 416 | }; 417 | name = Debug; 418 | }; 419 | 7A934EE52410E044000A8F1F /* Release */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | CODE_SIGN_STYLE = Automatic; 423 | INFOPLIST_FILE = NNPopObjcStaticExampleTests/Info.plist; 424 | LD_RUNPATH_SEARCH_PATHS = ( 425 | "$(inherited)", 426 | "@executable_path/Frameworks", 427 | "@loader_path/Frameworks", 428 | ); 429 | PRODUCT_BUNDLE_IDENTIFIER = com.nn.NNPopObjcStaticExampleTests; 430 | PRODUCT_NAME = "$(TARGET_NAME)"; 431 | TARGETED_DEVICE_FAMILY = "1,2"; 432 | }; 433 | name = Release; 434 | }; 435 | /* End XCBuildConfiguration section */ 436 | 437 | /* Begin XCConfigurationList section */ 438 | 7A934EC62410E043000A8F1F /* Build configuration list for PBXProject "NNPopObjcStaticExample" */ = { 439 | isa = XCConfigurationList; 440 | buildConfigurations = ( 441 | 7A934EDE2410E044000A8F1F /* Debug */, 442 | 7A934EDF2410E044000A8F1F /* Release */, 443 | ); 444 | defaultConfigurationIsVisible = 0; 445 | defaultConfigurationName = Release; 446 | }; 447 | 7A934EE02410E044000A8F1F /* Build configuration list for PBXNativeTarget "NNPopObjcStaticExample" */ = { 448 | isa = XCConfigurationList; 449 | buildConfigurations = ( 450 | 7A934EE12410E044000A8F1F /* Debug */, 451 | 7A934EE22410E044000A8F1F /* Release */, 452 | ); 453 | defaultConfigurationIsVisible = 0; 454 | defaultConfigurationName = Release; 455 | }; 456 | 7A934EE32410E044000A8F1F /* Build configuration list for PBXNativeTarget "NNPopObjcStaticExampleTests" */ = { 457 | isa = XCConfigurationList; 458 | buildConfigurations = ( 459 | 7A934EE42410E044000A8F1F /* Debug */, 460 | 7A934EE52410E044000A8F1F /* Release */, 461 | ); 462 | defaultConfigurationIsVisible = 0; 463 | defaultConfigurationName = Release; 464 | }; 465 | /* End XCConfigurationList section */ 466 | }; 467 | rootObject = 7A934EC32410E043000A8F1F /* Project object */; 468 | } 469 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/NNPopObjcStaticExample.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcStaticExample.h 3 | // NNPopObjcStaticExample 4 | // 5 | // Created by 顾海军 on 2020/3/5. 6 | // Copyright © 2020 顾海军. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NNPopObjcStaticExample. 12 | FOUNDATION_EXPORT double NNPopObjcStaticExampleVersionNumber; 13 | 14 | //! Project version string for NNPopObjcStaticExample. 15 | FOUNDATION_EXPORT const unsigned char NNPopObjcStaticExampleVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/NNStatic.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNStatic.h 3 | // NNPopObjcStaticExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNStaticProtocol.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NNStatic : NSObject 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/NNStatic.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNStatic.m 3 | // NNPopObjcStaticExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNStatic.h" 10 | 11 | @implementation NNStatic 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/NNStaticProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNStaticProtocol.h 3 | // NNPopObjcStaticExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @protocol NNStaticProtocol 15 | 16 | @optional 17 | + (void)sayHelloPop; 18 | - (void)sayHelloPop; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExample/NNStaticProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNStaticProtocol.m 3 | // NNPopObjcStaticExample 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNStaticProtocol.h" 10 | 11 | #ifndef NNCodeLog_h 12 | #define NNCodeLog_h 13 | 14 | #define DLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String]); 15 | 16 | #endif /* NNCodeLog_h */ 17 | 18 | @nn_extension(NNStaticProtocol) 19 | 20 | + (void)sayHelloPop { 21 | DLog(@"+[%@ %s] static says hello pop", self, sel_getName(_cmd)); 22 | } 23 | 24 | - (void)sayHelloPop { 25 | DLog(@"-[%@ %s] static says hello pop", [self class], sel_getName(_cmd)); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/NNPopObjcStaticExample/NNPopObjcStaticExampleTests/NNPopObjcStaticExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcStaticExampleTests.m 3 | // NNPopObjcStaticExampleTests 4 | // 5 | // Created by 顾海军 on 2020/3/5. 6 | // Copyright © 2020 顾海军. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NNPopObjcStaticExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NNPopObjcStaticExampleTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'xcpretty', '~> 0.2.8' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 amisare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NNPopObjc.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "NNPopObjc" 4 | s.version = ENV['BUMP_VERSION'] || '0.0.1' 5 | s.summary = "Implement protocol extensions for protocol-oriented programming." 6 | 7 | s.description = <<-DESC 8 | Inspired by swift's protocol-oriented programming。 9 | Based on runtime, the framework implements protocol extensions for protocol-oriented programming. 10 | DESC 11 | 12 | s.homepage = "https://github.com/amisare/NNPopObjc" 13 | s.license = { :type => "MIT", :file => "LICENSE" } 14 | s.author = { "Haijun Gu" => "243297288@qq.com" } 15 | s.social_media_url = "https://www.jianshu.com/u/9df9f28ff266" 16 | 17 | s.source = { :git => "https://github.com/amisare/NNPopObjc.git", :tag => s.version.to_s } 18 | 19 | s.requires_arc = true 20 | s.libraries = 'c++' 21 | 22 | s.user_target_xcconfig = {'OTHER_LDFLAGS' => '-all_load'} 23 | 24 | s.osx.deployment_target = '10.7' 25 | s.ios.deployment_target = '6.0' 26 | s.tvos.deployment_target = '9.0' 27 | s.watchos.deployment_target = '2.0' 28 | 29 | s.osx.pod_target_xcconfig = { 'PRODUCT_BUNDLE_IDENTIFIER' => 'com.nn.NNPopObjc' } 30 | s.ios.pod_target_xcconfig = { 'PRODUCT_BUNDLE_IDENTIFIER' => 'com.nn.NNPopObjc' } 31 | s.tvos.pod_target_xcconfig = { 'PRODUCT_BUNDLE_IDENTIFIER' => 'com.nn.NNPopObjc' } 32 | s.watchos.pod_target_xcconfig = { 'PRODUCT_BUNDLE_IDENTIFIER' => 'com.nn.NNPopObjc-watchOS' } 33 | 34 | s.source_files = 'NNPopObjc/*.{h,m,mm}' 35 | 36 | s.subspec 'extobjc' do |ss| 37 | ss.source_files = 'NNPopObjc/extobjc/*.{h,m}' 38 | end 39 | 40 | s.subspec 'Public' do |ss| 41 | ss.source_files = 'NNPopObjc/Public/*.{h,m,mm}' 42 | end 43 | 44 | s.subspec 'Source' do |ss| 45 | ss.source_files = 'NNPopObjc/Source/*.{h,m,mm}' 46 | ss.private_header_files = 'NNPopObjc/Source/*.{h}' 47 | ss.dependency 'NNPopObjc/extobjc' 48 | ss.dependency 'NNPopObjc/Public' 49 | end 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/xcshareddata/xcschemes/NNPopObjc iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/xcshareddata/xcschemes/NNPopObjc macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/xcshareddata/xcschemes/NNPopObjc tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /NNPopObjc.xcodeproj/xcshareddata/xcschemes/NNPopObjc watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 44 | 45 | 51 | 52 | 58 | 59 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /NNPopObjc.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /NNPopObjc.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NNPopObjc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /NNPopObjc/NNPopObjc.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjc.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for NNPopObjc. 12 | FOUNDATION_EXPORT double NNPopObjcVersionNumber; 13 | 14 | //! Project version string for NNPopObjc. 15 | FOUNDATION_EXPORT const unsigned char NNPopObjcVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #if __has_include() 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #else 28 | #import "NNPopObjcMetaMacros.h" 29 | #import "NNPopObjcMacros.h" 30 | #import "NNPopObjcDefines.h" 31 | #import "NNPopObjcWhere.h" 32 | #import "NNPopObjcDescription.h" 33 | #import "NNPopObjcExtension.h" 34 | #import "NNPopObjcScope.h" 35 | #endif 36 | 37 | /** 38 | * Protocol Extensions 39 | * 40 | * Protocols can be extended to provide method, and computed property implementations to 41 | * conforming types. This allows you to define behavior on protocols themselves, rather 42 | * than in each type’s individual conformance. 43 | * 44 | * The parameters of nn_extension include two parts, protocol and variable parameter. 45 | * The protocol is required and the variable parameter is optional. 46 | * The variable parameter can also be divided into two parts, @nn_where and confrom protocol list. 47 | * 48 | * A complete protocol extension: 49 | * @nn_extension(protocol, @nn_where(...), confrom_protocol_0, confrom_protocol_1, ..., confrom_protocol_n) 50 | * 51 | * An omitted confrom_protocols extension: 52 | * @nn_extension(protocol, @nn_where(...)) 53 | * 54 | * An omitted where clause and confrom protocol list extension: 55 | * @nn_extension(protocol) 56 | * 57 | * @param protocol A protocol. 58 | * @param @nn_where A where clause for protocol extension, it is used to add constraints to the 59 | * conforming classes. 60 | * @param confrom_protocols... A class that adopt protocol extension must confrom to all the protocols in this list. 61 | * 62 | * @note 63 | * 64 | * 1. Providing Default Implementations 65 | * You can use protocol extensions to provide a default implementation to any method or 66 | * computed property requirement of that protocol. If a conforming type provides its own 67 | * implementation of a required method or property, that implementation will be used instead 68 | * of the one provided by the extension. 69 | * 70 | * 2. Adding Constraints to Protocol Extensions 71 | * When you define a protocol extension, you can specify constraints that conforming types 72 | * must satisfy before the methods and properties of the extension are available. You write 73 | * these constraints after the name of the protocol you’re extending by writing a where clause. 74 | * 75 | */ 76 | #define nn_extension(protocol, ...) nn_pop_extension_(protocol, __VA_ARGS__) 77 | 78 | /** 79 | * Where Clause for Protocol Extension 80 | * 81 | * This provids where clause for Extension, the clause's variable parameter can pass up to 82 | * two parameters. 83 | * 84 | * A complete where clause: 85 | * @nn_where(unique_id, expression) 86 | * 87 | * An omitted unique_id where clause: 88 | * @nn_where(expression) 89 | * it is equivalent to @nn_where(_, expression) 90 | * 91 | * An omitted unique_id and expression where clause: 92 | * @nn_where() 93 | * it is equivalent to @nn_where(_, nn_where_block_default_) 94 | * 95 | * @param unique_id An unique id for where clause. When implementing multiple extensions 96 | * for a protocol, the unique_id is used to differentiate extensions. The unique_id will 97 | * be concat into the name of the extension struct variable in section, function and extension 98 | * class. 99 | * @param expression An expression that returns a bool value. In expression, You can use `self` 100 | * variable, which is the class may adopt to the extended protocol. 101 | * 102 | * @code 103 | 104 | // 0. Example of @nn_where(unique_id, expression) 105 | @nn_extension(NNHelloWorld, @nn_where(id_english, self == [NNEnglish class])) 106 | ... 107 | @end 108 | 109 | // 1. Example of @nn_where(expression) 110 | @nn_extension(NNHelloWorld, @nn_where(self == [NNEnglish class])) 111 | ... 112 | @end 113 | 114 | // 2. Example of @nn_where() 115 | @nn_extension(NNHelloWorld, @nn_where()) 116 | ... 117 | @end 118 | 119 | * @endcode 120 | * 121 | */ 122 | #define nn_where(...) nn_pop_where_(__VA_ARGS__) 123 | 124 | /** 125 | * Simplify type cast 126 | * 127 | * @discussion Used to resolve type lost in \c self at the implementation of protocol extension 128 | * 129 | * @code 130 | 131 | A *a = [A new]; 132 | @nn_exscope(a) { 133 | @nn_inscope(B *, a) 134 | a.var = @"var"; 135 | ... 136 | } 137 | 138 | * @endcode 139 | */ 140 | #define nn_exscope(VAR) nn_pop_exscope(VAR) 141 | #define nn_inscope(TYPE, VAR) nn_pop_inscope(TYPE, VAR) 142 | -------------------------------------------------------------------------------- /NNPopObjc/Public/NNPopObjcDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcDefines.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/4. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcDefines_h 10 | #define NNPopObjcDefines_h 11 | 12 | #import "NNPopObjcMacros.h" 13 | 14 | /** 15 | * Prefix of nn_extension implementation class name 16 | */ 17 | #define nn_pop_extension_prefix __NNPopObjc 18 | 19 | /** 20 | * Segment of NNPopObjc data storage location 21 | */ 22 | #define nn_pop_segment_name __DATA 23 | 24 | /** 25 | * Section in NNPopObjc data storage location segment 26 | */ 27 | #define nn_pop_section_name __nn_pop_objc__ 28 | 29 | #endif /* NNPopObjcDefines_h */ 30 | -------------------------------------------------------------------------------- /NNPopObjc/Public/NNPopObjcDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcDescription.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/2. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcDescription_h 10 | #define NNPopObjcDescription_h 11 | 12 | #import "NNPopObjcDefines.h" 13 | #import "NNPopObjcWhere.h" 14 | 15 | /// IMPLEMENTATION DETAILS FOLLOW! 16 | /// Do not write code that depends on anything below this line. 17 | 18 | typedef nn_pop_where_value_def (*where_fp)(Class clazz); 19 | 20 | typedef struct { 21 | /// Name of protocol be extended 22 | const char *protocol; 23 | /// Prefix of extension implementation class name 24 | const char *prefix; 25 | /// Name of extension implemention class 26 | const char *clazz; 27 | /// Where clause function pointer 28 | where_fp where_fp; 29 | /// Count of protocols that the adopted class should be confrom to. 30 | unsigned int confrom_protocol_count; 31 | /// Protocols that the adopted class should be confrom to. 32 | const char *confrom_protocols[20]; 33 | } nn_pop_extension_description_t; 34 | 35 | /** 36 | * __attribute__ of extension description section 37 | */ 38 | #define nn_pop_extension_description_section(section_name) __attribute__((used, section(nn_pop_metamacro_stringify(nn_pop_segment_name) "," section_name ))) 39 | 40 | /** 41 | * nn_pop_extension_description_t name, the name is prefixed as 's' and concated all args with '_' 42 | */ 43 | #define nn_pop_extension_description_name_(...) \ 44 | nn_pop_args_concat(_, s, __VA_ARGS__) \ 45 | 46 | /** 47 | * nn_pop_extension_description define 48 | */ 49 | #define nn_pop_extension_description_(prefix, protocol, where_unique_id, where_block, ...) \ 50 | const nn_pop_extension_description_t \ 51 | nn_pop_extension_description_name_(prefix, protocol, where_unique_id, __VA_ARGS__) \ 52 | nn_pop_extension_description_section(nn_pop_metamacro_stringify(nn_pop_section_name)) = \ 53 | { \ 54 | nn_pop_metamacro_stringify(protocol), \ 55 | nn_pop_metamacro_stringify(prefix), \ 56 | nn_pop_metamacro_stringify(nn_pop_extension_name_(prefix, protocol, where_unique_id, __VA_ARGS__)), \ 57 | nn_pop_extension_where_name_(prefix, protocol, where_unique_id, __VA_ARGS__), \ 58 | nn_pop_argcount(__VA_ARGS__), \ 59 | {nn_pop_confrom_protocol_names(__VA_ARGS__)}, \ 60 | }; \ 61 | 62 | /** 63 | * Convert each macro argument into a string constant, then concat all strings whth ','. 64 | * 65 | * @code 66 | 67 | nn_pop_confrom_protocol_names(a, b, c) 68 | // "a", "b", "c", 69 | 70 | * @endcode 71 | */ 72 | #define nn_pop_confrom_protocol_names(...) \ 73 | nn_pop_metamacro_if_eq(0, nn_pop_argcount(__VA_ARGS__))()\ 74 | (nn_pop_confrom_protocol_names_(__VA_ARGS__))\ 75 | 76 | #define nn_pop_confrom_protocol_names_(...) \ 77 | nn_pop_metamacro_foreach_cxt(nn_pop_confrom_protocol_name_iter,,, __VA_ARGS__) \ 78 | 79 | #define nn_pop_confrom_protocol_name_iter(INDEX, CONTEXT, VAR) \ 80 | nn_pop_metamacro_stringify(VAR), 81 | 82 | #endif /* NNPopObjcDescription_h */ 83 | -------------------------------------------------------------------------------- /NNPopObjc/Public/NNPopObjcExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcExtension.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/2. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcExtension_h 10 | #define NNPopObjcExtension_h 11 | 12 | #import "NNPopObjcDefines.h" 13 | 14 | /// IMPLEMENTATION DETAILS FOLLOW! 15 | /// Do not write code that depends on anything below this line. 16 | 17 | /** 18 | * nn_extension implementation class name, the name is concated all args with '_' 19 | */ 20 | #define nn_pop_extension_name_(...) \ 21 | nn_pop_args_concat(_, __VA_ARGS__) \ 22 | 23 | /** 24 | * nn_extension implementation 25 | */ 26 | #define nn_pop_extension_(protocol, ...) \ 27 | nn_pop_extension_where_clause_check(protocol, __VA_ARGS__) 28 | 29 | /** 30 | * Check nn_extension where clause. 3 parameters that the where clause @nn_where() expend. 31 | * If the parameter list count is less than the count of parameter that @nn_where() expend, 32 | * then fill @nn_where() as the variable parameter list input. 33 | */ 34 | #define nn_pop_extension_where_clause_check(protocol, ...) \ 35 | nn_pop_if_less(nn_pop_argcount(__VA_ARGS__), nn_pop_argcount(@nn_where())) \ 36 | (nn_pop_extension_param_fill(nn_pop_extension_prefix, protocol, @nn_where())) \ 37 | (nn_pop_extension_param_fill(nn_pop_extension_prefix, protocol, __VA_ARGS__)) \ 38 | 39 | /** 40 | * Fill nn_extension variable parameter list. 41 | */ 42 | #define nn_pop_extension_param_fill(prefix, protocol, ...) \ 43 | nn_pop_extension_expand(prefix, protocol, __VA_ARGS__) \ 44 | 45 | /** 46 | * Expand nn_extension 47 | */ 48 | #define nn_pop_extension_expand(prefix, protocol, where_keywordify, where_unique_id, where_block, ...) \ 49 | \ 50 | class NSObject; \ 51 | \ 52 | nn_pop_extension_where_(prefix, protocol, where_keywordify, where_unique_id, where_block, __VA_ARGS__)\ 53 | \ 54 | nn_pop_extension_description_(prefix, protocol, where_unique_id, where_block, __VA_ARGS__) \ 55 | \ 56 | @interface nn_pop_extension_name_(prefix, protocol, where_unique_id, __VA_ARGS__) : NSObject < protocol nn_pop_adopt_protocol(__VA_ARGS__)> \ 57 | \ 58 | @end \ 59 | \ 60 | @implementation nn_pop_extension_name_(prefix, protocol, where_unique_id, __VA_ARGS__) \ 61 | 62 | /** 63 | * Concat all args with ',' 64 | * 65 | * @code 66 | 67 | nn_pop_adopt_protocol(a, b, c) 68 | // ,a ,b ,c 69 | 70 | * @endcode 71 | */ 72 | #define nn_pop_adopt_protocol(...) \ 73 | nn_pop_metamacro_if_eq(0, nn_pop_argcount(__VA_ARGS__))()\ 74 | (nn_pop_adopt_protocol_(__VA_ARGS__))\ 75 | 76 | #define nn_pop_adopt_protocol_(...) \ 77 | nn_pop_metamacro_foreach_cxt(nn_pop_adopt_protocol_iter,,, __VA_ARGS__) \ 78 | 79 | #define nn_pop_adopt_protocol_iter(INDEX, CONTEXT, VAR) \ 80 | ,VAR \ 81 | 82 | #endif /* NNPopObjcExtension_h */ 83 | -------------------------------------------------------------------------------- /NNPopObjc/Public/NNPopObjcScope.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcScope.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/12/5. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcScope_h 10 | #define NNPopObjcScope_h 11 | 12 | /** 13 | * external scope 14 | */ 15 | #define nn_pop_exscope(VAR) \ 16 | nn_pop_scope_keywordify \ 17 | __typeof__(VAR) nn_pop_metamacro_concat(VAR, _scope_) = (VAR); 18 | 19 | /** 20 | * internal scope 21 | */ 22 | #define nn_pop_inscope(TYPE, VAR) \ 23 | nn_pop_scope_keywordify \ 24 | _Pragma("clang diagnostic push") \ 25 | _Pragma("clang diagnostic ignored \"-Wshadow\"") \ 26 | TYPE VAR = (TYPE)nn_pop_metamacro_concat(VAR, _scope_); \ 27 | _Pragma("clang diagnostic pop") 28 | 29 | /** 30 | * Inspired by libextobjc: https://github.com/jspahrsummers/libextobjc 31 | */ 32 | #if defined(DEBUG) && !defined(NDEBUG) 33 | #define nn_pop_scope_keywordify autoreleasepool {} 34 | #else 35 | #define nn_pop_scope_keywordify try {} @catch (...) {} 36 | #endif 37 | 38 | #endif /* NNPopObjcScope_h */ 39 | -------------------------------------------------------------------------------- /NNPopObjc/Public/NNPopObjcWhere.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcWhere.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/2. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcWhere_h 10 | #define NNPopObjcWhere_h 11 | 12 | #import "NNPopObjcDefines.h" 13 | 14 | /// IMPLEMENTATION DETAILS FOLLOW! 15 | /// Do not write code that depends on anything below this line. 16 | 17 | /** 18 | * nn_pop_where_block_ result type 19 | */ 20 | typedef enum : NSUInteger { 21 | nn_pop_where_value_unmatched = 0, 22 | nn_pop_where_value_matched_default, 23 | nn_pop_where_value_matched_constrained, 24 | } nn_pop_where_value_def; 25 | 26 | /** 27 | * nn_where implementation 28 | */ 29 | #define nn_pop_where_(...) \ 30 | nn_pop_metamacro_concat(nn_pop_where_, nn_pop_argcount(__VA_ARGS__))(__VA_ARGS__) 31 | 32 | /** 33 | * nn_where static function name, the name is prefixed as 'w' and concated all args with '_' 34 | */ 35 | #define nn_pop_extension_where_name_(...) \ 36 | nn_pop_args_concat(_, w, __VA_ARGS__) \ 37 | 38 | /** 39 | * nn_where static function define 40 | */ 41 | #define nn_pop_extension_where_(prefix, protocol, where_keywordify, where_unique_id, where_block, ...) \ 42 | static nn_pop_where_value_def nn_pop_extension_where_name_(prefix, protocol, where_unique_id, __VA_ARGS__)(Class self) { \ 43 | where_keywordify \ 44 | return where_block(self); \ 45 | } \ 46 | 47 | /** 48 | * nn_pop_where_ expansions 49 | */ 50 | #define nn_pop_where_0() \ 51 | nn_pop_where_keywordify, \ 52 | , \ 53 | nn_pop_where_block_(true, true) \ 54 | 55 | #define nn_pop_where_1(expression) \ 56 | nn_pop_where_keywordify, \ 57 | , \ 58 | nn_pop_where_block_(expression, false) \ 59 | 60 | #define nn_pop_where_2(unique_id, expression) \ 61 | nn_pop_where_keywordify, \ 62 | unique_id, \ 63 | nn_pop_where_block_(expression, false) \ 64 | 65 | /** 66 | * nn_pop_where_block_ expansions 67 | */ 68 | #define nn_pop_where_block_(expression, as_default) \ 69 | ^nn_pop_where_value_def(__unsafe_unretained Class self){ \ 70 | if (self == nil) { \ 71 | return nn_pop_where_value_unmatched; \ 72 | } \ 73 | BOOL is_match = ((expression) == true); \ 74 | if (is_match == false) { \ 75 | return nn_pop_where_value_unmatched; \ 76 | } \ 77 | return as_default ? nn_pop_where_value_matched_default : nn_pop_where_value_matched_constrained; \ 78 | } \ 79 | 80 | /** 81 | * Inspired by libextobjc: https://github.com/jspahrsummers/libextobjc 82 | */ 83 | #if defined(DEBUG) && !defined(NDEBUG) 84 | #define nn_pop_where_keywordify autoreleasepool {} 85 | #else 86 | #define nn_pop_where_keywordify try {} @catch (...) {} 87 | #endif 88 | 89 | #endif /* NNPopObjcWhere_h */ 90 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcInjection.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcInjection.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/10/26. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcInjection_h 10 | #define NNPopObjcInjection_h 11 | 12 | #import 13 | 14 | namespace popobjc { 15 | 16 | } // namespace popobjc 17 | 18 | #endif /* NNPopObjcInjection_h */ 19 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcInjection.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcInjection.m 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/10/26. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNPopObjcInjection.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | #import 20 | #import 21 | 22 | #import "NNPopObjcMemory.h" 23 | #import "NNPopObjcProtocol.h" 24 | #import "NNPopObjcLogging.h" 25 | #import "NNPopObjcTickTock.h" 26 | 27 | 28 | namespace popobjc { 29 | 30 | typedef struct 31 | #ifdef __LP64__ 32 | mach_header_64 33 | #else 34 | mach_header 35 | #endif 36 | nn_pop_mach_header; 37 | 38 | 39 | /// Returns a Boolean value that indicates whether a class conforms to a given protocol. 40 | /// It is same as: + (BOOL)conformsToProtocol:(Protocol *)protocol 41 | /// @param clazz The class you want to inspect. 42 | /// @param protocol A protocol. 43 | /// @param inheritLevel The class inherit level. 44 | BOOL classConformsToProtocol(Class clazz, Protocol *protocol, unsigned int *inheritLevel) { 45 | 46 | NSCAssert(clazz != nil, @"Parameter clazz cannot be nil"); 47 | NSCAssert(protocol != nil, @"Parameter protocol cannot be nil"); 48 | 49 | BOOL result = false; 50 | unsigned int level = 0; 51 | 52 | Class currentClazz = clazz; 53 | while (currentClazz) { 54 | level += 1; 55 | if (class_conformsToProtocol(currentClazz, protocol)) { 56 | result = true; 57 | } 58 | currentClazz = class_getSuperclass(currentClazz); 59 | } 60 | *inheritLevel = level; 61 | return result; 62 | } 63 | 64 | /// Returns a Boolean value that indicates whether clazz is in protocol implements. 65 | /// @param clazz A class 66 | /// @param extensionClazzes Class name list of protocol extension implementions 67 | BOOL classIsExtensionClass(Class clazz, vector &extensionClazzes) { 68 | 69 | NSCAssert(clazz != nil, @"Parameter clazz cannot be nil"); 70 | 71 | BOOL result = false; 72 | 73 | for (unsigned int i = 0; i < extensionClazzes.size(); i++) { 74 | if (clazz == objc_getClass(extensionClazzes[i])) { 75 | result = true; 76 | break; 77 | } 78 | } 79 | return result; 80 | } 81 | 82 | /// Injects extentionClass implements in to clazz 83 | /// @param clazz A class 84 | /// @param extentionClazz Extension implement class 85 | /// @param checkSupserImplement Whether the injection should check super implemention, 86 | /// if a instance mathod has been implemented by super class, then jump over the injection. 87 | void injectImplementions(Class clazz, Class extentionClazz, BOOL checkSupserImplement) { 88 | 89 | NSCAssert(clazz != nil, @"Parameter clazz cannot be nil"); 90 | NSCAssert(extentionClazz != nil, @"Parameter extentionClazz cannot be nil"); 91 | 92 | unsigned int iMethodCount = 0; 93 | Method *iMethodList = class_copyMethodList(extentionClazz, &iMethodCount); 94 | 95 | unsigned int cMethodCount = 0; 96 | Method *cMethodList = class_copyMethodList(object_getClass(extentionClazz), &cMethodCount); 97 | 98 | Class metaclazz = object_getClass(clazz); 99 | 100 | for (unsigned int i = 0; i < iMethodCount; i++) { 101 | Method method = iMethodList[i]; 102 | SEL selector = method_getName(method); 103 | 104 | if (checkSupserImplement && (class_getInstanceMethod(clazz, selector) != nil)) { 105 | continue; 106 | } 107 | 108 | IMP imp = method_getImplementation(method); 109 | const char *types = method_getTypeEncoding(method); 110 | class_addMethod(clazz, selector, imp, types); 111 | } 112 | 113 | for (unsigned int i = 0; i < cMethodCount; i++) { 114 | Method method = cMethodList[i]; 115 | SEL selector = method_getName(method); 116 | 117 | if (selector == @selector(initialize)) { 118 | continue; 119 | } 120 | 121 | if (checkSupserImplement && (class_getInstanceMethod(metaclazz, selector) != nil)) { 122 | continue; 123 | } 124 | 125 | IMP imp = method_getImplementation(method); 126 | const char *types = method_getTypeEncoding(method); 127 | class_addMethod(metaclazz, selector, imp, types); 128 | } 129 | 130 | free(iMethodList); iMethodList = NULL; 131 | free(cMethodList); cMethodList = NULL; 132 | 133 | (void)[extentionClazz class]; 134 | } 135 | 136 | /// Injects protocol extension in to clazz 137 | /// @param clazz A class 138 | /// @param extensions A nn_pop_protocol_extension_t struct list 139 | void injectProtocolExtension(Class clazz, vector &extensions) { 140 | 141 | NSCAssert(clazz != nil, @"Parameter clazz cannot be nil"); 142 | NSCAssert(extensions.size() != 0, @"Parameter protocolExtension cannot be nil"); 143 | 144 | vector defaultList; 145 | vector constrainedList; 146 | 147 | for (unsigned int i = 0; i < extensions.size(); i++) { 148 | 149 | ExtensionDescription *extension = extensions[i]; 150 | 151 | nn_pop_where_value_def matchValue = extension->where_fp(clazz); 152 | if (matchValue == nn_pop_where_value_matched_default) { 153 | defaultList.push_back(extension); 154 | } 155 | if (matchValue == nn_pop_where_value_matched_constrained) { 156 | BOOL conform = true; 157 | for (unsigned int i = 0; i < extension->confrom_protocol_count; i++) { 158 | Protocol *protocol = objc_getProtocol(extension->confrom_protocols[i]); 159 | if ([clazz conformsToProtocol:protocol] == false) { 160 | conform = false; 161 | break; 162 | } 163 | } 164 | if (conform) { 165 | constrainedList.push_back(extension); 166 | } 167 | } 168 | } 169 | 170 | __unused NSString *(^assertExtensionDesc)(vector list) = ^(vector list){ 171 | NSMutableArray *extension_names = [NSMutableArray new]; 172 | for (unsigned int i = 0; i < list.size(); i++) { 173 | [extension_names addObject:[NSString stringWithUTF8String:list[i]->clazz]]; 174 | } 175 | NSString *extensionDesc = [extension_names componentsJoinedByString:@", "]; 176 | return extensionDesc; 177 | }; 178 | NSCAssert(!(constrainedList.size() > 1), 179 | @"Matched multiple constraint protocol extensions for class %@. The matched protocol extensions: %@", @(class_getName(clazz)), assertExtensionDesc(constrainedList)); 180 | NSCAssert(!(defaultList.size() > 1), 181 | @"Matched multiple default protocol extensions for class %@. The matched protocol extensions: %@", @(class_getName(clazz)), assertExtensionDesc(defaultList)); 182 | NSCAssert(!((constrainedList.size() == 0) && (defaultList.size() == 0)), 183 | @"Unmatched to the protocol extension for class %@", @(class_getName(clazz))); 184 | 185 | if (constrainedList.size() == 1) { 186 | injectImplementions(clazz, objc_getClass(constrainedList[0]->clazz), false); 187 | } 188 | if (defaultList.size() == 1) { 189 | injectImplementions(clazz, objc_getClass(defaultList[0]->clazz), true); 190 | } 191 | 192 | defaultList.clear(); 193 | constrainedList.clear(); 194 | 195 | return; 196 | } 197 | 198 | /// Injects each protocols extension in to the corresponding class 199 | /// @param protocolExtension ProtocolExtension 200 | void injectProtocolExtensions(ProtocolExtension &protocolExtension) { 201 | 202 | POP_DLOG(INFO) << "Inject protocol extensions begin"; 203 | 204 | int classCount = objc_getClassList(NULL, 0); 205 | if (!classCount) { 206 | POP_LOG(FATAL) << "No clazzes registered with the runtime"; 207 | return; 208 | } 209 | 210 | Class *clazzes = (Class *)nn_pop_malloc((size_t)(classCount + 1) * sizeof(Class)); 211 | if (!clazzes) { 212 | POP_LOG(FATAL) << "Could not allocate space for " << classCount << " clazzes"; 213 | return; 214 | } 215 | 216 | classCount = objc_getClassList(clazzes, classCount); 217 | 218 | unordered_map>>protocolClazzesMap; 219 | // Loop all protocols 220 | int protocolCount = (int)protocolExtension.protocols.size(); 221 | for (int i = protocolCount - 1; i >= 0 ; i--) { 222 | const char *protocolName = protocolExtension.protocols[i]; 223 | Protocol *protocol = objc_getProtocol(protocolName); 224 | 225 | /* 226 | protocols: { protocol_a, protocol_b, protocol_c} 227 | classes : { class_0, class_1, class_2, class_3 } 228 | classes conforms to protocol_a: A = { class_0, class_2, class_3 } 229 | classes conforms to protocol_b: B = { class_1, class_2, class_3 } 230 | classes conforms to protocol_c: C = { class_2, class_3 } 231 | ∵ ∀c∈C, c∈A, c∈B ∴ C⊆A, C⊆B 232 | */ 233 | const char *nearestConformedProtocolName = NULL; 234 | for (int j = i + 1; j < protocolCount; j++) { 235 | const char *_conformedProtocolName = protocolExtension.protocols[j]; 236 | if (protocol_conformsToProtocol(protocol, objc_getProtocol(_conformedProtocolName))) { 237 | nearestConformedProtocolName = _conformedProtocolName; 238 | break; 239 | } 240 | } 241 | 242 | if (nearestConformedProtocolName != NULL) { // Loop clazzes that confirm nearestConformedProtocolName 243 | int nearestConformedProtocolClazzCount = (int)protocolClazzesMap[nearestConformedProtocolName].size(); 244 | for (int i = 0; i < nearestConformedProtocolClazzCount; i++) { 245 | const char *clazzName = protocolClazzesMap[nearestConformedProtocolName][i].first; 246 | Class clazz = objc_getClass(clazzName); 247 | unsigned int inheritLevel = 0; 248 | if (classConformsToProtocol(clazz, protocol, &inheritLevel)) { 249 | if (classIsExtensionClass(clazz, protocolExtension.clazzes)) { 250 | continue; 251 | } 252 | protocolClazzesMap[protocolName].push_back(make_pair(clazzName, inheritLevel)); 253 | } 254 | } 255 | } 256 | else { // Loop all clazzes 257 | for (int i = 0; i < classCount; i++) { 258 | Class clazz = clazzes[i]; 259 | const char *clazzName = class_getName(clazz); 260 | unsigned int inheritLevel = 0; 261 | if (classConformsToProtocol(clazz, protocol, &inheritLevel)) { 262 | if (classIsExtensionClass(clazz, protocolExtension.clazzes)) { 263 | continue; 264 | } 265 | protocolClazzesMap[protocolName].push_back(make_pair(clazzName, inheritLevel)); 266 | } 267 | } 268 | } 269 | // A higher inheritLevel has a higher priority 270 | sort(protocolClazzesMap[protocolName].begin(), protocolClazzesMap[protocolName].end(), [=](pair &lhs, pair &rhs) { 271 | return lhs.second > rhs.second; 272 | }); 273 | } 274 | // Inject 275 | for (int i = 0; i < protocolCount; i++) { 276 | const char *protocolName = protocolExtension.protocols[i]; 277 | for (auto clazzName : protocolClazzesMap[protocolName]) { 278 | vectorextensions = protocolExtension.extensions[protocolName]; 279 | Class clazz = objc_getClass(clazzName.first); 280 | injectProtocolExtension(clazz, extensions); 281 | } 282 | } 283 | 284 | free(clazzes); 285 | 286 | POP_DLOG(INFO) << "Inject protocol extensions end"; 287 | } 288 | 289 | /// Loads protocol extensions info from image segment 290 | /// @param loaded A section loaded callback 291 | void loadSection(std::function loaded) { 292 | 293 | const char *segment = nn_pop_metamacro_stringify(nn_pop_segment_name); 294 | const char *section = nn_pop_metamacro_stringify(nn_pop_section_name); 295 | 296 | ProtocolExtension protocolExtension; 297 | 298 | POP_DLOG(INFO) << "Load protocol extensions begin"; 299 | 300 | uint32_t c = _dyld_image_count(); 301 | for (uint32_t i = 0; i < c; i++) { 302 | nn_pop_mach_header *mhp = (nn_pop_mach_header *)_dyld_get_image_header(i); 303 | unsigned long size = 0; 304 | uintptr_t *sectionData = (uintptr_t*)getsectiondata(mhp, segment, section, &size); 305 | if (size == 0) { 306 | continue; 307 | } 308 | unsigned int sectionItemCount = (int)size / sizeof(ExtensionDescription); 309 | ExtensionDescription *sectionItems = (ExtensionDescription *)sectionData; 310 | protocolExtension.append(sectionItems, sectionItemCount); 311 | } 312 | 313 | POP_DLOG(INFO) << "Load protocol extensions end"; 314 | 315 | if (loaded) { 316 | loaded(protocolExtension); 317 | } 318 | } 319 | 320 | /// Initializer function is called by ImageLoaderMachO::doModInitFunctions at dyld project. 321 | /// @note dyld project: https://opensource.apple.com/tarballs/dyld/ 322 | /// @note mach_header is used to load the section which records the protocol extensions. 323 | /// In a library, vars paramater of __attribute__((constructor)) function can only get 324 | /// mach_header of current library. Here we need to get the mach_header from all libraries 325 | /// through _dyld_get_image_header function. 326 | /// @note Integration using the static library pattern, in the linking process, this function 327 | /// is not referenced, so it will be entirely ignored. 328 | /// To prevent this, you need add '-all_load' or '-force_load (path_to_static_archive_library)' 329 | /// parameter for Xcode > Build Settings > Other Linker Flags. 330 | __attribute__((constructor)) void initializer(int argc, 331 | const char **argv, 332 | const char **envp, 333 | const char **apple, 334 | const void* vars) { 335 | POP_DLOG(INFO) << "Tick: " << (long)(TickTock::Tick() * 1000); 336 | loadSection([](ProtocolExtension &protocolExtensions) { 337 | injectProtocolExtensions(protocolExtensions); 338 | }); 339 | POP_DLOG(INFO) << "Tock: " << TickTock::Tock(); 340 | } 341 | 342 | } // namespace popobjc 343 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcLogging.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcLogging.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/18. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #include 12 | 13 | namespace popobjc { 14 | 15 | typedef int LogSeverity; 16 | 17 | // Default log levels. Negative values can be used for verbose log levels. 18 | constexpr LogSeverity LOG_SEVERITY_INFO = 0; 19 | constexpr LogSeverity LOG_SEVERITY_WARNING = 1; 20 | constexpr LogSeverity LOG_SEVERITY_ERROR = 2; 21 | constexpr LogSeverity LOG_SEVERITY_FATAL = 3; 22 | constexpr LogSeverity LOG_SEVERITY_NUM_SEVERITIES = 4; 23 | 24 | // LOG_SEVERITY_DFATAL is LOG_SEVERITY_FATAL in debug mode, ERROR in normal mode 25 | #ifdef DEBUG 26 | const LogSeverity LOG_SEVERITY_DFATAL = LOG_SEVERITY_FATAL; 27 | #else 28 | const LogSeverity LOG_SEVERITY_DFATAL = LOG_SEVERITY_ERROR; 29 | #endif 30 | 31 | 32 | // Settings which control the behavior of popobjc logging. 33 | struct LogSettings { 34 | // The minimum logging level. 35 | private: 36 | LogSeverity _minLogLevel = LOG_SEVERITY_INFO; 37 | 38 | public: 39 | int minLogLevel(); 40 | void minLogLevel(LogSeverity minLogLevel); 41 | }; 42 | 43 | 44 | class LogMessageVoidify { 45 | public: 46 | void operator&(std::ostream&) {} 47 | }; 48 | 49 | 50 | class LogMessage { 51 | public: 52 | LogMessage(LogSeverity severity, 53 | const char* file, 54 | int line, 55 | const char* condition); 56 | ~LogMessage(); 57 | 58 | std::ostream& stream() { return stream_; } 59 | 60 | private: 61 | std::ostringstream stream_; 62 | const LogSeverity severity_; 63 | const char* file_; 64 | const int line_; 65 | 66 | // Disallows copy assign and move 67 | LogMessage(const LogMessage&) = delete; 68 | LogMessage& operator=(const LogMessage&) = delete; 69 | }; 70 | 71 | // Gets the POP_VLOG default verbosity level. 72 | int GetVlogVerbosity(); 73 | 74 | // Returns true if |severity| is at or above the current minimum log level. 75 | // LOG_SEVERITY_FATAL and above is always true. 76 | bool ShouldCreateLogMessage(LogSeverity severity); 77 | 78 | 79 | namespace state { 80 | 81 | extern LogSettings settings; 82 | 83 | } // namespace state 84 | 85 | } // namespace popobjc 86 | 87 | 88 | #define POP_LOG_STREAM(severity) \ 89 | ::popobjc::LogMessage(::popobjc::LOG_SEVERITY_##severity, __FILE__, __LINE__, nullptr).stream() 90 | 91 | #define POP_LAZY_STREAM(stream, condition) \ 92 | !(condition) ? (void)0 : ::popobjc::LogMessageVoidify() & (stream) 93 | 94 | #define POP_EAT_STREAM_PARAMETERS(ignored) \ 95 | true || (ignored) \ 96 | ? (void)0 \ 97 | : ::popobjc::LogMessageVoidify() & \ 98 | ::popobjc::LogMessage(::popobjc::LOG_SEVERITY_FATAL, 0, 0, nullptr).stream() 99 | 100 | #define POP_LOG_IS_ON(severity) \ 101 | (::popobjc::ShouldCreateLogMessage(::popobjc::LOG_SEVERITY_##severity)) 102 | 103 | #define POP_LOG(severity) \ 104 | POP_LAZY_STREAM(POP_LOG_STREAM(severity), POP_LOG_IS_ON(severity)) 105 | 106 | #define POP_CHECK(condition) \ 107 | POP_LAZY_STREAM( \ 108 | ::popobjc::LogMessage(::popobjc::LOG_SEVERITY_FATAL, __FILE__, __LINE__, #condition).stream(), \ 109 | !(condition)) 110 | 111 | #define POP_VLOG_IS_ON(verbose_level) \ 112 | ((verbose_level) <= ::popobjc::GetVlogVerbosity()) 113 | 114 | // The VLOG macros log with negative verbosities. 115 | #define POP_VLOG_STREAM(verbose_level) \ 116 | ::popobjc::LogMessage(-verbose_level, __FILE__, __LINE__, nullptr).stream() 117 | 118 | #define POP_VLOG(verbose_level) \ 119 | POP_LAZY_STREAM(POP_VLOG_STREAM(verbose_level), POP_VLOG_IS_ON(verbose_level)) 120 | 121 | #ifdef DEBUG 122 | #define POP_DLOG(severity) POP_LOG(severity) 123 | #define POP_DCHECK(condition) POP_CHECK(condition) 124 | #else 125 | #define POP_DLOG(severity) POP_EAT_STREAM_PARAMETERS(true) 126 | #define POP_DCHECK(condition) POP_EAT_STREAM_PARAMETERS(condition) 127 | #endif 128 | 129 | #define POP_NOTREACHED() POP_DCHECK(false) 130 | 131 | #define POP_NOTIMPLEMENTED() \ 132 | POP_LOG(ERROR) << "Not implemented in: " << __PRETTY_FUNCTION__ 133 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcLogging.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcLogging.m 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/18. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNPopObjcLogging.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace popobjc { 17 | 18 | int LogSettings::minLogLevel() { 19 | return std::min(this->_minLogLevel, LOG_SEVERITY_FATAL); 20 | } 21 | void LogSettings::minLogLevel(LogSeverity minLogLevel) { 22 | this->_minLogLevel = std::min(LOG_SEVERITY_FATAL, minLogLevel); 23 | } 24 | 25 | 26 | namespace { 27 | 28 | const char* const kLogSeverityNames[LOG_SEVERITY_NUM_SEVERITIES] = {"INFO", "WARNING", 29 | "ERROR", "FATAL"}; 30 | 31 | const char* GetNameForLogSeverity(LogSeverity severity) { 32 | if (severity >= LOG_SEVERITY_INFO && severity < LOG_SEVERITY_NUM_SEVERITIES) 33 | return kLogSeverityNames[severity]; 34 | return "UNKNOWN"; 35 | } 36 | 37 | const char* StripDots(const char* path) { 38 | while (strncmp(path, "../", 3) == 0) 39 | path += 3; 40 | return path; 41 | } 42 | 43 | const char* StripPath(const char* path) { 44 | auto* p = strrchr(path, '/'); 45 | if (p) 46 | return p + 1; 47 | else 48 | return path; 49 | } 50 | 51 | } // namespace 52 | 53 | 54 | LogMessage::LogMessage(LogSeverity severity, 55 | const char* file, 56 | int line, 57 | const char* condition) 58 | : severity_(severity), file_(file), line_(line) { 59 | 60 | stream_ << "["; 61 | 62 | if (severity >= LOG_SEVERITY_INFO) { 63 | stream_ << GetNameForLogSeverity(severity); 64 | } 65 | else { 66 | stream_ << "VERBOSE" << -severity; 67 | } 68 | 69 | stream_ << ":" << (severity > LOG_SEVERITY_INFO ? StripDots(file_) : StripPath(file_)) << "(" << line_ << ")] "; 70 | 71 | if (condition) { 72 | stream_ << "Check failed: " << condition << ". "; 73 | } 74 | } 75 | 76 | LogMessage::~LogMessage() { 77 | stream_ << std::endl; 78 | 79 | syslog(LOG_ALERT, "%s", stream_.str().c_str()); 80 | 81 | if (severity_ >= LOG_SEVERITY_FATAL) { 82 | abort(); 83 | } 84 | } 85 | 86 | int GetVlogVerbosity() { 87 | return std::max(-1, LOG_SEVERITY_INFO - state::settings.minLogLevel()); 88 | } 89 | 90 | bool ShouldCreateLogMessage(LogSeverity severity) { 91 | return severity >= state::settings.minLogLevel(); 92 | } 93 | 94 | 95 | namespace state { 96 | 97 | LogSettings settings; 98 | 99 | } // namespace state 100 | 101 | } // namespace popobjc 102 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcMemory.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcMemory.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/14. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcMemory_h 10 | #define NNPopObjcMemory_h 11 | 12 | #import 13 | 14 | namespace popobjc { 15 | 16 | void *nn_pop_malloc(size_t size); 17 | 18 | } // namespace popobjc 19 | 20 | #endif /* NNPopObjcMemory_h */ 21 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcMemory.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcMemory.m 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/14. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNPopObjcMemory.h" 10 | 11 | namespace popobjc { 12 | 13 | void *nn_pop_malloc(size_t size) { 14 | void *_ptr = malloc(size); 15 | memset(_ptr, 0, size); // fix: EXC_BAD_ACCESS 16 | return _ptr; 17 | } 18 | 19 | } // namespace popobjc 20 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcProtocol.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #ifndef NNPopObjcProtocol_h 10 | #define NNPopObjcProtocol_h 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #import "NNPopObjcDescription.h" 18 | 19 | using namespace std; 20 | 21 | namespace popobjc { 22 | 23 | /// Extension description struct. 24 | typedef nn_pop_extension_description_t ExtensionDescription; 25 | 26 | /// Protocol extension struct. 27 | struct ProtocolExtension { 28 | 29 | public: 30 | /// Protocol be extended. 31 | vector protocols; 32 | /// Protocol extension descriptions. 33 | unordered_map> extensions; 34 | /// All protocol extension classes. 35 | vector clazzes; 36 | 37 | void append(ExtensionDescription *extensionDescription, unsigned int count); 38 | 39 | ProtocolExtension() = default; 40 | ~ProtocolExtension(); 41 | }; 42 | 43 | } 44 | 45 | #endif /* NNPopObjcProtocol_h */ 46 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcProtocol.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcProtocol.m 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2019/11/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNPopObjcProtocol.h" 10 | #import 11 | #import "NNPopObjcLogging.h" 12 | 13 | namespace popobjc { 14 | 15 | void ProtocolExtension::append(ExtensionDescription *extensionDescription, unsigned int count) { 16 | 17 | for (unsigned int i = 0; i < count; i++) { 18 | ExtensionDescription *_extensionDescription = &extensionDescription[i]; 19 | this->extensions[_extensionDescription->protocol].push_back(_extensionDescription); 20 | this->clazzes.push_back(_extensionDescription->clazz); 21 | } 22 | this->protocols.clear(); 23 | for (auto extension : this->extensions) { 24 | this->protocols.push_back(extension.first); 25 | } 26 | 27 | // Sort by protocol's priority,reverse order 28 | std::sort(this->protocols.begin(), this->protocols.end(), [=](const char *lhs, const char *rhs) { 29 | // A higher return value here means a higher priority 30 | std::function protocolPriority = [=](const char *protocol) { 31 | int runningTotal = 0; 32 | for (auto extension : this->extensions) { 33 | if (extension.first == protocol) { 34 | continue; 35 | } 36 | if (protocol_conformsToProtocol(objc_getProtocol(protocol), objc_getProtocol(extension.first))) { 37 | runningTotal++; 38 | } 39 | } 40 | return runningTotal; 41 | }; 42 | int l_protocolPriority = protocolPriority(lhs); 43 | int r_protocolPriority = protocolPriority(rhs); 44 | return l_protocolPriority > r_protocolPriority; 45 | }); 46 | } 47 | 48 | ProtocolExtension::~ProtocolExtension() { 49 | 50 | } 51 | 52 | } // namespace popobjc 53 | 54 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcTickTock.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcTickTock.h 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | using namespace std; 14 | 15 | #define TICK popobjc::TickTock::Tick(); 16 | #define TOCK popobjc::TickTock::Tock(); 17 | 18 | namespace popobjc { 19 | 20 | namespace TickTock { 21 | 22 | class TickTock { 23 | public: 24 | NSTimeInterval tick; 25 | NSTimeInterval tock; 26 | TickTock(NSTimeInterval tick, NSTimeInterval tock) : tick(tick), tock(tock) {}; 27 | friend ostream &operator<<(ostream &output, const TickTock &thiz); 28 | }; 29 | 30 | inline ostream &operator<< (ostream &output, const TickTock &thiz) { 31 | NSTimeInterval tick = thiz.tick * 1000; 32 | NSTimeInterval tock = thiz.tock * 1000; 33 | output << fixed << setprecision(2); 34 | output << "total time: " << tock - tick << " milliseconds" << " "; 35 | output << "tick: " << (long)tick << " "; 36 | output << "tock: " << (long)tock; 37 | return output; 38 | } 39 | 40 | NSTimeInterval Tick(); 41 | TickTock Tock(); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /NNPopObjc/Source/NNPopObjcTickTock.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcTickTock.m 3 | // NNPopObjc 4 | // 5 | // Created by GuHaijun on 2020/2/23. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNPopObjcTickTock.h" 10 | #import 11 | 12 | using namespace std; 13 | 14 | namespace popobjc { 15 | 16 | namespace TickTock { 17 | 18 | stack ticks; 19 | 20 | NSTimeInterval Tick() { 21 | NSTimeInterval tick = [[NSDate date] timeIntervalSince1970]; 22 | ticks.push(tick); 23 | return tick; 24 | } 25 | 26 | TickTock Tock() { 27 | NSTimeInterval tock = [[NSDate date] timeIntervalSince1970]; 28 | NSTimeInterval tick = ({ 29 | NSTimeInterval _tick; 30 | if (!ticks.empty()) { 31 | _tick = ticks.top(); ticks.pop(); 32 | } 33 | else { 34 | _tick = -1.0; 35 | } 36 | _tick; 37 | }); 38 | return TickTock(tick, tock); 39 | } 40 | 41 | } // namespace TickTock 42 | 43 | } // namespace popobjc 44 | -------------------------------------------------------------------------------- /NNPopObjcTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNPopObjcTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNPopObjcTests.m 3 | // NNPopObjcTests 4 | // 5 | // Created by GuHaijun on 2019/10/3. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "NNTestProtocol.h" 12 | #import "NNTestClassCase0.h" 13 | #import "NNTestClassCase1.h" 14 | #import "NNTestClassCase2.h" 15 | #import "NNTestClassCase3.h" 16 | #import "NNTestClassCase4.h" 17 | #import "NNTestClassCase5.h" 18 | #import "NNTestClassCase6.h" 19 | 20 | @interface NSString (XCTAssert) 21 | 22 | - (BOOL)xct_isEqualToString:(NSString *)string; 23 | 24 | @end 25 | 26 | @implementation NSString (XCTAssert) 27 | 28 | - (BOOL)xct_isEqualToString:(NSString *)string { 29 | return [self isEqualToString:string]; 30 | } 31 | 32 | @end 33 | 34 | @interface NNPopObjcTests : XCTestCase 35 | 36 | @end 37 | 38 | @implementation NNPopObjcTests 39 | 40 | - (void)setUp { 41 | // Put setup code here. This method is called before the invocation of each test method in the class. 42 | } 43 | 44 | - (void)tearDown { 45 | // Put teardown code here. This method is called after the invocation of each test method in the class. 46 | } 47 | 48 | - (void)testCase0 { 49 | // NNTestClassCase0 50 | { 51 | Class clazz = [NNTestClassCase0 class]; 52 | XCTAssertFalse([clazz respondsToSelector:@selector(nameOfClass)]); 53 | XCTAssertFalse([[clazz new] respondsToSelector:@selector(nameOfClass)]); 54 | XCTAssertFalse([[clazz new] respondsToSelector:@selector(stringValue)]); 55 | XCTAssertFalse([[clazz new] respondsToSelector:@selector(setStringValue:)]); 56 | } 57 | } 58 | 59 | - (void)testCase1 { 60 | // NNTestClassCase10 61 | { 62 | Class clazz = [NNTestClassCase10 class]; 63 | NSString *clazzName = NSStringFromClass(clazz); 64 | // + nameOfClass 65 | { 66 | NSString *v = [clazz nameOfClass]; 67 | XCTAssertTrue([clazzName isEqualToString:v]); 68 | NNTestTrack *track = v.track; 69 | XCTAssertTrue({ 70 | [track.stack->top().implmentClass 71 | xct_isEqualToString: 72 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 73 | }); 74 | } 75 | // - nameOfClass 76 | { 77 | NSString *v = [[clazz new] nameOfClass]; 78 | XCTAssertTrue([clazzName isEqualToString:v]); 79 | NNTestTrack *track = v.track; 80 | XCTAssertTrue({ 81 | [track.stack->top().implmentClass 82 | xct_isEqualToString: 83 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 84 | }); 85 | } 86 | } 87 | // NNTestClassCase11 88 | { 89 | Class clazz = [NNTestClassCase11 class]; 90 | NSString *clazzName = NSStringFromClass(clazz); 91 | // + nameOfClass 92 | { 93 | NSString *v = [clazz nameOfClass]; 94 | XCTAssertTrue([clazzName isEqualToString:v]); 95 | NNTestTrack *track = v.track; 96 | XCTAssertTrue({ 97 | [track.stack->top().implmentClass 98 | xct_isEqualToString: 99 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 100 | }); 101 | } 102 | // - nameOfClass 103 | { 104 | NSString *v = [[clazz new] nameOfClass]; 105 | XCTAssertTrue([clazzName isEqualToString:v]); 106 | NNTestTrack *track = v.track; 107 | XCTAssertTrue({ 108 | [track.stack->top().implmentClass 109 | xct_isEqualToString: 110 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 111 | }); 112 | } 113 | { 114 | id obj = [clazz new]; 115 | obj.stringValue = clazzName; 116 | NSString *v = obj.stringValue; 117 | XCTAssertTrue([clazzName isEqualToString:v]); 118 | NNTestTrack *track = v.track; 119 | XCTAssertTrue({ 120 | [track.stack->top().implmentClass 121 | xct_isEqualToString: 122 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,,)))]; 123 | }); 124 | XCTAssertTrue({ 125 | [track.stack->top().methodName 126 | xct_isEqualToString: 127 | @"stringValue"]; 128 | }); 129 | track.stack->pop(); 130 | XCTAssertTrue({ 131 | [track.stack->top().implmentClass 132 | xct_isEqualToString: 133 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,,)))]; 134 | }); 135 | XCTAssertTrue({ 136 | [track.stack->top().methodName 137 | xct_isEqualToString: 138 | @"setStringValue:"]; 139 | }); 140 | } 141 | } 142 | } 143 | 144 | - (void)testCase2 { 145 | // NNTestClassCase21 146 | { 147 | Class clazz = [NNTestClassCase21 class]; 148 | NSString *clazzName = NSStringFromClass(clazz); 149 | // + nameOfClass 150 | { 151 | NSString *v = [clazz nameOfClass]; 152 | XCTAssertTrue([clazzName isEqualToString:v]); 153 | NNTestTrack *track = v.track; 154 | XCTAssertTrue({ 155 | [track.stack->top().implmentClass 156 | xct_isEqualToString: 157 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 158 | }); 159 | } 160 | // - nameOfClass 161 | { 162 | NSString *v = [[clazz new] nameOfClass]; 163 | XCTAssertTrue([clazzName isEqualToString:v]); 164 | NNTestTrack *track = v.track; 165 | XCTAssertTrue({ 166 | [track.stack->top().implmentClass 167 | xct_isEqualToString: 168 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 169 | }); 170 | } 171 | { 172 | id obj = [clazz new]; 173 | obj.stringValue = clazzName; 174 | NSString *v = obj.stringValue; 175 | XCTAssertTrue([clazzName isEqualToString:v]); 176 | NNTestTrack *track = v.track; 177 | XCTAssertTrue({ 178 | [track.stack->top().implmentClass 179 | xct_isEqualToString: 180 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,,)))]; 181 | }); 182 | XCTAssertTrue({ 183 | [track.stack->top().methodName 184 | xct_isEqualToString: 185 | @"stringValue"]; 186 | }); 187 | track.stack->pop(); 188 | XCTAssertTrue({ 189 | [track.stack->top().implmentClass 190 | xct_isEqualToString: 191 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,,)))]; 192 | }); 193 | XCTAssertTrue({ 194 | [track.stack->top().methodName 195 | xct_isEqualToString: 196 | @"setStringValue:"]; 197 | }); 198 | } 199 | } 200 | } 201 | 202 | - (void)testCase3 { 203 | // NNTestClassCase30 204 | { 205 | Class clazz = [NNTestClassCase30 class]; 206 | NSString *clazzName = NSStringFromClass(clazz); 207 | // + nameOfClass 208 | { 209 | NSString *v = [clazz nameOfClass]; 210 | XCTAssertTrue([clazzName isEqualToString:v]); 211 | NNTestTrack *track = v.track; 212 | XCTAssertTrue({ 213 | [track.stack->top().implmentClass 214 | xct_isEqualToString: 215 | clazzName]; 216 | }); 217 | } 218 | // - nameOfClass 219 | { 220 | NSString *v = [[clazz new] nameOfClass]; 221 | XCTAssertTrue([clazzName isEqualToString:v]); 222 | NNTestTrack *track = v.track; 223 | XCTAssertTrue({ 224 | [track.stack->top().implmentClass 225 | xct_isEqualToString: 226 | clazzName]; 227 | }); 228 | } 229 | { 230 | id obj = [clazz new]; 231 | obj.stringValue = clazzName; 232 | NSString *v = obj.stringValue; 233 | XCTAssertTrue([clazzName isEqualToString:v]); 234 | NNTestTrack *track = v.track; 235 | XCTAssertTrue({ 236 | [track.stack->top().implmentClass 237 | xct_isEqualToString: 238 | clazzName]; 239 | }); 240 | XCTAssertTrue({ 241 | [track.stack->top().methodName 242 | xct_isEqualToString: 243 | @"stringValue"]; 244 | }); 245 | track.stack->pop(); 246 | XCTAssertTrue({ 247 | [track.stack->top().implmentClass 248 | xct_isEqualToString: 249 | clazzName]; 250 | }); 251 | XCTAssertTrue({ 252 | [track.stack->top().methodName 253 | xct_isEqualToString: 254 | @"setStringValue:"]; 255 | }); 256 | } 257 | } 258 | // NNTestClassCase31 259 | { 260 | Class clazz = [NNTestClassCase31 class]; 261 | NSString *clazzName = NSStringFromClass(clazz); 262 | // + nameOfClass 263 | { 264 | NSString *v = [clazz nameOfClass]; 265 | XCTAssertTrue([clazzName isEqualToString:v]); 266 | NNTestTrack *track = v.track; 267 | XCTAssertTrue({ 268 | [track.stack->top().implmentClass 269 | xct_isEqualToString: 270 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 271 | }); 272 | } 273 | // - nameOfClass 274 | { 275 | NSString *v = [[clazz new] nameOfClass]; 276 | XCTAssertTrue([clazzName isEqualToString:v]); 277 | NNTestTrack *track = v.track; 278 | XCTAssertTrue({ 279 | [track.stack->top().implmentClass 280 | xct_isEqualToString: 281 | clazzName]; 282 | }); 283 | } 284 | { 285 | id obj = [clazz new]; 286 | obj.stringValue = clazzName; 287 | NSString *v = obj.stringValue; 288 | XCTAssertTrue([clazzName isEqualToString:v]); 289 | NNTestTrack *track = v.track; 290 | XCTAssertTrue({ 291 | [track.stack->top().implmentClass 292 | xct_isEqualToString: 293 | clazzName]; 294 | }); 295 | XCTAssertTrue({ 296 | [track.stack->top().methodName 297 | xct_isEqualToString: 298 | @"stringValue"]; 299 | }); 300 | track.stack->pop(); 301 | XCTAssertTrue({ 302 | [track.stack->top().implmentClass 303 | xct_isEqualToString: 304 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,,)))]; 305 | }); 306 | XCTAssertTrue({ 307 | [track.stack->top().methodName 308 | xct_isEqualToString: 309 | @"setStringValue:"]; 310 | }); 311 | } 312 | } 313 | } 314 | 315 | - (void)testCase4 { 316 | // NNTestClassCase40 317 | { 318 | Class clazz = [NNTestClassCase40 class]; 319 | NSString *clazzName = NSStringFromClass(clazz); 320 | // + nameOfClass 321 | { 322 | NSString *v = [clazz nameOfClass]; 323 | XCTAssertTrue([clazzName isEqualToString:v]); 324 | NNTestTrack *track = v.track; 325 | XCTAssertTrue({ 326 | [track.stack->top().implmentClass 327 | xct_isEqualToString: 328 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol, NNTestClassCase40Protocol,)))]; 329 | }); 330 | } 331 | // - nameOfClass 332 | { 333 | NSString *v = [[clazz new] nameOfClass]; 334 | XCTAssertTrue([clazzName isEqualToString:v]); 335 | NNTestTrack *track = v.track; 336 | XCTAssertTrue({ 337 | [track.stack->top().implmentClass 338 | xct_isEqualToString: 339 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 340 | }); 341 | } 342 | { 343 | id obj = [clazz new]; 344 | obj.stringValue = clazzName; 345 | NSString *v = obj.stringValue; 346 | XCTAssertTrue([clazzName isEqualToString:v]); 347 | NNTestTrack *track = v.track; 348 | XCTAssertTrue({ 349 | [track.stack->top().implmentClass 350 | xct_isEqualToString: 351 | clazzName]; 352 | }); 353 | XCTAssertTrue({ 354 | [track.stack->top().methodName 355 | xct_isEqualToString: 356 | @"stringValue"]; 357 | }); 358 | track.stack->pop(); 359 | XCTAssertTrue({ 360 | [track.stack->top().implmentClass 361 | xct_isEqualToString: 362 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol, NNTestClassCase40Protocol,)))]; 363 | }); 364 | XCTAssertTrue({ 365 | [track.stack->top().methodName 366 | xct_isEqualToString: 367 | @"setStringValue:"]; 368 | }); 369 | } 370 | } 371 | // NNTestClassCase41 372 | { 373 | Class clazz = [NNTestClassCase41 class]; 374 | NSString *clazzName = NSStringFromClass(clazz); 375 | // + nameOfClass 376 | { 377 | NSString *v = [clazz nameOfClass]; 378 | XCTAssertTrue([clazzName isEqualToString:v]); 379 | NNTestTrack *track = v.track; 380 | XCTAssertTrue({ 381 | [track.stack->top().implmentClass 382 | xct_isEqualToString: 383 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 384 | }); 385 | } 386 | // - nameOfClass 387 | { 388 | NSString *v = [[clazz new] nameOfClass]; 389 | XCTAssertTrue([clazzName isEqualToString:v]); 390 | NNTestTrack *track = v.track; 391 | XCTAssertTrue({ 392 | [track.stack->top().implmentClass 393 | xct_isEqualToString: 394 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,, NNTestClassCase41Protocol)))]; 395 | }); 396 | } 397 | { 398 | id obj = [clazz new]; 399 | obj.stringValue = clazzName; 400 | NSString *v = obj.stringValue; 401 | XCTAssertTrue([clazzName isEqualToString:v]); 402 | NNTestTrack *track = v.track; 403 | XCTAssertTrue({ 404 | [track.stack->top().implmentClass 405 | xct_isEqualToString: 406 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,, NNTestClassCase41Protocol)))]; 407 | }); 408 | XCTAssertTrue({ 409 | [track.stack->top().methodName 410 | xct_isEqualToString: 411 | @"stringValue"]; 412 | }); 413 | track.stack->pop(); 414 | XCTAssertTrue({ 415 | [track.stack->top().implmentClass 416 | xct_isEqualToString: 417 | clazzName]; 418 | }); 419 | XCTAssertTrue({ 420 | [track.stack->top().methodName 421 | xct_isEqualToString: 422 | @"setStringValue:"]; 423 | }); 424 | } 425 | } 426 | // NNTestClassCase42 427 | { 428 | Class clazz = [NNTestClassCase42 class]; 429 | NSString *clazzName = NSStringFromClass(clazz); 430 | // + nameOfClass 431 | { 432 | NSString *v = [clazz nameOfClass]; 433 | XCTAssertTrue([clazzName isEqualToString:v]); 434 | NNTestTrack *track = v.track; 435 | XCTAssertTrue({ 436 | [track.stack->top().implmentClass 437 | xct_isEqualToString: 438 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 439 | }); 440 | } 441 | // - nameOfClass 442 | { 443 | NSString *v = [[clazz new] nameOfClass]; 444 | XCTAssertTrue([clazzName isEqualToString:v]); 445 | NNTestTrack *track = v.track; 446 | XCTAssertTrue({ 447 | [track.stack->top().implmentClass 448 | xct_isEqualToString: 449 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 450 | }); 451 | } 452 | { 453 | id obj = [clazz new]; 454 | obj.stringValue = clazzName; 455 | __unused NSString *v = obj.stringValue; 456 | XCTAssertTrue([clazzName isEqualToString:v]); 457 | NNTestTrack *track = v.track; 458 | XCTAssertTrue({ 459 | [track.stack->top().implmentClass 460 | xct_isEqualToString: 461 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,, NNTestClassCase420Protocol, NNTestClassCase421Protocol)))]; 462 | }); 463 | XCTAssertTrue({ 464 | [track.stack->top().methodName 465 | xct_isEqualToString: 466 | @"stringValue"]; 467 | }); 468 | track.stack->pop(); 469 | XCTAssertTrue({ 470 | [track.stack->top().implmentClass 471 | xct_isEqualToString: 472 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol,, NNTestClassCase420Protocol, NNTestClassCase421Protocol)))]; 473 | }); 474 | XCTAssertTrue({ 475 | [track.stack->top().methodName 476 | xct_isEqualToString: 477 | @"setStringValue:"]; 478 | }); 479 | } 480 | } 481 | } 482 | 483 | - (void)testCase5 { 484 | // NNTestClassCase50 485 | { 486 | __unused id obj = [NNTestClassCase50 new]; 487 | NNTestTrack *track = case50Track.track; 488 | XCTAssertTrue({ 489 | [track.stack->top().invokeClass 490 | xct_isEqualToString: 491 | @"NNTestClassCase50"]; 492 | }); 493 | XCTAssertTrue({ 494 | [track.stack->top().implmentClass 495 | xct_isEqualToString: 496 | @"NNTestClassCase50"]; 497 | }); 498 | XCTAssertTrue({ 499 | track.stack->top().methodType == NNTestMethodTypeClass; 500 | }); 501 | XCTAssertTrue({ 502 | [track.stack->top().methodName 503 | xct_isEqualToString: 504 | @"initialize"]; 505 | }); 506 | } 507 | // NNTestClassCase51 508 | { 509 | // initialize method does not need injection 510 | __unused id obj = [NNTestClassCase51 new]; 511 | NNTestTrack *track = case51Track.track; 512 | XCTAssertTrue({ 513 | [track.stack->top().invokeClass 514 | xct_isEqualToString: 515 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol, NNTestClassCase50,)))]; 516 | }); 517 | XCTAssertTrue({ 518 | [track.stack->top().implmentClass 519 | xct_isEqualToString: 520 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestSubProtocol, NNTestClassCase50,)))]; 521 | }); 522 | XCTAssertTrue({ 523 | track.stack->top().methodType == NNTestMethodTypeClass; 524 | }); 525 | XCTAssertTrue({ 526 | [track.stack->top().methodName 527 | xct_isEqualToString: 528 | @"initialize"]; 529 | }); 530 | } 531 | } 532 | 533 | - (void)testCase6 { 534 | // NNTestClassCase60 535 | { 536 | Class clazz = [NNTestClassCase60 class]; 537 | NSString *clazzName = NSStringFromClass(clazz); 538 | // + nameOfClass 539 | { 540 | NSString *v = [clazz nameOfClass]; 541 | XCTAssertTrue([clazzName isEqualToString:v]); 542 | NNTestTrack *track = v.track; 543 | XCTAssertTrue({ 544 | [track.stack->top().implmentClass 545 | xct_isEqualToString: 546 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 547 | }); 548 | } 549 | // - nameOfClass 550 | { 551 | NSString *v = [[clazz new] nameOfClass]; 552 | XCTAssertTrue([clazzName isEqualToString:v]); 553 | NNTestTrack *track = v.track; 554 | XCTAssertTrue({ 555 | [track.stack->top().implmentClass 556 | xct_isEqualToString: 557 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 558 | }); 559 | } 560 | } 561 | // NNTestClassCase61 562 | { 563 | Class clazz = [NNTestClassCase61 class]; 564 | NSString *clazzName = NSStringFromClass(clazz); 565 | // + nameOfClass 566 | { 567 | NSString *v = [clazz nameOfClass]; 568 | XCTAssertTrue([clazzName isEqualToString:v]); 569 | NNTestTrack *track = v.track; 570 | XCTAssertTrue({ 571 | [track.stack->top().implmentClass 572 | xct_isEqualToString: 573 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 574 | }); 575 | } 576 | // - nameOfClass 577 | { 578 | NSString *v = [[clazz new] nameOfClass]; 579 | XCTAssertTrue([clazzName isEqualToString:v]); 580 | NNTestTrack *track = v.track; 581 | XCTAssertTrue({ 582 | [track.stack->top().implmentClass 583 | xct_isEqualToString: 584 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol,,)))]; 585 | }); 586 | } 587 | } 588 | // NNTestClassCase62 589 | { 590 | Class clazz = [NNTestClassCase62 class]; 591 | NSString *clazzName = NSStringFromClass(clazz); 592 | // + nameOfClass 593 | { 594 | NSString *v = [clazz nameOfClass]; 595 | XCTAssertTrue([clazzName isEqualToString:v]); 596 | NNTestTrack *track = v.track; 597 | XCTAssertTrue({ 598 | [track.stack->top().implmentClass 599 | xct_isEqualToString: 600 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol, NNTestClassCase62,)))]; 601 | }); 602 | } 603 | // - nameOfClass 604 | { 605 | NSString *v = [[clazz new] nameOfClass]; 606 | XCTAssertTrue([clazzName isEqualToString:v]); 607 | NNTestTrack *track = v.track; 608 | XCTAssertTrue({ 609 | [track.stack->top().implmentClass 610 | xct_isEqualToString: 611 | @(nn_pop_metamacro_stringify(nn_pop_extension_name_(nn_pop_extension_prefix, NNTestProtocol, NNTestClassCase62,)))]; 612 | }); 613 | } 614 | } 615 | } 616 | 617 | - (void)testPerformanceExample { 618 | // This is an example of a performance test case. 619 | [self measureBlock:^{ 620 | // Put the code you want to measure the time of here. 621 | }]; 622 | } 623 | 624 | @end 625 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase0.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase0.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/16. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NNTestClassCase0 : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase0.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase0.m 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/16. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase0.h" 10 | 11 | @implementation NNTestClassCase0 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase1.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase1.h 3 | // NNPopObjcTests 4 | // 5 | // Created by GuHaijun on 2019/12/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | @interface NNTestClassCase10 : NNTestClassCase0 14 | 15 | @end 16 | 17 | 18 | @interface NNTestClassCase11 : NNTestClassCase0 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase1.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase1.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by GuHaijun on 2019/12/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase1.h" 10 | 11 | 12 | @implementation NNTestClassCase10 13 | 14 | @end 15 | 16 | 17 | @implementation NNTestClassCase11 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase2.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase2.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/12. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | @interface NNTestClassCase20 : NNTestClassCase0 14 | 15 | @end 16 | 17 | 18 | @interface NNTestClassCase21 : NNTestClassCase20 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase2.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase2.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/12. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase2.h" 10 | 11 | 12 | @implementation NNTestClassCase20 13 | 14 | @end 15 | 16 | 17 | @implementation NNTestClassCase21 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase3.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase3.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/12. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | @interface NNTestClassCase30 : NNTestClassCase0 14 | 15 | @end 16 | 17 | @interface NNTestClassCase31 : NNTestClassCase0 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase3.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase3.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/12. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase3.h" 10 | #import 11 | 12 | @implementation NNTestClassCase30 { 13 | NSMutableString *_stringValue; 14 | } 15 | 16 | + (NSString *)nameOfClass { 17 | NSMutableString *value = [NSMutableString new]; 18 | [value appendString:NSStringFromClass(self)]; 19 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 20 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 21 | methodTypd:parse.methodType 22 | implmentClass:parse.implmentClass 23 | invokeClass:NSStringFromClass(self)]); 24 | return value; 25 | } 26 | 27 | - (NSString *)nameOfClass { 28 | NSMutableString *value = [NSMutableString new]; 29 | [value appendString:NSStringFromClass([self class])]; 30 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 31 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 32 | methodTypd:parse.methodType 33 | implmentClass:parse.implmentClass 34 | invokeClass:NSStringFromClass([self class])]); 35 | return value; 36 | } 37 | 38 | - (NSString *)stringValue { 39 | NSMutableString *value = _stringValue; 40 | if (value.length == 0) { 41 | value = [NSMutableString new]; 42 | } 43 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 44 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 45 | methodTypd:parse.methodType 46 | implmentClass:parse.implmentClass 47 | invokeClass:NSStringFromClass([self class])]); 48 | return value; 49 | } 50 | 51 | - (void)setStringValue:(NSString *)stringValue { 52 | NSMutableString *value = [NSMutableString stringWithString:stringValue]; 53 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 54 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 55 | methodTypd:parse.methodType 56 | implmentClass:parse.implmentClass 57 | invokeClass:NSStringFromClass([self class])]); 58 | _stringValue = value; 59 | } 60 | 61 | @end 62 | 63 | 64 | @implementation NNTestClassCase31 65 | 66 | - (NSString *)nameOfClass { 67 | NSMutableString *value = [NSMutableString new]; 68 | [value appendString:NSStringFromClass([self class])]; 69 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 70 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 71 | methodTypd:parse.methodType 72 | implmentClass:parse.implmentClass 73 | invokeClass:NSStringFromClass([self class])]); 74 | return value; 75 | } 76 | 77 | - (NSString *)stringValue { 78 | NSMutableString *value = objc_getAssociatedObject(self, @selector(stringValue)); 79 | if (value.length == 0) { 80 | value = [NSMutableString new]; 81 | } 82 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 83 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 84 | methodTypd:parse.methodType 85 | implmentClass:parse.implmentClass 86 | invokeClass:NSStringFromClass([self class])]); 87 | return value; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase4+NNTestProtocol.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase4+NNTestProtocol.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/16. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase4.h" 10 | #import "NNTestProtocol.h" 11 | #import 12 | #import 13 | 14 | @nn_extension(NNTestSubProtocol, @nn_where(NNTestClassCase40Protocol, [self conformsToProtocol:@protocol(NNTestClassCase40Protocol)])) 15 | 16 | + (NSString *)nameOfClass { 17 | NSMutableString *value = [NSMutableString new]; 18 | [value appendString:NSStringFromClass(self)]; 19 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 20 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 21 | methodTypd:parse.methodType 22 | implmentClass:parse.implmentClass 23 | invokeClass:NSStringFromClass(self)]); 24 | return value; 25 | } 26 | 27 | - (void)setStringValue:(NSString *)stringValue { 28 | NSMutableString *value = [NSMutableString stringWithString:stringValue]; 29 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 30 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 31 | methodTypd:parse.methodType 32 | implmentClass:parse.implmentClass 33 | invokeClass:NSStringFromClass([self class])]); 34 | objc_setAssociatedObject(self, @selector(stringValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 35 | } 36 | 37 | @end 38 | 39 | 40 | @nn_extension(NNTestSubProtocol, @nn_where(self == [NNTestClassCase41 class]), NNTestClassCase41Protocol) 41 | 42 | - (NSString *)nameOfClass { 43 | NSMutableString *value = [NSMutableString new]; 44 | [value appendString:NSStringFromClass([self class])]; 45 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 46 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 47 | methodTypd:parse.methodType 48 | implmentClass:parse.implmentClass 49 | invokeClass:NSStringFromClass([self class])]); 50 | return value; 51 | } 52 | 53 | - (NSString *)stringValue { 54 | NSMutableString *value = objc_getAssociatedObject(self, @selector(stringValue)); 55 | if (value.length == 0) { 56 | value = [NSMutableString new]; 57 | } 58 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 59 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 60 | methodTypd:parse.methodType 61 | implmentClass:parse.implmentClass 62 | invokeClass:NSStringFromClass([self class])]); 63 | return value; 64 | } 65 | 66 | @end 67 | 68 | @nn_extension(NNTestSubProtocol, @nn_where(self == [NNTestClassCase42 class]), NNTestClassCase420Protocol, NNTestClassCase421Protocol) 69 | 70 | - (NSString *)stringValue { 71 | NSMutableString *value = objc_getAssociatedObject(self, @selector(stringValue)); 72 | if (value.length == 0) { 73 | value = [NSMutableString new]; 74 | } 75 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 76 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 77 | methodTypd:parse.methodType 78 | implmentClass:parse.implmentClass 79 | invokeClass:NSStringFromClass([self class])]); 80 | self.case42 = value; 81 | return value; 82 | } 83 | 84 | - (void)setStringValue:(NSString *)stringValue { 85 | NSMutableString *value = [NSMutableString stringWithString:stringValue]; 86 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 87 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 88 | methodTypd:parse.methodType 89 | implmentClass:parse.implmentClass 90 | invokeClass:NSStringFromClass([self class])]); 91 | objc_setAssociatedObject(self, @selector(stringValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 92 | self.case42 = stringValue; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase4.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase4.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/16. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | 14 | @protocol NNTestClassCase40Protocol 15 | 16 | @end 17 | 18 | @interface NNTestClassCase40 : NNTestClassCase0 19 | 20 | @end 21 | 22 | @protocol NNTestClassCase41Protocol 23 | 24 | @end 25 | 26 | @interface NNTestClassCase41 : NNTestClassCase0 27 | 28 | @end 29 | 30 | @protocol NNTestClassCase420Protocol 31 | 32 | @optional 33 | @property (nonatomic, strong) NSString *case42; 34 | 35 | @end 36 | 37 | @protocol NNTestClassCase421Protocol 38 | 39 | @end 40 | 41 | @interface NNTestClassCase42 : NNTestClassCase0 42 | 43 | @property (nonatomic, strong) NSString *case42; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase4.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase4.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/16. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase4.h" 10 | #import 11 | 12 | 13 | @implementation NNTestClassCase40 14 | 15 | - (NSString *)stringValue { 16 | NSMutableString *value = objc_getAssociatedObject(self, @selector(stringValue)); 17 | if (value.length == 0) { 18 | value = [NSMutableString new]; 19 | } 20 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 21 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 22 | methodTypd:parse.methodType 23 | implmentClass:parse.implmentClass 24 | invokeClass:NSStringFromClass([self class])]); 25 | return value; 26 | } 27 | 28 | @end 29 | 30 | 31 | @implementation NNTestClassCase41 32 | 33 | - (void)setStringValue:(NSString *)stringValue { 34 | NSMutableString *value = [NSMutableString stringWithString:stringValue]; 35 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 36 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 37 | methodTypd:parse.methodType 38 | implmentClass:parse.implmentClass 39 | invokeClass:NSStringFromClass([self class])]); 40 | objc_setAssociatedObject(self, @selector(stringValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 41 | } 42 | 43 | @end 44 | 45 | 46 | @implementation NNTestClassCase42 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase5+NNTestProtocol.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase5+NNTestProtocol.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/17. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase5.h" 10 | #import "NNTestProtocol.h" 11 | #import 12 | #import 13 | 14 | @nn_extension(NNTestSubProtocol, @nn_where(NNTestClassCase50, self == [NNTestClassCase50 class])) 15 | 16 | + (void)initialize { 17 | NSMutableString *value = [NSMutableString stringWithString:@"initialize track"]; 18 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 19 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 20 | methodTypd:parse.methodType 21 | implmentClass:parse.implmentClass 22 | invokeClass:NSStringFromClass(self)]); 23 | case51Track = value; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase5.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase5.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/17. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | extern NSString *case50Track; 14 | extern NSString *case51Track; 15 | 16 | @interface NNTestClassCase50 : NNTestClassCase0 17 | 18 | @end 19 | 20 | @interface NNTestClassCase51 : NNTestClassCase0 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase5.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase5.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/17. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase5.h" 10 | 11 | NSString *case50Track; 12 | NSString *case51Track; 13 | 14 | @implementation NNTestClassCase50 15 | 16 | + (void)initialize { 17 | NSMutableString *value = [NSMutableString stringWithString:@"initialize track"]; 18 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 19 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 20 | methodTypd:parse.methodType 21 | implmentClass:parse.implmentClass 22 | invokeClass:NSStringFromClass(self)]); 23 | case50Track = value; 24 | } 25 | 26 | @end 27 | 28 | 29 | @implementation NNTestClassCase51 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase6+NNTestProtocol.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase6+NNTestProtocol.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2020/2/21. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase6.h" 10 | #import "NNTestProtocol.h" 11 | #import 12 | 13 | @nn_extension(NNTestProtocol, @nn_where(NNTestClassCase62, self == [NNTestClassCase62 class])) 14 | 15 | + (NSString *)nameOfClass { 16 | NSMutableString *value = [NSMutableString new]; 17 | [value appendString:NSStringFromClass(self)]; 18 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 19 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 20 | methodTypd:parse.methodType 21 | implmentClass:parse.implmentClass 22 | invokeClass:NSStringFromClass(self)]); 23 | return value; 24 | } 25 | 26 | - (NSString *)nameOfClass { 27 | NSMutableString *value = [NSMutableString new]; 28 | [value appendString:NSStringFromClass([self class])]; 29 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 30 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 31 | methodTypd:parse.methodType 32 | implmentClass:parse.implmentClass 33 | invokeClass:NSStringFromClass([self class])]); 34 | return value; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase6.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase6.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2020/2/21. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestClassCase0.h" 11 | #import "NNTestProtocol.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface NNTestClassCase60 : NNTestClassCase0 16 | 17 | @end 18 | 19 | @interface NNTestClassCase61 : NNTestClassCase60 20 | 21 | @end 22 | 23 | @interface NNTestClassCase62 : NNTestClassCase61 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestClasses/NNTestClassCase6.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestClassCase6.m 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2020/2/21. 6 | // Copyright © 2020 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestClassCase6.h" 10 | 11 | @implementation NNTestClassCase60 12 | 13 | @end 14 | 15 | @implementation NNTestClassCase61 16 | 17 | @end 18 | 19 | @implementation NNTestClassCase62 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestProtocol/NNTestProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestProtocol.h 3 | // NNPopObjcTests 4 | // 5 | // Created by GuHaijun on 2019/12/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NSString+NNTestTrack.h" 11 | 12 | @protocol NNTestProtocol 13 | 14 | @optional 15 | + (NSString *)nameOfClass; 16 | - (NSString *)nameOfClass; 17 | 18 | @end 19 | 20 | @protocol NNTestSubProtocol 21 | 22 | @optional 23 | @property (nonatomic, strong) NSString *stringValue; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestProtocol/NNTestProtocol.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestProtocol.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by GuHaijun on 2019/12/8. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestProtocol.h" 10 | #import 11 | #import 12 | 13 | @nn_extension(NNTestProtocol) 14 | 15 | + (NSString *)nameOfClass { 16 | NSMutableString *value = [NSMutableString new]; 17 | [value appendString:NSStringFromClass(self)]; 18 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 19 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 20 | methodTypd:parse.methodType 21 | implmentClass:parse.implmentClass 22 | invokeClass:NSStringFromClass(self)]); 23 | return value; 24 | } 25 | 26 | - (NSString *)nameOfClass { 27 | NSMutableString *value = [NSMutableString new]; 28 | [value appendString:NSStringFromClass([self class])]; 29 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 30 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 31 | methodTypd:parse.methodType 32 | implmentClass:parse.implmentClass 33 | invokeClass:NSStringFromClass([self class])]); 34 | return value; 35 | } 36 | 37 | @end 38 | 39 | 40 | @nn_extension(NNTestSubProtocol) 41 | 42 | - (NSString *)stringValue { 43 | NSMutableString *value = objc_getAssociatedObject(self, @selector(stringValue)); 44 | if (value.length == 0) { 45 | value = [NSMutableString new]; 46 | } 47 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 48 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 49 | methodTypd:parse.methodType 50 | implmentClass:parse.implmentClass 51 | invokeClass:NSStringFromClass([self class])]); 52 | return value; 53 | } 54 | 55 | - (void)setStringValue:(NSString *)stringValue { 56 | NSMutableString *value = [NSMutableString stringWithString:stringValue]; 57 | NNTestFunctionParse *parse = [NNTestFunctionParse parseWithFunctionInfo:@(__FUNCTION__)]; 58 | value.track.stack->push([NNTestTrackItem itemWithMethodName:parse.methodName 59 | methodTypd:parse.methodType 60 | implmentClass:parse.implmentClass 61 | invokeClass:NSStringFromClass([self class])]); 62 | objc_setAssociatedObject(self, @selector(stringValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NNTestFunctionParse.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestFunctionParse.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, NNTestMethodType) { 12 | NNTestMethodTypeUnknown, 13 | NNTestMethodTypeInstance, 14 | NNTestMethodTypeClass, 15 | }; 16 | 17 | @interface NNTestFunctionParse : NSObject 18 | 19 | + (instancetype)parseWithFunctionInfo:(NSString *)functionInfo; 20 | @property (nonatomic, strong) NSString *implmentClass; 21 | @property (nonatomic, assign) NNTestMethodType methodType; 22 | @property (nonatomic, strong) NSString *methodName; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NNTestFunctionParse.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestFunctionParse.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestFunctionParse.h" 10 | 11 | @implementation NNTestFunctionParse 12 | 13 | + (instancetype)parseWithFunctionInfo:(NSString *)functionInfo { 14 | 15 | NNTestFunctionParse *parse = [NNTestFunctionParse new]; 16 | NSString *info = functionInfo; 17 | info = [info stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 18 | NSArray *components = [info componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[] "]]; 19 | if (components.count == 4) { 20 | parse.methodType = NNTestMethodTypeUnknown; 21 | if ([components[0] isEqualToString:@"-"]) { 22 | parse.methodType = NNTestMethodTypeInstance; 23 | } 24 | if ([components[0] isEqualToString:@"+"]) { 25 | parse.methodType = NNTestMethodTypeClass; 26 | } 27 | parse.implmentClass = components[1]; 28 | parse.methodName = components[2]; 29 | } 30 | return parse; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NNTestTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestTrack.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #include 11 | #import "NNTestFunctionParse.h" 12 | 13 | @class NNTestTrackItem; 14 | 15 | @interface NNTestTrack : NSObject 16 | 17 | - (std::shared_ptr>)stack; 18 | 19 | @end 20 | 21 | 22 | @interface NNTestTrackItem : NSObject 23 | 24 | @property (nonatomic, strong) NSString *invokeClass; 25 | @property (nonatomic, strong) NSString *implmentClass; 26 | @property (nonatomic, assign) NNTestMethodType methodType; 27 | @property (nonatomic, strong) NSString *methodName; 28 | 29 | + (instancetype)itemWithMethodName:(NSString *)methodName 30 | methodTypd:(NNTestMethodType)methodType 31 | implmentClass:(NSString *)implmentClass 32 | invokeClass:(NSString *)invokeClass; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NNTestTrack.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NNTestTrack.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NNTestTrack.h" 10 | 11 | @implementation NNTestTrack { 12 | std::shared_ptr> _stack; 13 | } 14 | 15 | - (instancetype)init { 16 | self = [super init]; 17 | _stack = std::make_shared>(); 18 | return self; 19 | } 20 | 21 | - (std::shared_ptr>)stack { 22 | return _stack; 23 | } 24 | 25 | @end 26 | 27 | @implementation NNTestTrackItem 28 | 29 | + (instancetype)itemWithMethodName:(NSString *)methodName 30 | methodTypd:(NNTestMethodType)methodType 31 | implmentClass:(NSString *)implmentClass 32 | invokeClass:(NSString *)invokeClass { 33 | NNTestTrackItem *item = [NNTestTrackItem new]; 34 | item.methodName = methodName; 35 | item.methodType = methodType; 36 | item.implmentClass = implmentClass; 37 | item.invokeClass = invokeClass; 38 | return item; 39 | } 40 | 41 | @end 42 | 43 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NSString+NNTestTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+NNTestTrack.h 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NNTestTrack.h" 11 | 12 | @interface NSString (NNTestTrack) 13 | 14 | @property (nonatomic, strong) NNTestTrack* track; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /NNPopObjcTests/NNTestTrack/NSString+NNTestTrack.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+NNTestTrack.mm 3 | // NNPopObjcTests 4 | // 5 | // Created by 顾海军 on 2019/12/13. 6 | // Copyright © 2019 GuHaiJun. All rights reserved. 7 | // 8 | 9 | #import "NSString+NNTestTrack.h" 10 | #import 11 | 12 | @implementation NSString (NNTestTrack) 13 | 14 | - (NNTestTrack *)track { 15 | NNTestTrack *value = objc_getAssociatedObject(self, @selector(track)); 16 | if (value == nil) { 17 | value = [NNTestTrack new]; 18 | self.track = value; 19 | } 20 | return value; 21 | } 22 | 23 | - (void)setTrack:(NNTestTrack *)track { 24 | objc_setAssociatedObject(self, @selector(track), track, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

NNPopObjc

2 | 3 | [![CI](https://github.com/amisare/NNPopObjc/workflows/CI/badge.svg)](https://github.com/amisare/NNPopObjc/actions) 4 | [![codecov](https://codecov.io/gh/amisare/NNPopObjc/branch/master/graph/badge.svg)](https://codecov.io/gh/amisare/NNPopObjc) 5 | [![GitHub release](https://img.shields.io/github/release/amisare/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc/releases) 6 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/NNPopObjc.svg)](https://cocoapods.org/pods/NNPopObjc) 7 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | [![Platform](https://img.shields.io/cocoapods/p/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc) 9 | [![GitHub license](https://img.shields.io/github/license/amisare/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc/blob/master/LICENSE) 10 | 11 | 12 | NNPopObjc is inspired by protocol oriented programming, it provides extensibility for the protocol. 13 | 14 | [NNPopObjc 中文文档](README_zh_CN.md) 15 | 16 | ## Documents 17 | 18 | * Read the [NNPopObjc Guide](Doc/1.0.x/usage_en.md) document. 19 | 20 | ## Quick Start 21 | 22 | ### Declaring a Procotol 23 | 24 | Declaring the Procotol in a `.h` file 25 | 26 | ```objective-c 27 | @protocol NNDemoProtocol 28 | 29 | @optional 30 | - (void)sayHelloPop; 31 | + (void)sayHelloPop; 32 | 33 | @end 34 | ``` 35 | 36 | ### Extending the Procotol 37 | 38 | Extending the Procotol needs in a `.m` file 39 | 40 | ```objective-c 41 | ///Extending the Procotol for default implemention. 42 | @nn_extension(NNDemoProtocol) 43 | 44 | + (void)sayHelloPop { 45 | DLog(@"+[%@ %s] code say hello pop", self, sel_getName(_cmd)); 46 | } 47 | 48 | - (void)sayHelloPop { 49 | DLog(@"-[%@ %s] code say hello pop", [self class], sel_getName(_cmd)); 50 | } 51 | 52 | @end 53 | ``` 54 | 55 | ### Adopting the Procotol 56 | 57 | - Creating a Class 58 | 59 | ```objective-c 60 | @interface NNDemoObjc : NSObject 61 | 62 | @end 63 | ``` 64 | 65 | - Implementing the Class 66 | 67 | ``` 68 | @implementation NNDemoObjc 69 | 70 | @end 71 | ``` 72 | 73 | ### Using the Class 74 | 75 | - Calling the Methods 76 | 77 | ```objective-c 78 | [NNDemoObjc sayHelloPop]; 79 | [[NNDemoObjc new] sayHelloPop]; 80 | ``` 81 | 82 | - Outputting 83 | 84 | ```objective-c 85 | +[NNDemoObjc sayHelloPop] code say hello pop 86 | -[NNDemoObjc sayHelloPop] code say hello pop 87 | ``` 88 | 89 | ## Installation 90 | 91 | NNPopObjc supports multiple methods for installing the library in a project. 92 | 93 | ### Installation with CocoaPods 94 | 95 | You can install it with the following command: 96 | 97 | ```bash 98 | $ gem install cocoapods 99 | ``` 100 | 101 | #### Podfile 102 | 103 | To integrate NNPopObjc into your Xcode project using CocoaPods, specify it in your `Podfile`: 104 | 105 | ```ruby 106 | source 'https://github.com/CocoaPods/Specs.git' 107 | platform :ios, '8.0' 108 | 109 | target 'TargetName' do 110 | pod 'NNPopObjc' 111 | end 112 | ``` 113 | 114 | Then, run the following command: 115 | 116 | ```bash 117 | pod install 118 | ``` 119 | 120 | #### If installation failed with error: 121 | 122 | ```bash 123 | [!] Unable to find a specification for `NNPopObjc` 124 | ``` 125 | 126 | try install with the following command: 127 | 128 | ```bash 129 | pod install --repo-update 130 | ``` 131 | 132 | ### Installation with Carthage 133 | 134 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 135 | 136 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 137 | 138 | ```bash 139 | $ brew update 140 | $ brew install carthage 141 | ``` 142 | 143 | To integrate NNPopObjc into your Xcode project using Carthage, specify it in your `Cartfile`: 144 | 145 | ```ogdl 146 | github "amisare/NNPopObjc" ~> 1.0.6 147 | ``` 148 | 149 | Run `carthage` to build the framework and drag the built `NNPopObjc.framework` into your Xcode project. 150 | 151 | ## inspired 152 | 153 | - [libextobjc](https://github.com/jspahrsummers/libextobjc) 154 | - [ProtocolKit](https://github.com/forkingdog/ProtocolKit) 155 | - [Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/play/wwdc2015/408/) 156 | 157 | ## License 158 | 159 | NNPopObjc is released under the MIT license. See LICENSE for details. 160 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 |

NNPopObjc

2 | 3 | [![CI](https://github.com/amisare/NNPopObjc/workflows/CI/badge.svg)](https://github.com/amisare/NNPopObjc/actions) 4 | [![codecov](https://codecov.io/gh/amisare/NNPopObjc/branch/master/graph/badge.svg)](https://codecov.io/gh/amisare/NNPopObjc) 5 | [![GitHub release](https://img.shields.io/github/release/amisare/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc/releases) 6 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/NNPopObjc.svg)](https://cocoapods.org/pods/NNPopObjc) 7 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | [![Platform](https://img.shields.io/cocoapods/p/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc) 9 | [![GitHub license](https://img.shields.io/github/license/amisare/NNPopObjc.svg)](https://github.com/amisare/NNPopObjc/blob/master/LICENSE) 10 | 11 | 12 | NNPopObjc 受面向协议编程的启发,为协议提供了实现扩展的功能。 13 | 14 | [NNPopObjc English Document](README_zh_CN.md) 15 | 16 | ## 文档 17 | 18 | * 阅读 [NNPopObjc Guide](Doc/1.0.x/usage_zh_CN.md) 文档。 19 | 20 | ## 快速开始 21 | 22 | ### 声明协议 23 | 24 | 在 `.h` 文件中声明协议 25 | 26 | ```objective-c 27 | @protocol NNDemoProtocol 28 | 29 | @optional 30 | - (void)sayHelloPop; 31 | + (void)sayHelloPop; 32 | 33 | @end 34 | ``` 35 | 36 | ### 扩展协议 37 | 38 | 扩展协议需要在 `.m` 中实现 39 | 40 | ```objective-c 41 | /// 默认协议扩展 42 | @nn_extension(NNDemoProtocol) 43 | 44 | + (void)sayHelloPop { 45 | DLog(@"+[%@ %s] code say hello pop", self, sel_getName(_cmd)); 46 | } 47 | 48 | - (void)sayHelloPop { 49 | DLog(@"-[%@ %s] code say hello pop", [self class], sel_getName(_cmd)); 50 | } 51 | 52 | @end 53 | ``` 54 | 55 | ### 遵守协议 56 | 57 | - 创建类 58 | 59 | ```objective-c 60 | @interface NNDemoObjc : NSObject 61 | 62 | @end 63 | ``` 64 | 65 | - 实现类 66 | 67 | ``` 68 | @implementation NNDemoObjc 69 | 70 | @end 71 | ``` 72 | 73 | ### 使用类 74 | 75 | - 调用方法 76 | 77 | ```objective-c 78 | [NNDemoObjc sayHelloPop]; 79 | [[NNDemoObjc new] sayHelloPop]; 80 | ``` 81 | 82 | - 输出日志 83 | 84 | ```objective-cc 85 | +[NNDemoObjc sayHelloPop] code say hello pop 86 | -[NNDemoObjc sayHelloPop] code say hello pop 87 | ``` 88 | 89 | ## 安装 90 | 91 | NNPopObjc 支持多种方式集成方式。 92 | 93 | ### 通过 CocoaPods 集成 94 | 95 | 使用以下命令安装: 96 | 97 | ```bash 98 | $ gem install cocoapods 99 | ``` 100 | 101 | #### Podfile 102 | 103 | 将 NNPopObjc 添加到 `Podfile` ,通过 CocoaPods 集成 NNPopObjc 到 Xcode 项目: 104 | 105 | ```ruby 106 | source 'https://github.com/CocoaPods/Specs.git' 107 | platform :ios, '8.0' 108 | 109 | target 'TargetName' do 110 | pod 'NNPopObjc' 111 | end 112 | ``` 113 | 114 | 执行命令: 115 | 116 | ```bash 117 | pod install 118 | ``` 119 | 120 | #### 如安装失败,提示: 121 | 122 | ```bash 123 | [!] Unable to find a specification for `NNPopObjc` 124 | ``` 125 | 126 | 尝试使用命令: 127 | 128 | ```bash 129 | pod install --repo-update 130 | ``` 131 | 132 | ### 通过 Carthage 集成 133 | 134 | [Carthage](https://github.com/Carthage/Carthage) 是一个去中心化的依赖管理器,用于构建依赖和提供二进制 Framework 。 135 | 136 | 可以通过以下 [Homebrew](http://brew.sh/) 命令安装 Carthage : 137 | 138 | ```bash 139 | $ brew update 140 | $ brew install carthage 141 | ``` 142 | 143 | 通过 Carthage 将 NNPopObjc 集成到 Xcode 项目中,需要在 `Cartfile` 中添加: 144 | 145 | ```ogdl 146 | github "amisare/NNPopObjc" ~> 1.0.6 147 | ``` 148 | 149 | 执行 `carthage` 构建 Framework ,并将 `NNPopObjc.framework` 添加到 Xcode 项目中。 150 | --------------------------------------------------------------------------------