├── .gitignore ├── Podfile ├── Pods ├── Headers │ ├── Build │ │ └── Aspects │ │ │ └── Aspects.h │ └── Public │ │ └── Aspects │ │ └── Aspects.h ├── Target Support Files │ ├── Pods-Aspects │ │ ├── Pods-Aspects.xcconfig │ │ ├── Pods-Aspects-prefix.pch │ │ ├── Pods-Aspects-dummy.m │ │ └── Pods-Aspects-Private.xcconfig │ └── Pods │ │ ├── Pods-dummy.m │ │ ├── Pods.debug.xcconfig │ │ ├── Pods.release.xcconfig │ │ ├── Pods-environment.h │ │ ├── Pods-acknowledgements.markdown │ │ ├── Pods-acknowledgements.plist │ │ └── Pods-resources.sh ├── Manifest.lock ├── Pods.xcodeproj │ ├── xcuserdata │ │ └── peng.xcuserdatad │ │ │ └── xcschemes │ │ │ ├── xcschememanagement.plist │ │ │ ├── Pods.xcscheme │ │ │ └── Pods-Aspects.xcscheme │ └── project.pbxproj └── Aspects │ ├── LICENSE │ ├── Aspects.h │ ├── README.md │ └── Aspects.m ├── README.md ├── AspectsDemo ├── Images.xcassets │ ├── img0.imageset │ │ ├── Ray_Morimura_Hagurosan_.jpg │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── CustomCell.m ├── TableView.h ├── DetailViewController.m ├── DetailViewController.h ├── MainViewController.h ├── AppDelegate+Logging.h ├── CustomCell.h ├── AppDelegate.h ├── main.m ├── TableView.m ├── GLLogging.h ├── AppDelegate+Logging.m ├── Info.plist ├── AppDelegate.m ├── GLLogging.m ├── MainViewController.m ├── Base.lproj │ └── LaunchScreen.xib └── Main.storyboard ├── Podfile.lock ├── AspectsDemo.xcworkspace ├── xcuserdata │ └── peng.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── contents.xcworkspacedata ├── AspectsDemo.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── peng.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcuserdata │ └── peng.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── AspectsDemo.xcscheme └── project.pbxproj └── AspectsDemoTests ├── Info.plist └── AspectsDemoTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | ./Pods -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | pod 'Aspects' 2 | -------------------------------------------------------------------------------- /Pods/Headers/Build/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | ../../../Aspects/Aspects.h -------------------------------------------------------------------------------- /Pods/Headers/Public/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | ../../../Aspects/Aspects.h -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Aspects/Pods-Aspects.xcconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Demo for this article http://tech.glowing.com/cn/method-swizzling-aop/ 2 | 3 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Aspects/Pods-Aspects-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /AspectsDemo/Images.xcassets/img0.imageset/Ray_Morimura_Hagurosan_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangBoX/AspectsDemo/HEAD/AspectsDemo/Images.xcassets/img0.imageset/Ray_Morimura_Hagurosan_.jpg -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Aspects (1.4.1) 3 | 4 | DEPENDENCIES: 5 | - Aspects 6 | 7 | SPEC CHECKSUMS: 8 | Aspects: a6368eba05295b24ffe0e9acfb9080cc3b0f4b5f 9 | 10 | COCOAPODS: 0.35.0 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Aspects/Pods-Aspects-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Aspects : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Aspects 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Aspects (1.4.1) 3 | 4 | DEPENDENCIES: 5 | - Aspects 6 | 7 | SPEC CHECKSUMS: 8 | Aspects: a6368eba05295b24ffe0e9acfb9080cc3b0f4b5f 9 | 10 | COCOAPODS: 0.35.0 11 | -------------------------------------------------------------------------------- /AspectsDemo.xcworkspace/xcuserdata/peng.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangBoX/AspectsDemo/HEAD/AspectsDemo.xcworkspace/xcuserdata/peng.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AspectsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AspectsDemo.xcodeproj/project.xcworkspace/xcuserdata/peng.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangBoX/AspectsDemo/HEAD/AspectsDemo.xcodeproj/project.xcworkspace/xcuserdata/peng.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AspectsDemo/CustomCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCell.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "CustomCell.h" 10 | 11 | @implementation CustomCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /AspectsDemo/TableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableView.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableView : UITableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /AspectsDemo/DetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/16/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "DetailViewController.h" 10 | 11 | @implementation DetailViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /AspectsDemo/DetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/16/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DetailViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /AspectsDemo/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MainViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AspectsDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AspectsDemo/AppDelegate+Logging.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+Logging.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate (Logging) 12 | 13 | - (void)setupLogging; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /AspectsDemo/CustomCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCell.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CustomCell : UITableViewCell 12 | 13 | @property (nonatomic, weak) IBOutlet UIView *containerView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Aspects/Pods-Aspects-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-Aspects.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Build" "${PODS_ROOT}/Headers/Build/Aspects" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 4 | OTHER_LDFLAGS = -ObjC 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /AspectsDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /AspectsDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AspectsDemo 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Aspects" 4 | OTHER_LDFLAGS = -ObjC -l"Pods-Aspects" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Aspects" 4 | OTHER_LDFLAGS = -ObjC -l"Pods-Aspects" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /AspectsDemo/TableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableView.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "TableView.h" 10 | 11 | @implementation TableView 12 | 13 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 14 | { 15 | if (point.y < 0) { 16 | return NO; 17 | } 18 | return YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /AspectsDemo/Images.xcassets/img0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "Ray_Morimura_Hagurosan_.jpg" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // Aspects 10 | #define COCOAPODS_POD_AVAILABLE_Aspects 11 | #define COCOAPODS_VERSION_MAJOR_Aspects 1 12 | #define COCOAPODS_VERSION_MINOR_Aspects 4 13 | #define COCOAPODS_VERSION_PATCH_Aspects 1 14 | 15 | -------------------------------------------------------------------------------- /AspectsDemo/GLLogging.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoggingConfig.h 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/16/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | #define GLLoggingPageImpression @"GLLoggingPageImpression" 14 | #define GLLoggingTrackedEvents @"GLLoggingTrackedEvents" 15 | #define GLLoggingEventName @"GLLoggingEventName" 16 | #define GLLoggingEventSelectorName @"GLLoggingEventSelectorName" 17 | #define GLLoggingEventHandlerBlock @"GLLoggingEventHandlerBlock" 18 | 19 | @interface GLLogging : NSObject 20 | 21 | + (void)setupWithConfiguration:(NSDictionary *)configs; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /AspectsDemo.xcodeproj/xcuserdata/peng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AspectsDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B804801F1A41316700C14718 16 | 17 | primary 18 | 19 | 20 | B804803B1A41316700C14718 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AspectsDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/peng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-Aspects.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods.xcscheme 13 | 14 | isShown 15 | 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 28C0AC3EF84ACF6F48D535EF 21 | 22 | primary 23 | 24 | 25 | A8566ECB267BA59D3A89735B 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /AspectsDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | glow.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /AspectsDemoTests/AspectsDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AspectsDemoTests.m 3 | // AspectsDemoTests 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AspectsDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation AspectsDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pods/Aspects/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 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. -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Aspects 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | Generated by CocoaPods - http://cocoapods.org 28 | -------------------------------------------------------------------------------- /AspectsDemo/AppDelegate+Logging.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+Logging.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/17/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate+Logging.h" 10 | #import "GLLogging.h" 11 | 12 | @implementation AppDelegate (Logging) 13 | 14 | - (void)setupLogging 15 | { 16 | NSDictionary *config = @{ 17 | @"MainViewController": @{ 18 | GLLoggingPageImpression: @"page imp - main page", 19 | GLLoggingTrackedEvents: @[ 20 | @{ 21 | GLLoggingEventName: @"button one clicked", 22 | GLLoggingEventSelectorName: @"buttonOneClicked:", 23 | GLLoggingEventHandlerBlock: ^(id aspectInfo) { 24 | NSLog(@"button one clicked"); 25 | }, 26 | }, 27 | @{ 28 | GLLoggingEventName: @"button two clicked", 29 | GLLoggingEventSelectorName: @"buttonTwoClicked:", 30 | GLLoggingEventHandlerBlock: ^(id aspectInfo) { 31 | NSLog(@"button two clicked"); 32 | }, 33 | }, 34 | ], 35 | }, 36 | 37 | @"DetailViewController": @{ 38 | GLLoggingPageImpression: @"page imp - detail page", 39 | } 40 | }; 41 | 42 | [GLLogging setupWithConfiguration:config]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /AspectsDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | glow.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /AspectsDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "AppDelegate+Logging.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | 22 | [self setupLogging]; 23 | return YES; 24 | } 25 | 26 | - (void)applicationWillResignActive:(UIApplication *)application { 27 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 28 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/peng.xcuserdatad/xcschemes/Pods.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/peng.xcuserdatad/xcschemes/Pods-Aspects.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | Title 39 | Aspects 40 | Type 41 | PSGroupSpecifier 42 | 43 | 44 | FooterText 45 | Generated by CocoaPods - http://cocoapods.org 46 | Title 47 | 48 | Type 49 | PSGroupSpecifier 50 | 51 | 52 | StringsTable 53 | Acknowledgements 54 | Title 55 | Acknowledgements 56 | 57 | 58 | -------------------------------------------------------------------------------- /AspectsDemo/GLLogging.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoggingConfig.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 12/16/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "GLLogging.h" 10 | 11 | 12 | @import UIKit; 13 | 14 | 15 | @implementation GLLogging 16 | 17 | 18 | typedef void (^AspectHandlerBlock)(id aspectInfo); 19 | 20 | 21 | + (void)setupWithConfiguration:(NSDictionary *)configs 22 | { 23 | // Hook Page Impression 24 | [UIViewController aspect_hookSelector:@selector(viewDidAppear:) 25 | withOptions:AspectPositionAfter 26 | usingBlock:^(id aspectInfo) { 27 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 28 | NSString *className = NSStringFromClass([[aspectInfo instance] class]); 29 | NSString *pageImp = configs[className][GLLoggingPageImpression]; 30 | if (pageImp) { 31 | NSLog(@"%@", pageImp); 32 | } 33 | }); 34 | } error:NULL]; 35 | 36 | // Hook Events 37 | for (NSString *className in configs) { 38 | Class clazz = NSClassFromString(className); 39 | NSDictionary *config = configs[className]; 40 | 41 | if (config[GLLoggingTrackedEvents]) { 42 | for (NSDictionary *event in config[GLLoggingTrackedEvents]) { 43 | SEL selekor = NSSelectorFromString(event[GLLoggingEventSelectorName]); 44 | AspectHandlerBlock block = event[GLLoggingEventHandlerBlock]; 45 | 46 | [clazz aspect_hookSelector:selekor 47 | withOptions:AspectPositionAfter 48 | usingBlock:^(id aspectInfo) { 49 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 50 | block(aspectInfo); 51 | }); 52 | } error:NULL]; 53 | 54 | } 55 | } 56 | } 57 | } 58 | 59 | 60 | @end 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /AspectsDemo/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Test 4 | // 5 | // Created by Peng Gu on 11/24/14. 6 | // Copyright (c) 2014 Peng Gu. All rights reserved. 7 | // 8 | 9 | #import "MainViewController.h" 10 | #import 11 | #import "CustomCell.h" 12 | 13 | @interface MainViewController () 14 | 15 | @property (nonatomic, weak) IBOutlet UIView *headerView; 16 | @property (nonatomic, weak) IBOutlet UITableView *tableView; 17 | 18 | @end 19 | 20 | 21 | @implementation MainViewController 22 | 23 | #define PAGE_IMP_HEALTH @"page imp - health profile" 24 | #define BTN_CLK_HELP_EXPORT @"button click - export report" 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | 30 | // self.tableView.clipsToBounds = NO; 31 | self.tableView.contentInset = UIEdgeInsetsMake(300, 0, 0, 0); 32 | 33 | // [self aspect_hookSelector:@selector(viewDidAppear:) 34 | // withOptions:AspectPositionAfter 35 | // usingBlock:^(id aspectInfo){ 36 | // NSLog(@"view did appear: %@", aspectInfo); 37 | // } 38 | // error:NULL]; 39 | // 40 | // [self aspect_hookSelector:@selector(buttonClicked:) 41 | // withOptions:AspectPositionAfter 42 | // usingBlock:^(id aspectInfo, id sender) { 43 | // NSLog(@"button clicked: %@ \n %@", aspectInfo, sender); 44 | // } 45 | // error:NULL]; 46 | } 47 | 48 | 49 | - (void)didReceiveMemoryWarning 50 | { 51 | [super didReceiveMemoryWarning]; 52 | // Dispose of any resources that can be recreated. 53 | } 54 | 55 | 56 | 57 | 58 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 59 | { 60 | return 1; 61 | } 62 | 63 | 64 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 65 | { 66 | return 50; 67 | } 68 | 69 | 70 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 71 | { 72 | CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellReuseIdentifier" 73 | forIndexPath:indexPath]; 74 | 75 | CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 76 | CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white 77 | CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black 78 | UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 79 | 80 | cell.containerView.backgroundColor = color; 81 | // cell.textLabel.text = [NSString stringWithFormat:@"%ld %ld", indexPath.section, indexPath.row]; 82 | 83 | return cell; 84 | } 85 | 86 | 87 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 88 | { 89 | return 200; 90 | } 91 | 92 | 93 | - (IBAction)buttonOneClicked:(id)sender 94 | { 95 | 96 | } 97 | 98 | 99 | - (IBAction)buttonTwoClicked:(id)sender 100 | { 101 | 102 | } 103 | 104 | 105 | - (IBAction)buttonClicked:(id)sender 106 | { 107 | // NSLog(@"Button %ld Clicked", [sender tag]); 108 | } 109 | 110 | 111 | 112 | 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /AspectsDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Pods/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.h 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_OPTIONS(NSUInteger, AspectOptions) { 11 | AspectPositionAfter = 0, /// Called after the original implementation (default) 12 | AspectPositionInstead = 1, /// Will replace the original implementation. 13 | AspectPositionBefore = 2, /// Called before the original implementation. 14 | 15 | AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution. 16 | }; 17 | 18 | /// Opaque Aspect Token that allows to deregister the hook. 19 | @protocol AspectToken 20 | 21 | /// Deregisters an aspect. 22 | /// @return YES if deregistration is successful, otherwise NO. 23 | - (BOOL)remove; 24 | 25 | @end 26 | 27 | /// The AspectInfo protocol is the first parameter of our block syntax. 28 | @protocol AspectInfo 29 | 30 | /// The instance that is currently hooked. 31 | - (id)instance; 32 | 33 | /// The original invocation of the hooked method. 34 | - (NSInvocation *)originalInvocation; 35 | 36 | /// All method arguments, boxed. This is lazily evaluated. 37 | - (NSArray *)arguments; 38 | 39 | @end 40 | 41 | /** 42 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second. 43 | 44 | Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe. 45 | */ 46 | @interface NSObject (Aspects) 47 | 48 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 49 | /// 50 | /// @param block Aspects replicates the type signature of the method being hooked. 51 | /// The first parameter will be `id`, followed by all parameters of the method. 52 | /// These parameters are optional and will be filled to match the block signature. 53 | /// You can even use an empty block, or one that simple gets `id`. 54 | /// 55 | /// @note Hooking static methods is not supported. 56 | /// @return A token which allows to later deregister the aspect. 57 | + (id)aspect_hookSelector:(SEL)selector 58 | withOptions:(AspectOptions)options 59 | usingBlock:(id)block 60 | error:(NSError **)error; 61 | 62 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 63 | - (id)aspect_hookSelector:(SEL)selector 64 | withOptions:(AspectOptions)options 65 | usingBlock:(id)block 66 | error:(NSError **)error; 67 | 68 | @end 69 | 70 | 71 | typedef NS_ENUM(NSUInteger, AspectErrorCode) { 72 | AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted. 73 | AspectErrorDoesNotRespondToSelector, /// Selector could not be found. 74 | AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed. 75 | AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed. 76 | AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair. 77 | AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called. 78 | AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large. 79 | 80 | AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated. 81 | }; 82 | 83 | extern NSString *const AspectErrorDomain; 84 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | install_resource() 10 | { 11 | case $1 in 12 | *.storyboard) 13 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 14 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 15 | ;; 16 | *.xib) 17 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 18 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 19 | ;; 20 | *.framework) 21 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 22 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 23 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 25 | ;; 26 | *.xcdatamodel) 27 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 28 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 29 | ;; 30 | *.xcdatamodeld) 31 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 32 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 33 | ;; 34 | *.xcmappingmodel) 35 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 36 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 37 | ;; 38 | *.xcassets) 39 | ;; 40 | /*) 41 | echo "$1" 42 | echo "$1" >> "$RESOURCES_TO_COPY" 43 | ;; 44 | *) 45 | echo "${PODS_ROOT}/$1" 46 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 47 | ;; 48 | esac 49 | } 50 | 51 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 52 | if [[ "${ACTION}" == "install" ]]; then 53 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 54 | fi 55 | rm -f "$RESOURCES_TO_COPY" 56 | 57 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ] 58 | then 59 | case "${TARGETED_DEVICE_FAMILY}" in 60 | 1,2) 61 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 62 | ;; 63 | 1) 64 | TARGET_DEVICE_ARGS="--target-device iphone" 65 | ;; 66 | 2) 67 | TARGET_DEVICE_ARGS="--target-device ipad" 68 | ;; 69 | *) 70 | TARGET_DEVICE_ARGS="--target-device mac" 71 | ;; 72 | esac 73 | find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 74 | fi 75 | -------------------------------------------------------------------------------- /AspectsDemo.xcodeproj/xcuserdata/peng.xcuserdatad/xcschemes/AspectsDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Pods/Aspects/README.md: -------------------------------------------------------------------------------- 1 | Aspects v1.4.1 [![Build Status](https://travis-ci.org/steipete/Aspects.svg?branch=master)](https://travis-ci.org/steipete/Aspects) 2 | ============== 3 | 4 | Delightful, simple library for aspect oriented programming by [@steipete](http://twitter.com/steipete). 5 | 6 | **Think of Aspects as method swizzling on steroids. It allows you to add code to existing methods per class or per instance**, whilst thinking of the insertion point e.g. before/instead/after. Aspects automatically deals with calling super and is easier to use than regular method swizzling. 7 | 8 | This is stable and used in hundreds of apps since it's part of [PSPDFKit, an iOS PDF framework that ships with apps like Dropbox or Evernote](http://pspdfkit.com), and now I finally made it open source. 9 | 10 | Aspects extends `NSObject` with the following methods: 11 | 12 | ``` objc 13 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 14 | /// 15 | /// @param block Aspects replicates the type signature of the method being hooked. 16 | /// The first parameter will be `id`, followed by all parameters of the method. 17 | /// These parameters are optional and will be filled to match the block signature. 18 | /// You can even use an empty block, or one that simple gets `id`. 19 | /// 20 | /// @note Hooking static methods is not supported. 21 | /// @return A token which allows to later deregister the aspect. 22 | + (id)aspect_hookSelector:(SEL)selector 23 | withOptions:(AspectOptions)options 24 | usingBlock:(id)block 25 | error:(NSError **)error; 26 | 27 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 28 | - (id)aspect_hookSelector:(SEL)selector 29 | withOptions:(AspectOptions)options 30 | usingBlock:(id)block 31 | error:(NSError **)error; 32 | 33 | /// Deregister an aspect. 34 | /// @return YES if deregistration is successful, otherwise NO. 35 | id aspect = ...; 36 | [aspect remove]; 37 | ``` 38 | 39 | Adding aspects returns an opaque token of type `AspectToken` which can be used to deregister again. All calls are thread-safe. 40 | 41 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called 1000 times per second. 42 | 43 | Aspects calls and matches block arguments. Blocks without arguments are supported as well. The first block argument will be of type `id`. 44 | 45 | When to use Aspects 46 | ------------------- 47 | Aspect-oritented programming (AOP) is used to encapsulate "cross-cutting" concerns. These are the kind of requirements that *cut-accross* many modules in your system, and so cannot be encapsulated using normal Object Oriented programming. Some examples of these kinds of requirements: 48 | 49 | * *Whenever* a user invokes a method on the service client, security should be checked. 50 | * *Whenever* a useer interacts with the store, a genius suggestion should be presented, based on their interaction. 51 | * *All* calls should be logged. 52 | 53 | If we implemented the above requirements using regular OO there'd be some drawbacks: 54 | 55 | 56 | Good OO says a class should have a single responsibility, however adding on extra *cross-cutting* requirements means a class that is taking on other responsibilites. For example you might have a **StoreClient** that supposed to be all about making purchases from an online store. Add in some cross-cutting requirements and it might also have to take on the roles of logging, security and recommendations. This is not great: 57 | 58 | * Our StoreClient is now harder to understand and maintain. 59 | * These cross-cutting requirements are duplicated and spreading throughout our app. 60 | 61 | AOP lets us modularize these cross-cutting requirements, and then cleanly identify all of the places they should be applied. As shown in the examples above cross-cutting requirements can be eithe technical or business focused in nature. 62 | 63 | ## Here are some concrete examples: 64 | 65 | 66 | Aspects can be used to **dynamically add logging** for debug builds only: 67 | 68 | ``` objc 69 | [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo, BOOL animated) { 70 | NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated); 71 | } error:NULL]; 72 | ``` 73 | 74 | ------------------- 75 | It can be used to greatly simplify your analytics setup: 76 | https://github.com/orta/ARAnalytics 77 | 78 | ------------------- 79 | You can check if methods are really being called in your test cases: 80 | ``` objc 81 | - (void)testExample { 82 | TestClass *testClass = [TestClass new]; 83 | TestClass *testClass2 = [TestClass new]; 84 | 85 | __block BOOL testCallCalled = NO; 86 | [testClass aspect_hookSelector:@selector(testCall) withOptions:AspectPositionAfter usingBlock:^{ 87 | testCallCalled = YES; 88 | } error:NULL]; 89 | 90 | [testClass2 testCallAndExecuteBlock:^{ 91 | [testClass testCall]; 92 | } error:NULL]; 93 | XCTAssertTrue(testCallCalled, @"Calling testCallAndExecuteBlock must call testCall"); 94 | } 95 | ``` 96 | ------------------- 97 | It can be really useful for debugging. Here I was curious when exactly the tap gesture changed state: 98 | 99 | ``` objc 100 | [_singleTapGesture aspect_hookSelector:@selector(setState:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 101 | NSLog(@"%@: %@", aspectInfo.instance, aspectInfo.arguments); 102 | } error:NULL]; 103 | ``` 104 | 105 | ------------------- 106 | Another convenient use case is adding handlers for classes that you don't own. I've written it for use in [PSPDFKit](http://pspdfkit.com), where we require notifications when a view controller is being dismissed modally. This includes UIKit view controllers like `MFMailComposeViewController` or `UIImagePickerController`. We could have created subclasses for each of these controllers, but this would be quite a lot of unnecessary code. Aspects gives you a simpler solution for this problem: 107 | 108 | ``` objc 109 | @implementation UIViewController (DismissActionHook) 110 | 111 | // Will add a dismiss action once the controller gets dismissed. 112 | - (void)pspdf_addWillDismissAction:(void (^)(void))action { 113 | PSPDFAssert(action != NULL); 114 | 115 | [self aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 116 | if ([aspectInfo.instance isBeingDismissed]) { 117 | action(); 118 | } 119 | } error:NULL]; 120 | } 121 | 122 | @end 123 | ``` 124 | 125 | Debugging 126 | --------- 127 | Aspects identifies itself nicely in the stack trace, so it's easy to see if a method has been hooked: 128 | 129 | 130 | 131 | Using Aspects with non-void return types 132 | ---------------------------------------- 133 | 134 | You can use the invocation object to customize the return value: 135 | 136 | ``` objc 137 | [PSPDFDrawView aspect_hookSelector:@selector(shouldProcessTouches:withEvent:) withOptions:AspectPositionInstead usingBlock:^(id info, NSSet *touches, UIEvent *event) { 138 | // Call original implementation. 139 | BOOL processTouches; 140 | NSInvocation *invocation = info.originalInvocation; 141 | [invocation invoke]; 142 | [invocation getReturnValue:&processTouches]; 143 | 144 | if (processTouches) { 145 | processTouches = pspdf_stylusShouldProcessTouches(touches, event); 146 | [invocation setReturnValue:&processTouches]; 147 | } 148 | } error:NULL]; 149 | ``` 150 | 151 | Installation 152 | ------------ 153 | The simplest option is to use `pod "Aspects"`. 154 | 155 | You can also add the two files `Aspects.h/m`. There are no further requirements. 156 | 157 | Compatibility and Limitations 158 | ----------------------------- 159 | Aspects uses quite some runtime trickery to achieve what it does. You can mostly mix this with regular method swizzling. 160 | 161 | An important limitation is that for class-based hooking, a method can only be hooked once within the subclass hierarchy. [See #2](https://github.com/steipete/Aspects/issues/2) 162 | This does not apply for objects that are hooked. Aspects creates a dynamic subclass here and has full control. 163 | 164 | KVO works if observers are created after your calls `aspect_hookSelector:` It most likely will crash the other way around. 165 | Still looking for workarounds here - any help apprechiated. 166 | 167 | Because of ugly implementation details on the ObjC runtime, methods that return unions that also contain structs might not work correctly unless this code runs on the arm64 runtime. 168 | 169 | Credits 170 | ------- 171 | The idea to use `_objc_msgForward` and parts of the `NSInvocation` argument selection is from the excellent [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) from the GitHub guys. [This article](http://codeshaker.blogspot.co.at/2012/01/aop-delivered.html) explains how it works under the hood. 172 | 173 | 174 | Supported iOS & SDK Versions 175 | ----------------------------- 176 | 177 | * Aspects requires ARC. 178 | * Aspects is tested with iOS 6+ and OS X 10.7 or higher. 179 | 180 | License 181 | ------- 182 | MIT licensed, Copyright (c) 2014 Peter Steinberger, steipete@gmail.com, [@steipete](http://twitter.com/steipete) 183 | 184 | 185 | Release Notes 186 | ----------------- 187 | 188 | Version 1.4.1 189 | 190 | - Rename error codes. 191 | 192 | Version 1.4.0 193 | 194 | - Add support for block signatures that match method signatures. (thanks to @nickynick) 195 | 196 | Version 1.3.1 197 | 198 | - Add support for OS X 10.7 or higher. (thanks to @ashfurrow) 199 | 200 | Version 1.3.0 201 | 202 | - Add automatic deregistration. 203 | - Checks if the selector exists before trying to hook. 204 | - Improved dealloc hooking. (no more unsafe_unretained needed) 205 | - Better examples. 206 | - Always log errors. 207 | 208 | Version 1.2.0 209 | 210 | - Adds error parameter. 211 | - Improvements in subclassing registration tracking. 212 | 213 | Version 1.1.0 214 | 215 | - Renamed the files from NSObject+Aspects.m/h to just Aspects.m/h. 216 | - Removing now works via calling `remove` on the aspect token. 217 | - Allow hooking dealloc. 218 | - Fixes infinite loop if the same method is hooked for multiple classes. Hooking will only work for one class in the hierarchy. 219 | - Additional checks to prevent things like hooking retain/release/autorelease or forwardInvocation: 220 | - The original implementation of forwardInvocation is now correctly preserved. 221 | - Classes are properly cleaned up and restored to the original state after the last hook is deregistered. 222 | - Lots and lots of new test cases! 223 | 224 | Version 1.0.1 225 | 226 | - Minor tweaks and documentation improvements. 227 | 228 | Version 1.0.0 229 | 230 | - Initial release 231 | -------------------------------------------------------------------------------- /AspectsDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 527BF07057A7AF8351408B56 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DB1FF903FC127983D723C04 /* libPods.a */; }; 11 | B80480261A41316700C14718 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B80480251A41316700C14718 /* main.m */; }; 12 | B80480371A41316700C14718 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B80480351A41316700C14718 /* LaunchScreen.xib */; }; 13 | B80480431A41316700C14718 /* AspectsDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B80480421A41316700C14718 /* AspectsDemoTests.m */; }; 14 | B8E247131A4BE111009BEC8C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E247091A4BE111009BEC8C /* AppDelegate.m */; }; 15 | B8E247141A4BE111009BEC8C /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E2470B1A4BE111009BEC8C /* MainViewController.m */; }; 16 | B8E247151A4BE111009BEC8C /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E2470D1A4BE111009BEC8C /* DetailViewController.m */; }; 17 | B8E247161A4BE111009BEC8C /* TableView.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E2470F1A4BE111009BEC8C /* TableView.m */; }; 18 | B8E247171A4BE111009BEC8C /* CustomCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E247111A4BE111009BEC8C /* CustomCell.m */; }; 19 | B8E247181A4BE111009BEC8C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B8E247121A4BE111009BEC8C /* Main.storyboard */; }; 20 | B8E2471D1A4BE132009BEC8C /* GLLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E2471A1A4BE132009BEC8C /* GLLogging.m */; }; 21 | B8E2471E1A4BE132009BEC8C /* AppDelegate+Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = B8E2471C1A4BE132009BEC8C /* AppDelegate+Logging.m */; }; 22 | B8E247211A4BE15D009BEC8C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B8E247201A4BE15D009BEC8C /* Images.xcassets */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | B804803D1A41316700C14718 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = B80480181A41316700C14718 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = B804801F1A41316700C14718; 31 | remoteInfo = AspectsDemo; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 54A9AA11DEF1D1795C857AAA /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 37 | 7DB1FF903FC127983D723C04 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 7F1B9497B8CD02693B5E4858 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 39 | B80480201A41316700C14718 /* AspectsDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AspectsDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | B80480241A41316700C14718 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | B80480251A41316700C14718 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 42 | B80480361A41316700C14718 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 43 | B804803C1A41316700C14718 /* AspectsDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AspectsDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | B80480411A41316700C14718 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | B80480421A41316700C14718 /* AspectsDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AspectsDemoTests.m; sourceTree = ""; }; 46 | B8E247081A4BE111009BEC8C /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | B8E247091A4BE111009BEC8C /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | B8E2470A1A4BE111009BEC8C /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; 49 | B8E2470B1A4BE111009BEC8C /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; 50 | B8E2470C1A4BE111009BEC8C /* DetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = ""; }; 51 | B8E2470D1A4BE111009BEC8C /* DetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; 52 | B8E2470E1A4BE111009BEC8C /* TableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableView.h; sourceTree = ""; }; 53 | B8E2470F1A4BE111009BEC8C /* TableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableView.m; sourceTree = ""; }; 54 | B8E247101A4BE111009BEC8C /* CustomCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomCell.h; sourceTree = ""; }; 55 | B8E247111A4BE111009BEC8C /* CustomCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomCell.m; sourceTree = ""; }; 56 | B8E247121A4BE111009BEC8C /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 57 | B8E247191A4BE132009BEC8C /* GLLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLLogging.h; sourceTree = ""; }; 58 | B8E2471A1A4BE132009BEC8C /* GLLogging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLLogging.m; sourceTree = ""; }; 59 | B8E2471B1A4BE132009BEC8C /* AppDelegate+Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AppDelegate+Logging.h"; sourceTree = ""; }; 60 | B8E2471C1A4BE132009BEC8C /* AppDelegate+Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppDelegate+Logging.m"; sourceTree = ""; }; 61 | B8E247201A4BE15D009BEC8C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | B804801D1A41316700C14718 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | 527BF07057A7AF8351408B56 /* libPods.a in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | B80480391A41316700C14718 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 03CFC5FF35715114CA5824D6 /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 7DB1FF903FC127983D723C04 /* libPods.a */, 87 | ); 88 | name = Frameworks; 89 | sourceTree = ""; 90 | }; 91 | 5EC0109D3B32A913A2CD30BC /* Pods */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 54A9AA11DEF1D1795C857AAA /* Pods.debug.xcconfig */, 95 | 7F1B9497B8CD02693B5E4858 /* Pods.release.xcconfig */, 96 | ); 97 | name = Pods; 98 | sourceTree = ""; 99 | }; 100 | B80480171A41316700C14718 = { 101 | isa = PBXGroup; 102 | children = ( 103 | B80480221A41316700C14718 /* AspectsDemo */, 104 | B804803F1A41316700C14718 /* AspectsDemoTests */, 105 | B80480211A41316700C14718 /* Products */, 106 | 5EC0109D3B32A913A2CD30BC /* Pods */, 107 | 03CFC5FF35715114CA5824D6 /* Frameworks */, 108 | ); 109 | sourceTree = ""; 110 | }; 111 | B80480211A41316700C14718 /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | B80480201A41316700C14718 /* AspectsDemo.app */, 115 | B804803C1A41316700C14718 /* AspectsDemoTests.xctest */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | B80480221A41316700C14718 /* AspectsDemo */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | B8E247081A4BE111009BEC8C /* AppDelegate.h */, 124 | B8E247091A4BE111009BEC8C /* AppDelegate.m */, 125 | B8E2470A1A4BE111009BEC8C /* MainViewController.h */, 126 | B8E2470B1A4BE111009BEC8C /* MainViewController.m */, 127 | B8E2470C1A4BE111009BEC8C /* DetailViewController.h */, 128 | B8E2470D1A4BE111009BEC8C /* DetailViewController.m */, 129 | B8E2471F1A4BE137009BEC8C /* Logging */, 130 | B8E247221A4BE184009BEC8C /* Views */, 131 | B8E247201A4BE15D009BEC8C /* Images.xcassets */, 132 | B8E247121A4BE111009BEC8C /* Main.storyboard */, 133 | B80480351A41316700C14718 /* LaunchScreen.xib */, 134 | B80480231A41316700C14718 /* Supporting Files */, 135 | ); 136 | path = AspectsDemo; 137 | sourceTree = ""; 138 | }; 139 | B80480231A41316700C14718 /* Supporting Files */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | B80480241A41316700C14718 /* Info.plist */, 143 | B80480251A41316700C14718 /* main.m */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | B804803F1A41316700C14718 /* AspectsDemoTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | B80480421A41316700C14718 /* AspectsDemoTests.m */, 152 | B80480401A41316700C14718 /* Supporting Files */, 153 | ); 154 | path = AspectsDemoTests; 155 | sourceTree = ""; 156 | }; 157 | B80480401A41316700C14718 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | B80480411A41316700C14718 /* Info.plist */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | B8E2471F1A4BE137009BEC8C /* Logging */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | B8E247191A4BE132009BEC8C /* GLLogging.h */, 169 | B8E2471A1A4BE132009BEC8C /* GLLogging.m */, 170 | B8E2471B1A4BE132009BEC8C /* AppDelegate+Logging.h */, 171 | B8E2471C1A4BE132009BEC8C /* AppDelegate+Logging.m */, 172 | ); 173 | name = Logging; 174 | sourceTree = ""; 175 | }; 176 | B8E247221A4BE184009BEC8C /* Views */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | B8E2470E1A4BE111009BEC8C /* TableView.h */, 180 | B8E2470F1A4BE111009BEC8C /* TableView.m */, 181 | B8E247101A4BE111009BEC8C /* CustomCell.h */, 182 | B8E247111A4BE111009BEC8C /* CustomCell.m */, 183 | ); 184 | name = Views; 185 | sourceTree = ""; 186 | }; 187 | /* End PBXGroup section */ 188 | 189 | /* Begin PBXNativeTarget section */ 190 | B804801F1A41316700C14718 /* AspectsDemo */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = B80480461A41316700C14718 /* Build configuration list for PBXNativeTarget "AspectsDemo" */; 193 | buildPhases = ( 194 | 7D3C4CCC0776C33BA083923A /* Check Pods Manifest.lock */, 195 | B804801C1A41316700C14718 /* Sources */, 196 | B804801D1A41316700C14718 /* Frameworks */, 197 | B804801E1A41316700C14718 /* Resources */, 198 | 730A66D511D52FD1E09307A4 /* Copy Pods Resources */, 199 | ); 200 | buildRules = ( 201 | ); 202 | dependencies = ( 203 | ); 204 | name = AspectsDemo; 205 | productName = AspectsDemo; 206 | productReference = B80480201A41316700C14718 /* AspectsDemo.app */; 207 | productType = "com.apple.product-type.application"; 208 | }; 209 | B804803B1A41316700C14718 /* AspectsDemoTests */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = B80480491A41316700C14718 /* Build configuration list for PBXNativeTarget "AspectsDemoTests" */; 212 | buildPhases = ( 213 | B80480381A41316700C14718 /* Sources */, 214 | B80480391A41316700C14718 /* Frameworks */, 215 | B804803A1A41316700C14718 /* Resources */, 216 | ); 217 | buildRules = ( 218 | ); 219 | dependencies = ( 220 | B804803E1A41316700C14718 /* PBXTargetDependency */, 221 | ); 222 | name = AspectsDemoTests; 223 | productName = AspectsDemoTests; 224 | productReference = B804803C1A41316700C14718 /* AspectsDemoTests.xctest */; 225 | productType = "com.apple.product-type.bundle.unit-test"; 226 | }; 227 | /* End PBXNativeTarget section */ 228 | 229 | /* Begin PBXProject section */ 230 | B80480181A41316700C14718 /* Project object */ = { 231 | isa = PBXProject; 232 | attributes = { 233 | LastUpgradeCheck = 0610; 234 | ORGANIZATIONNAME = "Peng Gu"; 235 | TargetAttributes = { 236 | B804801F1A41316700C14718 = { 237 | CreatedOnToolsVersion = 6.1.1; 238 | }; 239 | B804803B1A41316700C14718 = { 240 | CreatedOnToolsVersion = 6.1.1; 241 | TestTargetID = B804801F1A41316700C14718; 242 | }; 243 | }; 244 | }; 245 | buildConfigurationList = B804801B1A41316700C14718 /* Build configuration list for PBXProject "AspectsDemo" */; 246 | compatibilityVersion = "Xcode 3.2"; 247 | developmentRegion = English; 248 | hasScannedForEncodings = 0; 249 | knownRegions = ( 250 | en, 251 | Base, 252 | ); 253 | mainGroup = B80480171A41316700C14718; 254 | productRefGroup = B80480211A41316700C14718 /* Products */; 255 | projectDirPath = ""; 256 | projectRoot = ""; 257 | targets = ( 258 | B804801F1A41316700C14718 /* AspectsDemo */, 259 | B804803B1A41316700C14718 /* AspectsDemoTests */, 260 | ); 261 | }; 262 | /* End PBXProject section */ 263 | 264 | /* Begin PBXResourcesBuildPhase section */ 265 | B804801E1A41316700C14718 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | B8E247211A4BE15D009BEC8C /* Images.xcassets in Resources */, 270 | B8E247181A4BE111009BEC8C /* Main.storyboard in Resources */, 271 | B80480371A41316700C14718 /* LaunchScreen.xib in Resources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | B804803A1A41316700C14718 /* Resources */ = { 276 | isa = PBXResourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXResourcesBuildPhase section */ 283 | 284 | /* Begin PBXShellScriptBuildPhase section */ 285 | 730A66D511D52FD1E09307A4 /* Copy Pods Resources */ = { 286 | isa = PBXShellScriptBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | ); 290 | inputPaths = ( 291 | ); 292 | name = "Copy Pods Resources"; 293 | outputPaths = ( 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | shellPath = /bin/sh; 297 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 298 | showEnvVarsInLog = 0; 299 | }; 300 | 7D3C4CCC0776C33BA083923A /* Check Pods Manifest.lock */ = { 301 | isa = PBXShellScriptBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | ); 305 | inputPaths = ( 306 | ); 307 | name = "Check Pods Manifest.lock"; 308 | outputPaths = ( 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | shellPath = /bin/sh; 312 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 313 | showEnvVarsInLog = 0; 314 | }; 315 | /* End PBXShellScriptBuildPhase section */ 316 | 317 | /* Begin PBXSourcesBuildPhase section */ 318 | B804801C1A41316700C14718 /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | B8E247131A4BE111009BEC8C /* AppDelegate.m in Sources */, 323 | B8E247141A4BE111009BEC8C /* MainViewController.m in Sources */, 324 | B8E247171A4BE111009BEC8C /* CustomCell.m in Sources */, 325 | B80480261A41316700C14718 /* main.m in Sources */, 326 | B8E2471D1A4BE132009BEC8C /* GLLogging.m in Sources */, 327 | B8E247161A4BE111009BEC8C /* TableView.m in Sources */, 328 | B8E2471E1A4BE132009BEC8C /* AppDelegate+Logging.m in Sources */, 329 | B8E247151A4BE111009BEC8C /* DetailViewController.m in Sources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | B80480381A41316700C14718 /* Sources */ = { 334 | isa = PBXSourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | B80480431A41316700C14718 /* AspectsDemoTests.m in Sources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | /* End PBXSourcesBuildPhase section */ 342 | 343 | /* Begin PBXTargetDependency section */ 344 | B804803E1A41316700C14718 /* PBXTargetDependency */ = { 345 | isa = PBXTargetDependency; 346 | target = B804801F1A41316700C14718 /* AspectsDemo */; 347 | targetProxy = B804803D1A41316700C14718 /* PBXContainerItemProxy */; 348 | }; 349 | /* End PBXTargetDependency section */ 350 | 351 | /* Begin PBXVariantGroup section */ 352 | B80480351A41316700C14718 /* LaunchScreen.xib */ = { 353 | isa = PBXVariantGroup; 354 | children = ( 355 | B80480361A41316700C14718 /* Base */, 356 | ); 357 | name = LaunchScreen.xib; 358 | sourceTree = ""; 359 | }; 360 | /* End PBXVariantGroup section */ 361 | 362 | /* Begin XCBuildConfiguration section */ 363 | B80480441A41316700C14718 /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ALWAYS_SEARCH_USER_PATHS = NO; 367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 368 | CLANG_CXX_LIBRARY = "libc++"; 369 | CLANG_ENABLE_MODULES = YES; 370 | CLANG_ENABLE_OBJC_ARC = YES; 371 | CLANG_WARN_BOOL_CONVERSION = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 374 | CLANG_WARN_EMPTY_BODY = YES; 375 | CLANG_WARN_ENUM_CONVERSION = YES; 376 | CLANG_WARN_INT_CONVERSION = YES; 377 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 378 | CLANG_WARN_UNREACHABLE_CODE = YES; 379 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 380 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 381 | COPY_PHASE_STRIP = NO; 382 | ENABLE_STRICT_OBJC_MSGSEND = YES; 383 | GCC_C_LANGUAGE_STANDARD = gnu99; 384 | GCC_DYNAMIC_NO_PIC = NO; 385 | GCC_OPTIMIZATION_LEVEL = 0; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 391 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 392 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 393 | GCC_WARN_UNDECLARED_SELECTOR = YES; 394 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 395 | GCC_WARN_UNUSED_FUNCTION = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 398 | MTL_ENABLE_DEBUG_INFO = YES; 399 | ONLY_ACTIVE_ARCH = YES; 400 | SDKROOT = iphoneos; 401 | }; 402 | name = Debug; 403 | }; 404 | B80480451A41316700C14718 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_MODULES = YES; 411 | CLANG_ENABLE_OBJC_ARC = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 419 | CLANG_WARN_UNREACHABLE_CODE = YES; 420 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 421 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 422 | COPY_PHASE_STRIP = YES; 423 | ENABLE_NS_ASSERTIONS = NO; 424 | ENABLE_STRICT_OBJC_MSGSEND = YES; 425 | GCC_C_LANGUAGE_STANDARD = gnu99; 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 433 | MTL_ENABLE_DEBUG_INFO = NO; 434 | SDKROOT = iphoneos; 435 | VALIDATE_PRODUCT = YES; 436 | }; 437 | name = Release; 438 | }; 439 | B80480471A41316700C14718 /* Debug */ = { 440 | isa = XCBuildConfiguration; 441 | baseConfigurationReference = 54A9AA11DEF1D1795C857AAA /* Pods.debug.xcconfig */; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | INFOPLIST_FILE = AspectsDemo/Info.plist; 445 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 446 | PRODUCT_NAME = "$(TARGET_NAME)"; 447 | }; 448 | name = Debug; 449 | }; 450 | B80480481A41316700C14718 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | baseConfigurationReference = 7F1B9497B8CD02693B5E4858 /* Pods.release.xcconfig */; 453 | buildSettings = { 454 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 455 | INFOPLIST_FILE = AspectsDemo/Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | }; 459 | name = Release; 460 | }; 461 | B804804A1A41316700C14718 /* Debug */ = { 462 | isa = XCBuildConfiguration; 463 | buildSettings = { 464 | BUNDLE_LOADER = "$(TEST_HOST)"; 465 | FRAMEWORK_SEARCH_PATHS = ( 466 | "$(SDKROOT)/Developer/Library/Frameworks", 467 | "$(inherited)", 468 | ); 469 | GCC_PREPROCESSOR_DEFINITIONS = ( 470 | "DEBUG=1", 471 | "$(inherited)", 472 | ); 473 | INFOPLIST_FILE = AspectsDemoTests/Info.plist; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 475 | PRODUCT_NAME = "$(TARGET_NAME)"; 476 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AspectsDemo.app/AspectsDemo"; 477 | }; 478 | name = Debug; 479 | }; 480 | B804804B1A41316700C14718 /* Release */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | BUNDLE_LOADER = "$(TEST_HOST)"; 484 | FRAMEWORK_SEARCH_PATHS = ( 485 | "$(SDKROOT)/Developer/Library/Frameworks", 486 | "$(inherited)", 487 | ); 488 | INFOPLIST_FILE = AspectsDemoTests/Info.plist; 489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AspectsDemo.app/AspectsDemo"; 492 | }; 493 | name = Release; 494 | }; 495 | /* End XCBuildConfiguration section */ 496 | 497 | /* Begin XCConfigurationList section */ 498 | B804801B1A41316700C14718 /* Build configuration list for PBXProject "AspectsDemo" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | B80480441A41316700C14718 /* Debug */, 502 | B80480451A41316700C14718 /* Release */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | B80480461A41316700C14718 /* Build configuration list for PBXNativeTarget "AspectsDemo" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | B80480471A41316700C14718 /* Debug */, 511 | B80480481A41316700C14718 /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | B80480491A41316700C14718 /* Build configuration list for PBXNativeTarget "AspectsDemoTests" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | B804804A1A41316700C14718 /* Debug */, 520 | B804804B1A41316700C14718 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | /* End XCConfigurationList section */ 526 | }; 527 | rootObject = B80480181A41316700C14718 /* Project object */; 528 | } 529 | -------------------------------------------------------------------------------- /AspectsDemo/Main.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 | 58 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | archiveVersion 6 | 1 7 | classes 8 | 9 | objectVersion 10 | 46 11 | objects 12 | 13 | 0AC8060DEF70CB6132B3A463 14 | 15 | explicitFileType 16 | archive.ar 17 | includeInIndex 18 | 0 19 | isa 20 | PBXFileReference 21 | path 22 | libPods.a 23 | sourceTree 24 | BUILT_PRODUCTS_DIR 25 | 26 | 0DD15114DE6BCDEF56E4199D 27 | 28 | buildActionMask 29 | 2147483647 30 | files 31 | 32 | C9D67EBEE7BE83FC3C3B5602 33 | 34 | isa 35 | PBXFrameworksBuildPhase 36 | runOnlyForDeploymentPostprocessing 37 | 0 38 | 39 | 171B38074453D7471E927D48 40 | 41 | children 42 | 43 | 82DFAF930A814EC9C95EBBF1 44 | 45 | isa 46 | PBXGroup 47 | name 48 | Frameworks 49 | sourceTree 50 | <group> 51 | 52 | 18628307D6608CCD146E4497 53 | 54 | buildConfigurations 55 | 56 | DABF93E4FD23C3009A9385B6 57 | F557BE5B0DFD3D3BAF9203FB 58 | 59 | defaultConfigurationIsVisible 60 | 0 61 | defaultConfigurationName 62 | Release 63 | isa 64 | XCConfigurationList 65 | 66 | 1A9B42B8967A254C1B981DC2 67 | 68 | buildActionMask 69 | 2147483647 70 | files 71 | 72 | 605A3E57D9E48755851DB02D 73 | 74 | isa 75 | PBXHeadersBuildPhase 76 | runOnlyForDeploymentPostprocessing 77 | 0 78 | 79 | 1BC6CF0EE66A7032229BA714 80 | 81 | includeInIndex 82 | 1 83 | isa 84 | PBXFileReference 85 | lastKnownFileType 86 | sourcecode.c.objc 87 | path 88 | Aspects.m 89 | sourceTree 90 | <group> 91 | 92 | 1F845E67ACEB0B0253959792 93 | 94 | includeInIndex 95 | 1 96 | isa 97 | PBXFileReference 98 | lastKnownFileType 99 | text 100 | path 101 | Pods-acknowledgements.markdown 102 | sourceTree 103 | <group> 104 | 105 | 28C0AC3EF84ACF6F48D535EF 106 | 107 | buildConfigurationList 108 | 18628307D6608CCD146E4497 109 | buildPhases 110 | 111 | B5272E12AB82B0C73E621171 112 | F16F40B30CD3A91F407013A8 113 | 114 | buildRules 115 | 116 | dependencies 117 | 118 | BB1B1C560A87C0666D23D272 119 | 120 | isa 121 | PBXNativeTarget 122 | name 123 | Pods 124 | productName 125 | Pods 126 | productReference 127 | 0AC8060DEF70CB6132B3A463 128 | productType 129 | com.apple.product-type.library.static 130 | 131 | 3647E777FFD17B69D8D218B4 132 | 133 | fileRef 134 | 1BC6CF0EE66A7032229BA714 135 | isa 136 | PBXBuildFile 137 | 138 | 39DA40E1D4C12F5CD3C64659 139 | 140 | includeInIndex 141 | 1 142 | isa 143 | PBXFileReference 144 | lastKnownFileType 145 | text 146 | name 147 | Podfile 148 | path 149 | ../Podfile 150 | sourceTree 151 | SOURCE_ROOT 152 | xcLanguageSpecificationIdentifier 153 | xcode.lang.ruby 154 | 155 | 47E6B33B48DF55E1B1731B5B 156 | 157 | children 158 | 159 | EA1080B7EC3430421FFB24AF 160 | 1BC6CF0EE66A7032229BA714 161 | 4DA49117A8172E37EBE6BB23 162 | 163 | isa 164 | PBXGroup 165 | name 166 | Aspects 167 | path 168 | Aspects 169 | sourceTree 170 | <group> 171 | 172 | 496587D150821CE797098DBA 173 | 174 | baseConfigurationReference 175 | 8AAED7456BDB262EA77D4EC3 176 | buildSettings 177 | 178 | ALWAYS_SEARCH_USER_PATHS 179 | NO 180 | COPY_PHASE_STRIP 181 | NO 182 | DSTROOT 183 | /tmp/xcodeproj.dst 184 | GCC_DYNAMIC_NO_PIC 185 | NO 186 | GCC_OPTIMIZATION_LEVEL 187 | 0 188 | GCC_PRECOMPILE_PREFIX_HEADER 189 | YES 190 | GCC_PREFIX_HEADER 191 | Target Support Files/Pods-Aspects/Pods-Aspects-prefix.pch 192 | GCC_PREPROCESSOR_DEFINITIONS 193 | 194 | DEBUG=1 195 | $(inherited) 196 | 197 | GCC_SYMBOLS_PRIVATE_EXTERN 198 | NO 199 | INSTALL_PATH 200 | $(BUILT_PRODUCTS_DIR) 201 | IPHONEOS_DEPLOYMENT_TARGET 202 | 8.1 203 | OTHER_LDFLAGS 204 | 205 | OTHER_LIBTOOLFLAGS 206 | 207 | PRODUCT_NAME 208 | $(TARGET_NAME) 209 | PUBLIC_HEADERS_FOLDER_PATH 210 | $(TARGET_NAME) 211 | SDKROOT 212 | iphoneos 213 | SKIP_INSTALL 214 | YES 215 | 216 | isa 217 | XCBuildConfiguration 218 | name 219 | Debug 220 | 221 | 4DA49117A8172E37EBE6BB23 222 | 223 | children 224 | 225 | 8860328F36C418AF1906D61C 226 | 8AAED7456BDB262EA77D4EC3 227 | C1B0A4045574DE197B4F5EC7 228 | BFCE3235023801B314DF379F 229 | 230 | isa 231 | PBXGroup 232 | name 233 | Support Files 234 | path 235 | ../Target Support Files/Pods-Aspects 236 | sourceTree 237 | <group> 238 | 239 | 5686C5FA4BEAC8522232921A 240 | 241 | buildSettings 242 | 243 | ALWAYS_SEARCH_USER_PATHS 244 | NO 245 | CLANG_CXX_LANGUAGE_STANDARD 246 | gnu++0x 247 | CLANG_CXX_LIBRARY 248 | libc++ 249 | CLANG_ENABLE_MODULES 250 | YES 251 | CLANG_ENABLE_OBJC_ARC 252 | YES 253 | CLANG_WARN_BOOL_CONVERSION 254 | YES 255 | CLANG_WARN_CONSTANT_CONVERSION 256 | YES 257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 258 | YES 259 | CLANG_WARN_EMPTY_BODY 260 | YES 261 | CLANG_WARN_ENUM_CONVERSION 262 | YES 263 | CLANG_WARN_INT_CONVERSION 264 | YES 265 | CLANG_WARN_OBJC_ROOT_CLASS 266 | YES 267 | COPY_PHASE_STRIP 268 | NO 269 | ENABLE_NS_ASSERTIONS 270 | NO 271 | GCC_C_LANGUAGE_STANDARD 272 | gnu99 273 | GCC_PREPROCESSOR_DEFINITIONS 274 | 275 | RELEASE=1 276 | 277 | GCC_WARN_64_TO_32_BIT_CONVERSION 278 | YES 279 | GCC_WARN_ABOUT_RETURN_TYPE 280 | YES 281 | GCC_WARN_UNDECLARED_SELECTOR 282 | YES 283 | GCC_WARN_UNINITIALIZED_AUTOS 284 | YES 285 | GCC_WARN_UNUSED_FUNCTION 286 | YES 287 | GCC_WARN_UNUSED_VARIABLE 288 | YES 289 | IPHONEOS_DEPLOYMENT_TARGET 290 | 8.1 291 | STRIP_INSTALLED_PRODUCT 292 | NO 293 | VALIDATE_PRODUCT 294 | YES 295 | 296 | isa 297 | XCBuildConfiguration 298 | name 299 | Release 300 | 301 | 605A3E57D9E48755851DB02D 302 | 303 | fileRef 304 | EA1080B7EC3430421FFB24AF 305 | isa 306 | PBXBuildFile 307 | 308 | 63422697841FA2F3B1A098AE 309 | 310 | includeInIndex 311 | 1 312 | isa 313 | PBXFileReference 314 | lastKnownFileType 315 | text.plist.xml 316 | path 317 | Pods-acknowledgements.plist 318 | sourceTree 319 | <group> 320 | 321 | 66456CE19420D2291CF68C50 322 | 323 | buildSettings 324 | 325 | ALWAYS_SEARCH_USER_PATHS 326 | NO 327 | CLANG_CXX_LANGUAGE_STANDARD 328 | gnu++0x 329 | CLANG_CXX_LIBRARY 330 | libc++ 331 | CLANG_ENABLE_MODULES 332 | YES 333 | CLANG_ENABLE_OBJC_ARC 334 | YES 335 | CLANG_WARN_BOOL_CONVERSION 336 | YES 337 | CLANG_WARN_CONSTANT_CONVERSION 338 | YES 339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 340 | YES 341 | CLANG_WARN_EMPTY_BODY 342 | YES 343 | CLANG_WARN_ENUM_CONVERSION 344 | YES 345 | CLANG_WARN_INT_CONVERSION 346 | YES 347 | CLANG_WARN_OBJC_ROOT_CLASS 348 | YES 349 | COPY_PHASE_STRIP 350 | YES 351 | GCC_C_LANGUAGE_STANDARD 352 | gnu99 353 | GCC_DYNAMIC_NO_PIC 354 | NO 355 | GCC_OPTIMIZATION_LEVEL 356 | 0 357 | GCC_PREPROCESSOR_DEFINITIONS 358 | 359 | DEBUG=1 360 | $(inherited) 361 | 362 | GCC_SYMBOLS_PRIVATE_EXTERN 363 | NO 364 | GCC_WARN_64_TO_32_BIT_CONVERSION 365 | YES 366 | GCC_WARN_ABOUT_RETURN_TYPE 367 | YES 368 | GCC_WARN_UNDECLARED_SELECTOR 369 | YES 370 | GCC_WARN_UNINITIALIZED_AUTOS 371 | YES 372 | GCC_WARN_UNUSED_FUNCTION 373 | YES 374 | GCC_WARN_UNUSED_VARIABLE 375 | YES 376 | IPHONEOS_DEPLOYMENT_TARGET 377 | 8.1 378 | ONLY_ACTIVE_ARCH 379 | YES 380 | STRIP_INSTALLED_PRODUCT 381 | NO 382 | 383 | isa 384 | XCBuildConfiguration 385 | name 386 | Debug 387 | 388 | 74784EE01CE56B53F7741285 389 | 390 | attributes 391 | 392 | LastUpgradeCheck 393 | 0510 394 | 395 | buildConfigurationList 396 | FB94125DDC897A977F674804 397 | compatibilityVersion 398 | Xcode 3.2 399 | developmentRegion 400 | English 401 | hasScannedForEncodings 402 | 0 403 | isa 404 | PBXProject 405 | knownRegions 406 | 407 | en 408 | 409 | mainGroup 410 | B990E2C1B0C97E5556B8A642 411 | productRefGroup 412 | D139E9900C48416C38139DA3 413 | projectDirPath 414 | 415 | projectReferences 416 | 417 | projectRoot 418 | 419 | targets 420 | 421 | 28C0AC3EF84ACF6F48D535EF 422 | A8566ECB267BA59D3A89735B 423 | 424 | 425 | 76F2CD27F72D3DBA6B3E25D7 426 | 427 | children 428 | 429 | CFB200A142FCA7C2C1FFAA96 430 | 431 | isa 432 | PBXGroup 433 | name 434 | Targets Support Files 435 | sourceTree 436 | <group> 437 | 438 | 82DFAF930A814EC9C95EBBF1 439 | 440 | children 441 | 442 | A46A54154CFECD0CB7677BAD 443 | 444 | isa 445 | PBXGroup 446 | name 447 | iOS 448 | sourceTree 449 | <group> 450 | 451 | 8720DD9D91698A5B77903375 452 | 453 | fileRef 454 | 9892ECCB9A6E542F3E395097 455 | isa 456 | PBXBuildFile 457 | 458 | 877835586A2534B3252A8A72 459 | 460 | baseConfigurationReference 461 | 8AAED7456BDB262EA77D4EC3 462 | buildSettings 463 | 464 | ALWAYS_SEARCH_USER_PATHS 465 | NO 466 | COPY_PHASE_STRIP 467 | YES 468 | DSTROOT 469 | /tmp/xcodeproj.dst 470 | GCC_PRECOMPILE_PREFIX_HEADER 471 | YES 472 | GCC_PREFIX_HEADER 473 | Target Support Files/Pods-Aspects/Pods-Aspects-prefix.pch 474 | INSTALL_PATH 475 | $(BUILT_PRODUCTS_DIR) 476 | IPHONEOS_DEPLOYMENT_TARGET 477 | 8.1 478 | OTHER_CFLAGS 479 | 480 | -DNS_BLOCK_ASSERTIONS=1 481 | $(inherited) 482 | 483 | OTHER_CPLUSPLUSFLAGS 484 | 485 | -DNS_BLOCK_ASSERTIONS=1 486 | $(inherited) 487 | 488 | OTHER_LDFLAGS 489 | 490 | OTHER_LIBTOOLFLAGS 491 | 492 | PRODUCT_NAME 493 | $(TARGET_NAME) 494 | PUBLIC_HEADERS_FOLDER_PATH 495 | $(TARGET_NAME) 496 | SDKROOT 497 | iphoneos 498 | SKIP_INSTALL 499 | YES 500 | VALIDATE_PRODUCT 501 | YES 502 | 503 | isa 504 | XCBuildConfiguration 505 | name 506 | Release 507 | 508 | 8860328F36C418AF1906D61C 509 | 510 | includeInIndex 511 | 1 512 | isa 513 | PBXFileReference 514 | lastKnownFileType 515 | text.xcconfig 516 | path 517 | Pods-Aspects.xcconfig 518 | sourceTree 519 | <group> 520 | 521 | 8A241690BEC26DA30FD65BDE 522 | 523 | buildConfigurations 524 | 525 | 496587D150821CE797098DBA 526 | 877835586A2534B3252A8A72 527 | 528 | defaultConfigurationIsVisible 529 | 0 530 | defaultConfigurationName 531 | Release 532 | isa 533 | XCConfigurationList 534 | 535 | 8AAED7456BDB262EA77D4EC3 536 | 537 | includeInIndex 538 | 1 539 | isa 540 | PBXFileReference 541 | lastKnownFileType 542 | text.xcconfig 543 | path 544 | Pods-Aspects-Private.xcconfig 545 | sourceTree 546 | <group> 547 | 548 | 969770857794A85A2E1C8B71 549 | 550 | fileRef 551 | A46A54154CFECD0CB7677BAD 552 | isa 553 | PBXBuildFile 554 | 555 | 9892ECCB9A6E542F3E395097 556 | 557 | includeInIndex 558 | 1 559 | isa 560 | PBXFileReference 561 | lastKnownFileType 562 | sourcecode.c.objc 563 | path 564 | Pods-dummy.m 565 | sourceTree 566 | <group> 567 | 568 | 9FD08F50018EEA453BCAC598 569 | 570 | includeInIndex 571 | 1 572 | isa 573 | PBXFileReference 574 | lastKnownFileType 575 | text.xcconfig 576 | path 577 | Pods.debug.xcconfig 578 | sourceTree 579 | <group> 580 | 581 | A46A54154CFECD0CB7677BAD 582 | 583 | isa 584 | PBXFileReference 585 | lastKnownFileType 586 | wrapper.framework 587 | name 588 | Foundation.framework 589 | path 590 | Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/Foundation.framework 591 | sourceTree 592 | DEVELOPER_DIR 593 | 594 | A8566ECB267BA59D3A89735B 595 | 596 | buildConfigurationList 597 | 8A241690BEC26DA30FD65BDE 598 | buildPhases 599 | 600 | B2B08F6FAC68915343A7345D 601 | 0DD15114DE6BCDEF56E4199D 602 | 1A9B42B8967A254C1B981DC2 603 | 604 | buildRules 605 | 606 | dependencies 607 | 608 | isa 609 | PBXNativeTarget 610 | name 611 | Pods-Aspects 612 | productName 613 | Pods-Aspects 614 | productReference 615 | AF233DC3629B08BE5E356545 616 | productType 617 | com.apple.product-type.library.static 618 | 619 | AF233DC3629B08BE5E356545 620 | 621 | explicitFileType 622 | archive.ar 623 | includeInIndex 624 | 0 625 | isa 626 | PBXFileReference 627 | path 628 | libPods-Aspects.a 629 | sourceTree 630 | BUILT_PRODUCTS_DIR 631 | 632 | B2B08F6FAC68915343A7345D 633 | 634 | buildActionMask 635 | 2147483647 636 | files 637 | 638 | 3647E777FFD17B69D8D218B4 639 | B4E35BA59AFFE7AAFC0AC692 640 | 641 | isa 642 | PBXSourcesBuildPhase 643 | runOnlyForDeploymentPostprocessing 644 | 0 645 | 646 | B4E35BA59AFFE7AAFC0AC692 647 | 648 | fileRef 649 | C1B0A4045574DE197B4F5EC7 650 | isa 651 | PBXBuildFile 652 | 653 | B5272E12AB82B0C73E621171 654 | 655 | buildActionMask 656 | 2147483647 657 | files 658 | 659 | 8720DD9D91698A5B77903375 660 | 661 | isa 662 | PBXSourcesBuildPhase 663 | runOnlyForDeploymentPostprocessing 664 | 0 665 | 666 | B990E2C1B0C97E5556B8A642 667 | 668 | children 669 | 670 | 39DA40E1D4C12F5CD3C64659 671 | 171B38074453D7471E927D48 672 | BF4ED7FA9CF507E688EEE22B 673 | D139E9900C48416C38139DA3 674 | 76F2CD27F72D3DBA6B3E25D7 675 | 676 | isa 677 | PBXGroup 678 | sourceTree 679 | <group> 680 | 681 | B9E1CF9AAC4C6752CEBC0A75 682 | 683 | includeInIndex 684 | 1 685 | isa 686 | PBXFileReference 687 | lastKnownFileType 688 | text.xcconfig 689 | path 690 | Pods.release.xcconfig 691 | sourceTree 692 | <group> 693 | 694 | BB1B1C560A87C0666D23D272 695 | 696 | isa 697 | PBXTargetDependency 698 | name 699 | Pods-Aspects 700 | target 701 | A8566ECB267BA59D3A89735B 702 | targetProxy 703 | CF7AAEB962CAF923C67BBE80 704 | 705 | BF4ED7FA9CF507E688EEE22B 706 | 707 | children 708 | 709 | 47E6B33B48DF55E1B1731B5B 710 | 711 | isa 712 | PBXGroup 713 | name 714 | Pods 715 | sourceTree 716 | <group> 717 | 718 | BFCE3235023801B314DF379F 719 | 720 | includeInIndex 721 | 1 722 | isa 723 | PBXFileReference 724 | lastKnownFileType 725 | sourcecode.c.h 726 | path 727 | Pods-Aspects-prefix.pch 728 | sourceTree 729 | <group> 730 | 731 | C1B0A4045574DE197B4F5EC7 732 | 733 | includeInIndex 734 | 1 735 | isa 736 | PBXFileReference 737 | lastKnownFileType 738 | sourcecode.c.objc 739 | path 740 | Pods-Aspects-dummy.m 741 | sourceTree 742 | <group> 743 | 744 | C9D67EBEE7BE83FC3C3B5602 745 | 746 | fileRef 747 | A46A54154CFECD0CB7677BAD 748 | isa 749 | PBXBuildFile 750 | 751 | CF7AAEB962CAF923C67BBE80 752 | 753 | containerPortal 754 | 74784EE01CE56B53F7741285 755 | isa 756 | PBXContainerItemProxy 757 | proxyType 758 | 1 759 | remoteGlobalIDString 760 | A8566ECB267BA59D3A89735B 761 | remoteInfo 762 | Pods-Aspects 763 | 764 | CFB200A142FCA7C2C1FFAA96 765 | 766 | children 767 | 768 | 1F845E67ACEB0B0253959792 769 | 63422697841FA2F3B1A098AE 770 | 9892ECCB9A6E542F3E395097 771 | F4D8FBE1476EDB1AA4FBC9F5 772 | F65F741A0435E033B3662613 773 | 9FD08F50018EEA453BCAC598 774 | B9E1CF9AAC4C6752CEBC0A75 775 | 776 | isa 777 | PBXGroup 778 | name 779 | Pods 780 | path 781 | Target Support Files/Pods 782 | sourceTree 783 | <group> 784 | 785 | D139E9900C48416C38139DA3 786 | 787 | children 788 | 789 | 0AC8060DEF70CB6132B3A463 790 | AF233DC3629B08BE5E356545 791 | 792 | isa 793 | PBXGroup 794 | name 795 | Products 796 | sourceTree 797 | <group> 798 | 799 | DABF93E4FD23C3009A9385B6 800 | 801 | baseConfigurationReference 802 | 9FD08F50018EEA453BCAC598 803 | buildSettings 804 | 805 | ALWAYS_SEARCH_USER_PATHS 806 | NO 807 | COPY_PHASE_STRIP 808 | NO 809 | DSTROOT 810 | /tmp/xcodeproj.dst 811 | GCC_DYNAMIC_NO_PIC 812 | NO 813 | GCC_OPTIMIZATION_LEVEL 814 | 0 815 | GCC_PRECOMPILE_PREFIX_HEADER 816 | YES 817 | GCC_PREPROCESSOR_DEFINITIONS 818 | 819 | DEBUG=1 820 | $(inherited) 821 | 822 | GCC_SYMBOLS_PRIVATE_EXTERN 823 | NO 824 | INSTALL_PATH 825 | $(BUILT_PRODUCTS_DIR) 826 | IPHONEOS_DEPLOYMENT_TARGET 827 | 8.1 828 | OTHER_LDFLAGS 829 | 830 | OTHER_LIBTOOLFLAGS 831 | 832 | PRODUCT_NAME 833 | $(TARGET_NAME) 834 | PUBLIC_HEADERS_FOLDER_PATH 835 | $(TARGET_NAME) 836 | SDKROOT 837 | iphoneos 838 | SKIP_INSTALL 839 | YES 840 | 841 | isa 842 | XCBuildConfiguration 843 | name 844 | Debug 845 | 846 | EA1080B7EC3430421FFB24AF 847 | 848 | includeInIndex 849 | 1 850 | isa 851 | PBXFileReference 852 | lastKnownFileType 853 | sourcecode.c.h 854 | path 855 | Aspects.h 856 | sourceTree 857 | <group> 858 | 859 | F16F40B30CD3A91F407013A8 860 | 861 | buildActionMask 862 | 2147483647 863 | files 864 | 865 | 969770857794A85A2E1C8B71 866 | 867 | isa 868 | PBXFrameworksBuildPhase 869 | runOnlyForDeploymentPostprocessing 870 | 0 871 | 872 | F4D8FBE1476EDB1AA4FBC9F5 873 | 874 | includeInIndex 875 | 1 876 | isa 877 | PBXFileReference 878 | lastKnownFileType 879 | sourcecode.c.h 880 | path 881 | Pods-environment.h 882 | sourceTree 883 | <group> 884 | 885 | F557BE5B0DFD3D3BAF9203FB 886 | 887 | baseConfigurationReference 888 | B9E1CF9AAC4C6752CEBC0A75 889 | buildSettings 890 | 891 | ALWAYS_SEARCH_USER_PATHS 892 | NO 893 | COPY_PHASE_STRIP 894 | YES 895 | DSTROOT 896 | /tmp/xcodeproj.dst 897 | GCC_PRECOMPILE_PREFIX_HEADER 898 | YES 899 | INSTALL_PATH 900 | $(BUILT_PRODUCTS_DIR) 901 | IPHONEOS_DEPLOYMENT_TARGET 902 | 8.1 903 | OTHER_CFLAGS 904 | 905 | -DNS_BLOCK_ASSERTIONS=1 906 | $(inherited) 907 | 908 | OTHER_CPLUSPLUSFLAGS 909 | 910 | -DNS_BLOCK_ASSERTIONS=1 911 | $(inherited) 912 | 913 | OTHER_LDFLAGS 914 | 915 | OTHER_LIBTOOLFLAGS 916 | 917 | PRODUCT_NAME 918 | $(TARGET_NAME) 919 | PUBLIC_HEADERS_FOLDER_PATH 920 | $(TARGET_NAME) 921 | SDKROOT 922 | iphoneos 923 | SKIP_INSTALL 924 | YES 925 | VALIDATE_PRODUCT 926 | YES 927 | 928 | isa 929 | XCBuildConfiguration 930 | name 931 | Release 932 | 933 | F65F741A0435E033B3662613 934 | 935 | includeInIndex 936 | 1 937 | isa 938 | PBXFileReference 939 | lastKnownFileType 940 | text.script.sh 941 | path 942 | Pods-resources.sh 943 | sourceTree 944 | <group> 945 | 946 | FB94125DDC897A977F674804 947 | 948 | buildConfigurations 949 | 950 | 66456CE19420D2291CF68C50 951 | 5686C5FA4BEAC8522232921A 952 | 953 | defaultConfigurationIsVisible 954 | 0 955 | defaultConfigurationName 956 | Release 957 | isa 958 | XCConfigurationList 959 | 960 | 961 | rootObject 962 | 74784EE01CE56B53F7741285 963 | 964 | 965 | -------------------------------------------------------------------------------- /Pods/Aspects/Aspects.m: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.m 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import "Aspects.h" 9 | #import 10 | #import 11 | #import 12 | 13 | #define AspectLog(...) 14 | //#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0) 15 | #define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0) 16 | 17 | // Block internals. 18 | typedef NS_OPTIONS(int, AspectBlockFlags) { 19 | AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25), 20 | AspectBlockFlagsHasSignature = (1 << 30) 21 | }; 22 | typedef struct _AspectBlock { 23 | __unused Class isa; 24 | AspectBlockFlags flags; 25 | __unused int reserved; 26 | void (__unused *invoke)(struct _AspectBlock *block, ...); 27 | struct { 28 | unsigned long int reserved; 29 | unsigned long int size; 30 | // requires AspectBlockFlagsHasCopyDisposeHelpers 31 | void (*copy)(void *dst, const void *src); 32 | void (*dispose)(const void *); 33 | // requires AspectBlockFlagsHasSignature 34 | const char *signature; 35 | const char *layout; 36 | } *descriptor; 37 | // imported variables 38 | } *AspectBlockRef; 39 | 40 | @interface AspectInfo : NSObject 41 | - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation; 42 | @property (nonatomic, unsafe_unretained, readonly) id instance; 43 | @property (nonatomic, strong, readonly) NSArray *arguments; 44 | @property (nonatomic, strong, readonly) NSInvocation *originalInvocation; 45 | @end 46 | 47 | // Tracks a single aspect. 48 | @interface AspectIdentifier : NSObject 49 | + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error; 50 | - (BOOL)invokeWithInfo:(id)info; 51 | @property (nonatomic, assign) SEL selector; 52 | @property (nonatomic, strong) id block; 53 | @property (nonatomic, strong) NSMethodSignature *blockSignature; 54 | @property (nonatomic, weak) id object; 55 | @property (nonatomic, assign) AspectOptions options; 56 | @end 57 | 58 | // Tracks all aspects for an object/class. 59 | @interface AspectsContainer : NSObject 60 | - (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)injectPosition; 61 | - (BOOL)removeAspect:(id)aspect; 62 | - (BOOL)hasAspects; 63 | @property (atomic, copy) NSArray *beforeAspects; 64 | @property (atomic, copy) NSArray *insteadAspects; 65 | @property (atomic, copy) NSArray *afterAspects; 66 | @end 67 | 68 | @interface AspectTracker : NSObject 69 | - (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent; 70 | @property (nonatomic, strong) Class trackedClass; 71 | @property (nonatomic, strong) NSMutableSet *selectorNames; 72 | @property (nonatomic, weak) AspectTracker *parentEntry; 73 | @end 74 | 75 | @interface NSInvocation (Aspects) 76 | - (NSArray *)aspects_arguments; 77 | @end 78 | 79 | #define AspectPositionFilter 0x07 80 | 81 | #define AspectError(errorCode, errorDescription) do { \ 82 | AspectLogError(@"Aspects: %@", errorDescription); \ 83 | if (error) { *error = [NSError errorWithDomain:AspectErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; }}while(0) 84 | 85 | NSString *const AspectErrorDomain = @"AspectErrorDomain"; 86 | static NSString *const AspectsSubclassSuffix = @"_Aspects_"; 87 | static NSString *const AspectsMessagePrefix = @"aspects_"; 88 | 89 | @implementation NSObject (Aspects) 90 | 91 | /////////////////////////////////////////////////////////////////////////////////////////// 92 | #pragma mark - Public Aspects API 93 | 94 | + (id)aspect_hookSelector:(SEL)selector 95 | withOptions:(AspectOptions)options 96 | usingBlock:(id)block 97 | error:(NSError **)error { 98 | return aspect_add((id)self, selector, options, block, error); 99 | } 100 | 101 | /// @return A token which allows to later deregister the aspect. 102 | - (id)aspect_hookSelector:(SEL)selector 103 | withOptions:(AspectOptions)options 104 | usingBlock:(id)block 105 | error:(NSError **)error { 106 | return aspect_add(self, selector, options, block, error); 107 | } 108 | 109 | /////////////////////////////////////////////////////////////////////////////////////////// 110 | #pragma mark - Private Helper 111 | 112 | static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) { 113 | NSCParameterAssert(self); 114 | NSCParameterAssert(selector); 115 | NSCParameterAssert(block); 116 | 117 | __block AspectIdentifier *identifier = nil; 118 | aspect_performLocked(^{ 119 | if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) { 120 | AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector); 121 | identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error]; 122 | if (identifier) { 123 | [aspectContainer addAspect:identifier withOptions:options]; 124 | 125 | // Modify the class to allow message interception. 126 | aspect_prepareClassAndHookSelector(self, selector, error); 127 | } 128 | } 129 | }); 130 | return identifier; 131 | } 132 | 133 | static BOOL aspect_remove(AspectIdentifier *aspect, NSError **error) { 134 | NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @"Must have correct type."); 135 | 136 | __block BOOL success = NO; 137 | aspect_performLocked(^{ 138 | id self = aspect.object; // strongify 139 | if (self) { 140 | AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector); 141 | success = [aspectContainer removeAspect:aspect]; 142 | 143 | aspect_cleanupHookedClassAndSelector(self, aspect.selector); 144 | // destroy token 145 | aspect.object = nil; 146 | aspect.block = nil; 147 | aspect.selector = NULL; 148 | }else { 149 | NSString *errrorDesc = [NSString stringWithFormat:@"Unable to deregister hook. Object already deallocated: %@", aspect]; 150 | AspectError(AspectErrorRemoveObjectAlreadyDeallocated, errrorDesc); 151 | } 152 | }); 153 | return success; 154 | } 155 | 156 | static void aspect_performLocked(dispatch_block_t block) { 157 | static OSSpinLock aspect_lock = OS_SPINLOCK_INIT; 158 | OSSpinLockLock(&aspect_lock); 159 | block(); 160 | OSSpinLockUnlock(&aspect_lock); 161 | } 162 | 163 | static SEL aspect_aliasForSelector(SEL selector) { 164 | NSCParameterAssert(selector); 165 | return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]); 166 | } 167 | 168 | static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) { 169 | AspectBlockRef layout = (__bridge void *)block; 170 | if (!(layout->flags & AspectBlockFlagsHasSignature)) { 171 | NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block]; 172 | AspectError(AspectErrorMissingBlockSignature, description); 173 | return nil; 174 | } 175 | void *desc = layout->descriptor; 176 | desc += 2 * sizeof(unsigned long int); 177 | if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) { 178 | desc += 2 * sizeof(void *); 179 | } 180 | if (!desc) { 181 | NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block]; 182 | AspectError(AspectErrorMissingBlockSignature, description); 183 | return nil; 184 | } 185 | const char *signature = (*(const char **)desc); 186 | return [NSMethodSignature signatureWithObjCTypes:signature]; 187 | } 188 | 189 | static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) { 190 | NSCParameterAssert(blockSignature); 191 | NSCParameterAssert(object); 192 | NSCParameterAssert(selector); 193 | 194 | BOOL signaturesMatch = YES; 195 | NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector]; 196 | if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) { 197 | signaturesMatch = NO; 198 | }else { 199 | if (blockSignature.numberOfArguments > 1) { 200 | const char *blockType = [blockSignature getArgumentTypeAtIndex:1]; 201 | if (blockType[0] != '@') { 202 | signaturesMatch = NO; 203 | } 204 | } 205 | // Argument 0 is self/block, argument 1 is SEL or id. We start comparing at argument 2. 206 | // The block can have less arguments than the method, that's ok. 207 | if (signaturesMatch) { 208 | for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) { 209 | const char *methodType = [methodSignature getArgumentTypeAtIndex:idx]; 210 | const char *blockType = [blockSignature getArgumentTypeAtIndex:idx]; 211 | // Only compare parameter, not the optional type data. 212 | if (!methodType || !blockType || methodType[0] != blockType[0]) { 213 | signaturesMatch = NO; break; 214 | } 215 | } 216 | } 217 | } 218 | 219 | if (!signaturesMatch) { 220 | NSString *description = [NSString stringWithFormat:@"Blog signature %@ doesn't match %@.", blockSignature, methodSignature]; 221 | AspectError(AspectErrorIncompatibleBlockSignature, description); 222 | return NO; 223 | } 224 | return YES; 225 | } 226 | 227 | /////////////////////////////////////////////////////////////////////////////////////////// 228 | #pragma mark - Class + Selector Preparation 229 | 230 | static BOOL aspect_isMsgForwardIMP(IMP impl) { 231 | return impl == _objc_msgForward 232 | #if !defined(__arm64__) 233 | || impl == (IMP)_objc_msgForward_stret 234 | #endif 235 | ; 236 | } 237 | 238 | static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) { 239 | IMP msgForwardIMP = _objc_msgForward; 240 | #if !defined(__arm64__) 241 | // As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id. 242 | // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html 243 | // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783 244 | // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4) 245 | Method method = class_getInstanceMethod(self.class, selector); 246 | const char *encoding = method_getTypeEncoding(method); 247 | BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B; 248 | if (methodReturnsStructValue) { 249 | @try { 250 | NSUInteger valueSize = 0; 251 | NSGetSizeAndAlignment(encoding, &valueSize, NULL); 252 | 253 | if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) { 254 | methodReturnsStructValue = NO; 255 | } 256 | } @catch (NSException *e) {} 257 | } 258 | if (methodReturnsStructValue) { 259 | msgForwardIMP = (IMP)_objc_msgForward_stret; 260 | } 261 | #endif 262 | return msgForwardIMP; 263 | } 264 | 265 | static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) { 266 | NSCParameterAssert(selector); 267 | Class klass = aspect_hookClass(self, error); 268 | Method targetMethod = class_getInstanceMethod(klass, selector); 269 | IMP targetMethodIMP = method_getImplementation(targetMethod); 270 | if (!aspect_isMsgForwardIMP(targetMethodIMP)) { 271 | // Make a method alias for the existing method implementation, it not already copied. 272 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 273 | SEL aliasSelector = aspect_aliasForSelector(selector); 274 | if (![klass instancesRespondToSelector:aliasSelector]) { 275 | __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding); 276 | NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass); 277 | } 278 | 279 | // We use forwardInvocation to hook in. 280 | class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding); 281 | AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector)); 282 | } 283 | } 284 | 285 | // Will undo the runtime changes made. 286 | static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) { 287 | NSCParameterAssert(self); 288 | NSCParameterAssert(selector); 289 | 290 | Class klass = object_getClass(self); 291 | BOOL isMetaClass = class_isMetaClass(klass); 292 | if (isMetaClass) { 293 | klass = (Class)self; 294 | } 295 | 296 | // Check if the method is marked as forwarded and undo that. 297 | Method targetMethod = class_getInstanceMethod(klass, selector); 298 | IMP targetMethodIMP = method_getImplementation(targetMethod); 299 | if (aspect_isMsgForwardIMP(targetMethodIMP)) { 300 | // Restore the original method implementation. 301 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 302 | SEL aliasSelector = aspect_aliasForSelector(selector); 303 | Method originalMethod = class_getInstanceMethod(klass, aliasSelector); 304 | IMP originalIMP = method_getImplementation(originalMethod); 305 | NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass); 306 | 307 | class_replaceMethod(klass, selector, originalIMP, typeEncoding); 308 | AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector)); 309 | } 310 | 311 | // Deregister global tracked selector 312 | aspect_deregisterTrackedSelector(self, selector); 313 | 314 | // Get the aspect container and check if there are any hooks remaining. Clean up if there are not. 315 | AspectsContainer *container = aspect_getContainerForObject(self, selector); 316 | if (!container.hasAspects) { 317 | // Destroy the container 318 | aspect_destroyContainerForObject(self, selector); 319 | 320 | // Figure out how the class was modified to undo the changes. 321 | NSString *className = NSStringFromClass(klass); 322 | if ([className hasSuffix:AspectsSubclassSuffix]) { 323 | Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]); 324 | NSCAssert(originalClass != nil, @"Original class must exist"); 325 | object_setClass(self, originalClass); 326 | AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass)); 327 | 328 | // We can only dispose the class pair if we can ensure that no instances exist using our subclass. 329 | // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around. 330 | //objc_disposeClassPair(object.class); 331 | }else { 332 | // Class is most likely swizzled in place. Undo that. 333 | if (isMetaClass) { 334 | aspect_undoSwizzleClassInPlace((Class)self); 335 | } 336 | } 337 | } 338 | } 339 | 340 | /////////////////////////////////////////////////////////////////////////////////////////// 341 | #pragma mark - Hook Class 342 | 343 | static Class aspect_hookClass(NSObject *self, NSError **error) { 344 | NSCParameterAssert(self); 345 | Class statedClass = self.class; 346 | Class baseClass = object_getClass(self); 347 | NSString *className = NSStringFromClass(baseClass); 348 | 349 | // Already subclassed 350 | if ([className hasSuffix:AspectsSubclassSuffix]) { 351 | return baseClass; 352 | 353 | // We swizzle a class object, not a single object. 354 | }else if (class_isMetaClass(baseClass)) { 355 | return aspect_swizzleClassInPlace((Class)self); 356 | // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place. 357 | }else if (statedClass != baseClass) { 358 | return aspect_swizzleClassInPlace(baseClass); 359 | } 360 | 361 | // Default case. Create dynamic subclass. 362 | const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String; 363 | Class subclass = objc_getClass(subclassName); 364 | 365 | if (subclass == nil) { 366 | subclass = objc_allocateClassPair(baseClass, subclassName, 0); 367 | if (subclass == nil) { 368 | NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName]; 369 | AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc); 370 | return nil; 371 | } 372 | 373 | aspect_swizzleForwardInvocation(subclass); 374 | aspect_hookedGetClass(subclass, statedClass); 375 | aspect_hookedGetClass(object_getClass(subclass), statedClass); 376 | objc_registerClassPair(subclass); 377 | } 378 | 379 | object_setClass(self, subclass); 380 | return subclass; 381 | } 382 | 383 | static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:"; 384 | static void aspect_swizzleForwardInvocation(Class klass) { 385 | NSCParameterAssert(klass); 386 | // If there is no method, replace will act like class_addMethod. 387 | IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@"); 388 | if (originalImplementation) { 389 | class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@"); 390 | } 391 | AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass)); 392 | } 393 | 394 | static void aspect_undoSwizzleForwardInvocation(Class klass) { 395 | NSCParameterAssert(klass); 396 | Method originalMethod = class_getInstanceMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName)); 397 | Method objectMethod = class_getInstanceMethod(NSObject.class, @selector(forwardInvocation:)); 398 | // There is no class_removeMethod, so the best we can do is to retore the original implementation, or use a dummy. 399 | IMP originalImplementation = method_getImplementation(originalMethod ?: objectMethod); 400 | class_replaceMethod(klass, @selector(forwardInvocation:), originalImplementation, "v@:@"); 401 | 402 | AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(klass)); 403 | } 404 | 405 | static void aspect_hookedGetClass(Class class, Class statedClass) { 406 | NSCParameterAssert(class); 407 | NSCParameterAssert(statedClass); 408 | Method method = class_getInstanceMethod(class, @selector(class)); 409 | IMP newIMP = imp_implementationWithBlock(^(id self) { 410 | return statedClass; 411 | }); 412 | class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method)); 413 | } 414 | 415 | /////////////////////////////////////////////////////////////////////////////////////////// 416 | #pragma mark - Swizzle Class In Place 417 | 418 | static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) { 419 | static NSMutableSet *swizzledClasses; 420 | static dispatch_once_t pred; 421 | dispatch_once(&pred, ^{ 422 | swizzledClasses = [NSMutableSet new]; 423 | }); 424 | @synchronized(swizzledClasses) { 425 | block(swizzledClasses); 426 | } 427 | } 428 | 429 | static Class aspect_swizzleClassInPlace(Class klass) { 430 | NSCParameterAssert(klass); 431 | NSString *className = NSStringFromClass(klass); 432 | 433 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 434 | if (![swizzledClasses containsObject:className]) { 435 | aspect_swizzleForwardInvocation(klass); 436 | [swizzledClasses addObject:className]; 437 | } 438 | }); 439 | return klass; 440 | } 441 | 442 | static void aspect_undoSwizzleClassInPlace(Class klass) { 443 | NSCParameterAssert(klass); 444 | NSString *className = NSStringFromClass(klass); 445 | 446 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 447 | if ([swizzledClasses containsObject:className]) { 448 | aspect_undoSwizzleForwardInvocation(klass); 449 | [swizzledClasses removeObject:className]; 450 | } 451 | }); 452 | } 453 | 454 | /////////////////////////////////////////////////////////////////////////////////////////// 455 | #pragma mark - Aspect Invoke Point 456 | 457 | // This is a macro so we get a cleaner stack trace. 458 | #define aspect_invoke(aspects, info) \ 459 | for (AspectIdentifier *aspect in aspects) {\ 460 | [aspect invokeWithInfo:info];\ 461 | if (aspect.options & AspectOptionAutomaticRemoval) { \ 462 | aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \ 463 | } \ 464 | } 465 | 466 | // This is the swizzled forwardInvocation: method. 467 | static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) { 468 | NSCParameterAssert(self); 469 | NSCParameterAssert(invocation); 470 | SEL originalSelector = invocation.selector; 471 | SEL aliasSelector = aspect_aliasForSelector(invocation.selector); 472 | invocation.selector = aliasSelector; 473 | AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector); 474 | AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector); 475 | AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation]; 476 | NSArray *aspectsToRemove = nil; 477 | 478 | // Before hooks. 479 | aspect_invoke(classContainer.beforeAspects, info); 480 | aspect_invoke(objectContainer.beforeAspects, info); 481 | 482 | // Instead hooks. 483 | BOOL respondsToAlias = YES; 484 | if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) { 485 | aspect_invoke(classContainer.insteadAspects, info); 486 | aspect_invoke(objectContainer.insteadAspects, info); 487 | }else { 488 | Class klass = object_getClass(invocation.target); 489 | do { 490 | if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) { 491 | [invocation invoke]; 492 | break; 493 | } 494 | }while (!respondsToAlias && (klass = class_getSuperclass(klass))); 495 | } 496 | 497 | // After hooks. 498 | aspect_invoke(classContainer.afterAspects, info); 499 | aspect_invoke(objectContainer.afterAspects, info); 500 | 501 | // If no hooks are installed, call original implementation (usually to throw an exception) 502 | if (!respondsToAlias) { 503 | invocation.selector = originalSelector; 504 | SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName); 505 | if ([self respondsToSelector:originalForwardInvocationSEL]) { 506 | ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation); 507 | }else { 508 | [self doesNotRecognizeSelector:invocation.selector]; 509 | } 510 | } 511 | 512 | // Remove any hooks that are queued for deregistration. 513 | [aspectsToRemove makeObjectsPerformSelector:@selector(remove)]; 514 | } 515 | #undef aspect_invoke 516 | 517 | /////////////////////////////////////////////////////////////////////////////////////////// 518 | #pragma mark - Aspect Container Management 519 | 520 | // Loads or creates the aspect container. 521 | static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) { 522 | NSCParameterAssert(self); 523 | SEL aliasSelector = aspect_aliasForSelector(selector); 524 | AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector); 525 | if (!aspectContainer) { 526 | aspectContainer = [AspectsContainer new]; 527 | objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN); 528 | } 529 | return aspectContainer; 530 | } 531 | 532 | static AspectsContainer *aspect_getContainerForClass(Class klass, SEL selector) { 533 | NSCParameterAssert(klass); 534 | AspectsContainer *classContainer = nil; 535 | do { 536 | classContainer = objc_getAssociatedObject(klass, selector); 537 | if (classContainer.hasAspects) break; 538 | }while ((klass = class_getSuperclass(klass))); 539 | 540 | return classContainer; 541 | } 542 | 543 | static void aspect_destroyContainerForObject(id self, SEL selector) { 544 | NSCParameterAssert(self); 545 | SEL aliasSelector = aspect_aliasForSelector(selector); 546 | objc_setAssociatedObject(self, aliasSelector, nil, OBJC_ASSOCIATION_RETAIN); 547 | } 548 | 549 | /////////////////////////////////////////////////////////////////////////////////////////// 550 | #pragma mark - Selector Blacklist Checking 551 | 552 | static NSMutableDictionary *aspect_getSwizzledClassesDict() { 553 | static NSMutableDictionary *swizzledClassesDict; 554 | static dispatch_once_t pred; 555 | dispatch_once(&pred, ^{ 556 | swizzledClassesDict = [NSMutableDictionary new]; 557 | }); 558 | return swizzledClassesDict; 559 | } 560 | 561 | static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) { 562 | static NSSet *disallowedSelectorList; 563 | static dispatch_once_t pred; 564 | dispatch_once(&pred, ^{ 565 | disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil]; 566 | }); 567 | 568 | // Check against the blacklist. 569 | NSString *selectorName = NSStringFromSelector(selector); 570 | if ([disallowedSelectorList containsObject:selectorName]) { 571 | NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName]; 572 | AspectError(AspectErrorSelectorBlacklisted, errorDescription); 573 | return NO; 574 | } 575 | 576 | // Additional checks. 577 | AspectOptions position = options&AspectPositionFilter; 578 | if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) { 579 | NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc."; 580 | AspectError(AspectErrorSelectorDeallocPosition, errorDesc); 581 | return NO; 582 | } 583 | 584 | if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) { 585 | NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName]; 586 | AspectError(AspectErrorDoesNotRespondToSelector, errorDesc); 587 | return NO; 588 | } 589 | 590 | // Search for the current class and the class hierarchy IF we are modifying a class object 591 | if (class_isMetaClass(object_getClass(self))) { 592 | Class klass = [self class]; 593 | NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict(); 594 | Class currentClass = [self class]; 595 | do { 596 | AspectTracker *tracker = swizzledClassesDict[currentClass]; 597 | if ([tracker.selectorNames containsObject:selectorName]) { 598 | 599 | // Find the topmost class for the log. 600 | if (tracker.parentEntry) { 601 | AspectTracker *topmostEntry = tracker.parentEntry; 602 | while (topmostEntry.parentEntry) { 603 | topmostEntry = topmostEntry.parentEntry; 604 | } 605 | NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(topmostEntry.trackedClass)]; 606 | AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription); 607 | return NO; 608 | }else if (klass == currentClass) { 609 | // Already modified and topmost! 610 | return YES; 611 | } 612 | } 613 | }while ((currentClass = class_getSuperclass(currentClass))); 614 | 615 | // Add the selector as being modified. 616 | currentClass = klass; 617 | AspectTracker *parentTracker = nil; 618 | do { 619 | AspectTracker *tracker = swizzledClassesDict[currentClass]; 620 | if (!tracker) { 621 | tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker]; 622 | swizzledClassesDict[(id)currentClass] = tracker; 623 | } 624 | [tracker.selectorNames addObject:selectorName]; 625 | // All superclasses get marked as having a subclass that is modified. 626 | parentTracker = tracker; 627 | }while ((currentClass = class_getSuperclass(currentClass))); 628 | } 629 | 630 | return YES; 631 | } 632 | 633 | static void aspect_deregisterTrackedSelector(id self, SEL selector) { 634 | if (!class_isMetaClass(object_getClass(self))) return; 635 | 636 | NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict(); 637 | NSString *selectorName = NSStringFromSelector(selector); 638 | Class currentClass = [self class]; 639 | do { 640 | AspectTracker *tracker = swizzledClassesDict[currentClass]; 641 | if (tracker) { 642 | [tracker.selectorNames removeObject:selectorName]; 643 | if (tracker.selectorNames.count == 0) { 644 | [swizzledClassesDict removeObjectForKey:tracker]; 645 | } 646 | } 647 | }while ((currentClass = class_getSuperclass(currentClass))); 648 | } 649 | 650 | @end 651 | 652 | @implementation AspectTracker 653 | 654 | - (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent { 655 | if (self = [super init]) { 656 | _trackedClass = trackedClass; 657 | _parentEntry = parent; 658 | _selectorNames = [NSMutableSet new]; 659 | } 660 | return self; 661 | } 662 | - (NSString *)description { 663 | return [NSString stringWithFormat:@"<%@: %@, trackedClass: %@, selectorNames:%@, parent:%p>", self.class, self, NSStringFromClass(self.trackedClass), self.selectorNames, self.parentEntry]; 664 | } 665 | 666 | @end 667 | 668 | /////////////////////////////////////////////////////////////////////////////////////////// 669 | #pragma mark - NSInvocation (Aspects) 670 | 671 | @implementation NSInvocation (Aspects) 672 | 673 | // Thanks to the ReactiveCocoa team for providing a generic solution for this. 674 | - (id)aspect_argumentAtIndex:(NSUInteger)index { 675 | const char *argType = [self.methodSignature getArgumentTypeAtIndex:index]; 676 | // Skip const type qualifier. 677 | if (argType[0] == _C_CONST) argType++; 678 | 679 | #define WRAP_AND_RETURN(type) do { type val = 0; [self getArgument:&val atIndex:(NSInteger)index]; return @(val); } while (0) 680 | if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) { 681 | __autoreleasing id returnObj; 682 | [self getArgument:&returnObj atIndex:(NSInteger)index]; 683 | return returnObj; 684 | } else if (strcmp(argType, @encode(SEL)) == 0) { 685 | SEL selector = 0; 686 | [self getArgument:&selector atIndex:(NSInteger)index]; 687 | return NSStringFromSelector(selector); 688 | } else if (strcmp(argType, @encode(Class)) == 0) { 689 | __autoreleasing Class theClass = Nil; 690 | [self getArgument:&theClass atIndex:(NSInteger)index]; 691 | return theClass; 692 | // Using this list will box the number with the appropriate constructor, instead of the generic NSValue. 693 | } else if (strcmp(argType, @encode(char)) == 0) { 694 | WRAP_AND_RETURN(char); 695 | } else if (strcmp(argType, @encode(int)) == 0) { 696 | WRAP_AND_RETURN(int); 697 | } else if (strcmp(argType, @encode(short)) == 0) { 698 | WRAP_AND_RETURN(short); 699 | } else if (strcmp(argType, @encode(long)) == 0) { 700 | WRAP_AND_RETURN(long); 701 | } else if (strcmp(argType, @encode(long long)) == 0) { 702 | WRAP_AND_RETURN(long long); 703 | } else if (strcmp(argType, @encode(unsigned char)) == 0) { 704 | WRAP_AND_RETURN(unsigned char); 705 | } else if (strcmp(argType, @encode(unsigned int)) == 0) { 706 | WRAP_AND_RETURN(unsigned int); 707 | } else if (strcmp(argType, @encode(unsigned short)) == 0) { 708 | WRAP_AND_RETURN(unsigned short); 709 | } else if (strcmp(argType, @encode(unsigned long)) == 0) { 710 | WRAP_AND_RETURN(unsigned long); 711 | } else if (strcmp(argType, @encode(unsigned long long)) == 0) { 712 | WRAP_AND_RETURN(unsigned long long); 713 | } else if (strcmp(argType, @encode(float)) == 0) { 714 | WRAP_AND_RETURN(float); 715 | } else if (strcmp(argType, @encode(double)) == 0) { 716 | WRAP_AND_RETURN(double); 717 | } else if (strcmp(argType, @encode(BOOL)) == 0) { 718 | WRAP_AND_RETURN(BOOL); 719 | } else if (strcmp(argType, @encode(bool)) == 0) { 720 | WRAP_AND_RETURN(BOOL); 721 | } else if (strcmp(argType, @encode(char *)) == 0) { 722 | WRAP_AND_RETURN(const char *); 723 | } else if (strcmp(argType, @encode(void (^)(void))) == 0) { 724 | __unsafe_unretained id block = nil; 725 | [self getArgument:&block atIndex:(NSInteger)index]; 726 | return [block copy]; 727 | } else { 728 | NSUInteger valueSize = 0; 729 | NSGetSizeAndAlignment(argType, &valueSize, NULL); 730 | 731 | unsigned char valueBytes[valueSize]; 732 | [self getArgument:valueBytes atIndex:(NSInteger)index]; 733 | 734 | return [NSValue valueWithBytes:valueBytes objCType:argType]; 735 | } 736 | return nil; 737 | #undef WRAP_AND_RETURN 738 | } 739 | 740 | - (NSArray *)aspects_arguments { 741 | NSMutableArray *argumentsArray = [NSMutableArray array]; 742 | for (NSUInteger idx = 2; idx < self.methodSignature.numberOfArguments; idx++) { 743 | [argumentsArray addObject:[self aspect_argumentAtIndex:idx] ?: NSNull.null]; 744 | } 745 | return [argumentsArray copy]; 746 | } 747 | 748 | @end 749 | 750 | /////////////////////////////////////////////////////////////////////////////////////////// 751 | #pragma mark - AspectIdentifier 752 | 753 | @implementation AspectIdentifier 754 | 755 | + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error { 756 | NSCParameterAssert(block); 757 | NSCParameterAssert(selector); 758 | NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); // TODO: check signature compatibility, etc. 759 | if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) { 760 | return nil; 761 | } 762 | 763 | AspectIdentifier *identifier = nil; 764 | if (blockSignature) { 765 | identifier = [AspectIdentifier new]; 766 | identifier.selector = selector; 767 | identifier.block = block; 768 | identifier.blockSignature = blockSignature; 769 | identifier.options = options; 770 | identifier.object = object; // weak 771 | } 772 | return identifier; 773 | } 774 | 775 | - (BOOL)invokeWithInfo:(id)info { 776 | NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature]; 777 | NSInvocation *originalInvocation = info.originalInvocation; 778 | NSUInteger numberOfArguments = self.blockSignature.numberOfArguments; 779 | 780 | // Be extra paranoid. We already check that on hook registration. 781 | if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) { 782 | AspectLogError(@"Block has too many arguments. Not calling %@", info); 783 | return NO; 784 | } 785 | 786 | // The `self` of the block will be the AspectInfo. Optional. 787 | if (numberOfArguments > 1) { 788 | [blockInvocation setArgument:&info atIndex:1]; 789 | } 790 | 791 | void *argBuf = NULL; 792 | for (NSUInteger idx = 2; idx < numberOfArguments; idx++) { 793 | const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; 794 | NSUInteger argSize; 795 | NSGetSizeAndAlignment(type, &argSize, NULL); 796 | 797 | if (!(argBuf = reallocf(argBuf, argSize))) { 798 | AspectLogError(@"Failed to allocate memory for block invocation."); 799 | return NO; 800 | } 801 | 802 | [originalInvocation getArgument:argBuf atIndex:idx]; 803 | [blockInvocation setArgument:argBuf atIndex:idx]; 804 | } 805 | 806 | [blockInvocation invokeWithTarget:self.block]; 807 | 808 | if (argBuf != NULL) { 809 | free(argBuf); 810 | } 811 | return YES; 812 | } 813 | 814 | - (NSString *)description { 815 | return [NSString stringWithFormat:@"<%@: %p, SEL:%@ object:%@ options:%tu block:%@ (#%tu args)>", self.class, self, NSStringFromSelector(self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments]; 816 | } 817 | 818 | - (BOOL)remove { 819 | return aspect_remove(self, NULL); 820 | } 821 | 822 | @end 823 | 824 | /////////////////////////////////////////////////////////////////////////////////////////// 825 | #pragma mark - AspectsContainer 826 | 827 | @implementation AspectsContainer 828 | 829 | - (BOOL)hasAspects { 830 | return self.beforeAspects.count > 0 || self.insteadAspects.count > 0 || self.afterAspects.count > 0; 831 | } 832 | 833 | - (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options { 834 | NSParameterAssert(aspect); 835 | NSUInteger position = options&AspectPositionFilter; 836 | switch (position) { 837 | case AspectPositionBefore: self.beforeAspects = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break; 838 | case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break; 839 | case AspectPositionAfter: self.afterAspects = [(self.afterAspects ?:@[]) arrayByAddingObject:aspect]; break; 840 | } 841 | } 842 | 843 | - (BOOL)removeAspect:(id)aspect { 844 | for (NSString *aspectArrayName in @[NSStringFromSelector(@selector(beforeAspects)), 845 | NSStringFromSelector(@selector(insteadAspects)), 846 | NSStringFromSelector(@selector(afterAspects))]) { 847 | NSArray *array = [self valueForKey:aspectArrayName]; 848 | NSUInteger index = [array indexOfObjectIdenticalTo:aspect]; 849 | if (array && index != NSNotFound) { 850 | NSMutableArray *newArray = [NSMutableArray arrayWithArray:array]; 851 | [newArray removeObjectAtIndex:index]; 852 | [self setValue:newArray forKey:aspectArrayName]; 853 | return YES; 854 | } 855 | } 856 | return NO; 857 | } 858 | 859 | - (NSString *)description { 860 | return [NSString stringWithFormat:@"<%@: %p, before:%@, instead:%@, after:%@>", self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects]; 861 | } 862 | 863 | @end 864 | 865 | /////////////////////////////////////////////////////////////////////////////////////////// 866 | #pragma mark - AspectInfo 867 | 868 | @implementation AspectInfo 869 | 870 | @synthesize arguments = _arguments; 871 | 872 | - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation { 873 | NSCParameterAssert(instance); 874 | NSCParameterAssert(invocation); 875 | if (self = [super init]) { 876 | _instance = instance; 877 | _originalInvocation = invocation; 878 | } 879 | return self; 880 | } 881 | 882 | - (NSArray *)arguments { 883 | // Lazily evaluate arguments, boxing is expensive. 884 | if (!_arguments) { 885 | _arguments = self.originalInvocation.aspects_arguments; 886 | } 887 | return _arguments; 888 | } 889 | 890 | @end 891 | --------------------------------------------------------------------------------