├── LXD_KeyValueObserveDemo.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── linxinda.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── LXD_KeyValueObserveDemo.xcscheme └── project.pbxproj ├── LXD_ObservedObject.m ├── LXD_KeyValueObserveDemo ├── ViewController.h ├── AppDelegate.h ├── main.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── ViewController.m ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.xib └── AppDelegate.m ├── LXD_ObservedObject.h ├── LXD_KeyValueObserveDemoTests ├── Info.plist └── LXD_KeyValueObserveDemoTests.m ├── NSObject+LXD_KVO.h └── NSObject+LXD_KVO.m /LXD_KeyValueObserveDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LXD_ObservedObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXD_ObservedObject.m 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/16. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import "LXD_ObservedObject.h" 10 | 11 | @implementation LXD_ObservedObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LXD_ObservedObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXD_ObservedObject.h 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/16. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LXD_ObservedObject : NSObject 12 | 13 | @property (assign, nonatomic) NSNumber * observedNum; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. 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 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. 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 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LXD_KeyValueObserveDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | EF324E081AB5298300069547 16 | 17 | primary 18 | 19 | 20 | EF324E211AB5298300069547 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/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 | } -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | LXD.$(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 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "NSObject+LXD_KVO.h" 11 | #import "LXD_ObservedObject.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | LXD_ObservedObject * object = [LXD_ObservedObject new]; 23 | [object LXD_addObserver: self forKey: @"observedNum" withBlock: ^(id observedObject, NSString *observedKey, id oldValue, id newValue) { 24 | 25 | NSLog(@"Value had changed yet"); 26 | }]; 27 | 28 | object.observedNum = @10; 29 | } 30 | 31 | - (void)didReceiveMemoryWarning { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemoTests/LXD_KeyValueObserveDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXD_KeyValueObserveDemoTests.m 3 | // LXD_KeyValueObserveDemoTests 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LXD_KeyValueObserveDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation LXD_KeyValueObserveDemoTests 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 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | LXD.$(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 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /NSObject+LXD_KVO.h: -------------------------------------------------------------------------------- 1 | /** 2 | File: NSObject + LXD_KVO 3 | 4 | Abstract: This is category file of NSObject, This file add two method to add observer with class. We can't custom a selector to be invoked when specified event happens. This makes it very easy to searate things from superclass. In another way, we can's sure if the superclass is interested with the same notification as subclass interestes in. 5 | 6 | Detail: How does apple do to implement KVO. The class as observed object actually motify its isa pointer pointing to an intermediate class rather than the true class when an observer is registered for its attribute of object. Surely, apple must override this intermediate class's setter method to invoke the attribute is observed has changed. By the way, apple will also override method named "class" for concealling the true implementation. 7 | 8 | About: This file provide block method instance of default callback method to be invoked by the observer to implement custom selector to be invoked, and also remove the context param cause it really be useless 9 | 10 | Warning: This Key-Value-Observe can only be used for id type in objective-c language, if you use this method to observe a no-object, the programm will crash 11 | 12 | Implement: Create new class includes observer as NSObject * type, key as NSString * type and handler as custom block type to store each notification event. Use Objective-C's runtime to call something operation base on class-structive 13 | 14 | Author: Wrote by John Lin on 2015.3.15 15 | **/ 16 | 17 | // 18 | // NSObject+LXD_KVO.h 19 | // LXD_KeyValueObserveDemo 20 | // 21 | // Created by linxinda on 15/3/15. 22 | // Copyright (c) 2015年 Personal. All rights reserved. 23 | // 24 | 25 | #import 26 | 27 | typedef void (^LXD_ObservingHandler) (id observedObject, NSString * observedKey, id oldValue, id newValue); 28 | 29 | @interface NSObject (LXD_KVO) 30 | 31 | /** 32 | * method stead of traditional addObserver API 33 | * 34 | * @param object object as observer 35 | * @param key attribute of object to be observed 36 | * @param observedHandler method to be invoked when notification be observed has changed 37 | */ 38 | - (void)LXD_addObserver: (NSObject *)object forKey: (NSString *)key withBlock: (LXD_ObservingHandler)observedHandler; 39 | 40 | 41 | /** 42 | * remove the observe 43 | * 44 | * @param object object as observer 45 | * @param key attribute observed will remove the observe 46 | */ 47 | - (void)LXD_removeObserver: (NSObject *)object forKey: (NSString *)key; 48 | 49 | @end -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo/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 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo.xcodeproj/xcuserdata/linxinda.xcuserdatad/xcschemes/LXD_KeyValueObserveDemo.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 | -------------------------------------------------------------------------------- /NSObject+LXD_KVO.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+LXD_KVO.m 3 | // LXD_KeyValueObserveDemo 4 | // 5 | // Created by linxinda on 15/3/15. 6 | // Copyright (c) 2015年 Personal. All rights reserved. 7 | // 8 | 9 | #import "NSObject+LXD_KVO.h" 10 | #import 11 | #import 12 | 13 | //as prefix string of kvo class 14 | static NSString * const kLXDkvoClassPrefix = @"LXDObserver_"; 15 | static NSString * const kLXDkvoAssiociateObserver = @"LXDAssiociateObserver"; 16 | 17 | @interface LXD_ObserverInfo : NSObject 18 | 19 | @property (nonatomic, weak) NSObject * observer; 20 | @property (nonatomic, copy) NSString * key; 21 | @property (nonatomic, copy) LXD_ObservingHandler handler; 22 | 23 | @end 24 | 25 | 26 | 27 | 28 | 29 | @implementation LXD_ObserverInfo 30 | 31 | - (instancetype)initWithObserver: (NSObject *)observer forKey: (NSString *)key observeHandler: (LXD_ObservingHandler)handler 32 | { 33 | if (self = [super init]) { 34 | 35 | _observer = observer; 36 | self.key = key; 37 | self.handler = handler; 38 | } 39 | return self; 40 | } 41 | 42 | @end 43 | 44 | 45 | 46 | #pragma mark -- Debug Method 47 | static NSArray * ClassMethodsName(Class class) 48 | { 49 | NSMutableArray * methodsArr = [NSMutableArray array]; 50 | 51 | unsigned methodCount = 0; 52 | Method * methodList = class_copyMethodList(class, &methodCount); 53 | for (int i = 0; i < methodCount; i++) { 54 | 55 | [methodsArr addObject: NSStringFromSelector(method_getName(methodList[i]))]; 56 | } 57 | free(methodList); 58 | 59 | return methodsArr; 60 | } 61 | 62 | 63 | 64 | #pragma mark -- Transform setter or getter to each other Methods 65 | static NSString * setterForGetter(NSString * getter) 66 | { 67 | if (getter.length <= 0) { return nil; } 68 | NSString * firstString = [[getter substringToIndex: 1] uppercaseString]; 69 | NSString * leaveString = [getter substringFromIndex: 1]; 70 | 71 | return [NSString stringWithFormat: @"set%@%@:", firstString, leaveString]; 72 | } 73 | 74 | 75 | static NSString * getterForSetter(NSString * setter) 76 | { 77 | if (setter.length <= 0 || ![setter hasPrefix: @"set"] || ![setter hasSuffix: @":"]) { 78 | 79 | return nil; 80 | } 81 | 82 | NSRange range = NSMakeRange(3, setter.length - 4); 83 | NSString * getter = [setter substringWithRange: range]; 84 | 85 | NSString * firstString = [[getter substringToIndex: 1] lowercaseString]; 86 | getter = [getter stringByReplacingCharactersInRange: NSMakeRange(0, 1) withString: firstString]; 87 | 88 | return getter; 89 | } 90 | 91 | 92 | #pragma mark -- Override setter and getter Methods 93 | static void KVO_setter(id self, SEL _cmd, id newValue) 94 | { 95 | NSString * setterName = NSStringFromSelector(_cmd); 96 | NSString * getterName = getterForSetter(setterName); 97 | if (!getterName) { 98 | @throw [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat: @"unrecognized selector sent to instance %p", self] userInfo: nil]; 99 | return; 100 | } 101 | 102 | id oldValue = [self valueForKey: getterName]; 103 | struct objc_super superClass = { 104 | .receiver = self, 105 | .super_class = class_getSuperclass(object_getClass(self)) 106 | }; 107 | 108 | [self willChangeValueForKey: getterName]; 109 | void (*objc_msgSendSuperKVO)(void *, SEL, id) = (void *)objc_msgSendSuper; 110 | objc_msgSendSuperKVO(&superClass, _cmd, newValue); 111 | [self didChangeValueForKey: getterName]; 112 | 113 | //获取所有监听回调对象进行回调 114 | NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge const void *)kLXDkvoAssiociateObserver); 115 | for (LXD_ObserverInfo * info in observers) { 116 | if ([info.key isEqualToString: getterName]) { 117 | dispatch_async(dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 118 | info.handler(self, getterName, oldValue, newValue); 119 | }); 120 | } 121 | } 122 | } 123 | 124 | 125 | static Class kvo_Class(id self) 126 | { 127 | return class_getSuperclass(object_getClass(self)); 128 | } 129 | 130 | 131 | 132 | #pragma mark -- NSObject Category(KVO Reconstruct) 133 | @implementation NSObject (LXD_KVO) 134 | 135 | - (void)LXD_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(LXD_ObservingHandler)observedHandler 136 | { 137 | //step 1 get setter method, if not, throw exception 138 | SEL setterSelector = NSSelectorFromString(setterForGetter(key)); 139 | Method setterMethod = class_getInstanceMethod([self class], setterSelector); 140 | if (!setterMethod) { 141 | @throw [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat: @"unrecognized selector sent to instance %@", self] userInfo: nil]; 142 | return; 143 | } 144 | Class observedClass = object_getClass(self); 145 | NSString * className = NSStringFromClass(observedClass); 146 | 147 | //如果被监听者没有LXDObserver_,那么判断是否需要创建新类 148 | if (![className hasPrefix: kLXDkvoClassPrefix]) { 149 | observedClass = [self createKVOClassWithOriginalClassName: className]; 150 | object_setClass(self, observedClass); 151 | } 152 | 153 | 154 | //add kvo setter method if its class(or superclass)hasn't implement setter 155 | if (![self hasSelector: setterSelector]) { 156 | const char * types = method_getTypeEncoding(setterMethod); 157 | class_addMethod(observedClass, setterSelector, (IMP)KVO_setter, types); 158 | } 159 | 160 | 161 | //add this observation info to saved new observer 162 | LXD_ObserverInfo * newInfo = [[LXD_ObserverInfo alloc] initWithObserver: observer forKey: key observeHandler: observedHandler]; 163 | NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kLXDkvoAssiociateObserver); 164 | 165 | if (!observers) { 166 | observers = [NSMutableArray array]; 167 | objc_setAssociatedObject(self, (__bridge void *)kLXDkvoAssiociateObserver, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 168 | } 169 | [observers addObject: newInfo]; 170 | } 171 | 172 | 173 | - (void)LXD_removeObserver:(NSObject *)object forKey:(NSString *)key 174 | { 175 | NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kLXDkvoAssiociateObserver); 176 | 177 | LXD_ObserverInfo * observerRemoved = nil; 178 | for (LXD_ObserverInfo * observerInfo in observers) { 179 | 180 | if (observerInfo.observer == object && [observerInfo.key isEqualToString: key]) { 181 | 182 | observerRemoved = observerInfo; 183 | break; 184 | } 185 | } 186 | [observers removeObject: observerRemoved]; 187 | } 188 | 189 | 190 | - (Class)createKVOClassWithOriginalClassName: (NSString *)className 191 | { 192 | NSString * kvoClassName = [kLXDkvoClassPrefix stringByAppendingString: className]; 193 | Class observedClass = NSClassFromString(kvoClassName); 194 | 195 | if (observedClass) { return observedClass; } 196 | 197 | //创建新类,并且添加LXDObserver_为类名新前缀 198 | Class originalClass = object_getClass(self); 199 | Class kvoClass = objc_allocateClassPair(originalClass, kvoClassName.UTF8String, 0); 200 | 201 | //获取监听对象的class方法实现代码,然后替换新建类的class实现 202 | Method classMethod = class_getInstanceMethod(originalClass, @selector(class)); 203 | const char * types = method_getTypeEncoding(classMethod); 204 | class_addMethod(kvoClass, @selector(class), (IMP)kvo_Class, types); 205 | objc_registerClassPair(kvoClass); 206 | return kvoClass; 207 | } 208 | 209 | 210 | - (BOOL)hasSelector: (SEL)selector 211 | { 212 | Class observedClass = object_getClass(self); 213 | unsigned int methodCount = 0; 214 | Method * methodList = class_copyMethodList(observedClass, &methodCount); 215 | for (int i = 0; i < methodCount; i++) { 216 | 217 | SEL thisSelector = method_getName(methodList[i]); 218 | if (thisSelector == selector) { 219 | 220 | free(methodList); 221 | return YES; 222 | } 223 | } 224 | 225 | free(methodList); 226 | return NO; 227 | } 228 | 229 | 230 | @end 231 | -------------------------------------------------------------------------------- /LXD_KeyValueObserveDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | EF324E0F1AB5298300069547 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = EF324E0E1AB5298300069547 /* main.m */; }; 11 | EF324E121AB5298300069547 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EF324E111AB5298300069547 /* AppDelegate.m */; }; 12 | EF324E151AB5298300069547 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF324E141AB5298300069547 /* ViewController.m */; }; 13 | EF324E181AB5298300069547 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EF324E161AB5298300069547 /* Main.storyboard */; }; 14 | EF324E1A1AB5298300069547 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EF324E191AB5298300069547 /* Images.xcassets */; }; 15 | EF324E1D1AB5298300069547 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = EF324E1B1AB5298300069547 /* LaunchScreen.xib */; }; 16 | EF324E291AB5298300069547 /* LXD_KeyValueObserveDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = EF324E281AB5298300069547 /* LXD_KeyValueObserveDemoTests.m */; }; 17 | EF324E341AB52AFD00069547 /* NSObject+LXD_KVO.m in Sources */ = {isa = PBXBuildFile; fileRef = EF324E331AB52AFD00069547 /* NSObject+LXD_KVO.m */; }; 18 | EFD71F8D1AB6643500E919CB /* LXD_ObservedObject.m in Sources */ = {isa = PBXBuildFile; fileRef = EFD71F8C1AB6643500E919CB /* LXD_ObservedObject.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | EF324E231AB5298300069547 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = EF324E011AB5298300069547 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = EF324E081AB5298300069547; 27 | remoteInfo = LXD_KeyValueObserveDemo; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | EF324E091AB5298300069547 /* LXD_KeyValueObserveDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LXD_KeyValueObserveDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | EF324E0D1AB5298300069547 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | EF324E0E1AB5298300069547 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | EF324E101AB5298300069547 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36 | EF324E111AB5298300069547 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 37 | EF324E131AB5298300069547 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 38 | EF324E141AB5298300069547 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 39 | EF324E171AB5298300069547 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | EF324E191AB5298300069547 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 41 | EF324E1C1AB5298300069547 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 42 | EF324E221AB5298300069547 /* LXD_KeyValueObserveDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LXD_KeyValueObserveDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | EF324E271AB5298300069547 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | EF324E281AB5298300069547 /* LXD_KeyValueObserveDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LXD_KeyValueObserveDemoTests.m; sourceTree = ""; }; 45 | EF324E321AB52AFD00069547 /* NSObject+LXD_KVO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+LXD_KVO.h"; sourceTree = ""; }; 46 | EF324E331AB52AFD00069547 /* NSObject+LXD_KVO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+LXD_KVO.m"; sourceTree = ""; }; 47 | EFD71F8B1AB6643500E919CB /* LXD_ObservedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXD_ObservedObject.h; sourceTree = ""; }; 48 | EFD71F8C1AB6643500E919CB /* LXD_ObservedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXD_ObservedObject.m; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | EF324E061AB5298300069547 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | EF324E1F1AB5298300069547 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | EF324E001AB5298300069547 = { 70 | isa = PBXGroup; 71 | children = ( 72 | EFD71F8B1AB6643500E919CB /* LXD_ObservedObject.h */, 73 | EFD71F8C1AB6643500E919CB /* LXD_ObservedObject.m */, 74 | EF324E321AB52AFD00069547 /* NSObject+LXD_KVO.h */, 75 | EF324E331AB52AFD00069547 /* NSObject+LXD_KVO.m */, 76 | EF324E0B1AB5298300069547 /* LXD_KeyValueObserveDemo */, 77 | EF324E251AB5298300069547 /* LXD_KeyValueObserveDemoTests */, 78 | EF324E0A1AB5298300069547 /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | EF324E0A1AB5298300069547 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | EF324E091AB5298300069547 /* LXD_KeyValueObserveDemo.app */, 86 | EF324E221AB5298300069547 /* LXD_KeyValueObserveDemoTests.xctest */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | EF324E0B1AB5298300069547 /* LXD_KeyValueObserveDemo */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | EF324E101AB5298300069547 /* AppDelegate.h */, 95 | EF324E111AB5298300069547 /* AppDelegate.m */, 96 | EF324E131AB5298300069547 /* ViewController.h */, 97 | EF324E141AB5298300069547 /* ViewController.m */, 98 | EF324E161AB5298300069547 /* Main.storyboard */, 99 | EF324E191AB5298300069547 /* Images.xcassets */, 100 | EF324E1B1AB5298300069547 /* LaunchScreen.xib */, 101 | EF324E0C1AB5298300069547 /* Supporting Files */, 102 | ); 103 | path = LXD_KeyValueObserveDemo; 104 | sourceTree = ""; 105 | }; 106 | EF324E0C1AB5298300069547 /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | EF324E0D1AB5298300069547 /* Info.plist */, 110 | EF324E0E1AB5298300069547 /* main.m */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | EF324E251AB5298300069547 /* LXD_KeyValueObserveDemoTests */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | EF324E281AB5298300069547 /* LXD_KeyValueObserveDemoTests.m */, 119 | EF324E261AB5298300069547 /* Supporting Files */, 120 | ); 121 | path = LXD_KeyValueObserveDemoTests; 122 | sourceTree = ""; 123 | }; 124 | EF324E261AB5298300069547 /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | EF324E271AB5298300069547 /* Info.plist */, 128 | ); 129 | name = "Supporting Files"; 130 | sourceTree = ""; 131 | }; 132 | /* End PBXGroup section */ 133 | 134 | /* Begin PBXNativeTarget section */ 135 | EF324E081AB5298300069547 /* LXD_KeyValueObserveDemo */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = EF324E2C1AB5298300069547 /* Build configuration list for PBXNativeTarget "LXD_KeyValueObserveDemo" */; 138 | buildPhases = ( 139 | EF324E051AB5298300069547 /* Sources */, 140 | EF324E061AB5298300069547 /* Frameworks */, 141 | EF324E071AB5298300069547 /* Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = LXD_KeyValueObserveDemo; 148 | productName = LXD_KeyValueObserveDemo; 149 | productReference = EF324E091AB5298300069547 /* LXD_KeyValueObserveDemo.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | EF324E211AB5298300069547 /* LXD_KeyValueObserveDemoTests */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = EF324E2F1AB5298300069547 /* Build configuration list for PBXNativeTarget "LXD_KeyValueObserveDemoTests" */; 155 | buildPhases = ( 156 | EF324E1E1AB5298300069547 /* Sources */, 157 | EF324E1F1AB5298300069547 /* Frameworks */, 158 | EF324E201AB5298300069547 /* Resources */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | EF324E241AB5298300069547 /* PBXTargetDependency */, 164 | ); 165 | name = LXD_KeyValueObserveDemoTests; 166 | productName = LXD_KeyValueObserveDemoTests; 167 | productReference = EF324E221AB5298300069547 /* LXD_KeyValueObserveDemoTests.xctest */; 168 | productType = "com.apple.product-type.bundle.unit-test"; 169 | }; 170 | /* End PBXNativeTarget section */ 171 | 172 | /* Begin PBXProject section */ 173 | EF324E011AB5298300069547 /* Project object */ = { 174 | isa = PBXProject; 175 | attributes = { 176 | LastUpgradeCheck = 0600; 177 | ORGANIZATIONNAME = Personal; 178 | TargetAttributes = { 179 | EF324E081AB5298300069547 = { 180 | CreatedOnToolsVersion = 6.0; 181 | }; 182 | EF324E211AB5298300069547 = { 183 | CreatedOnToolsVersion = 6.0; 184 | TestTargetID = EF324E081AB5298300069547; 185 | }; 186 | }; 187 | }; 188 | buildConfigurationList = EF324E041AB5298300069547 /* Build configuration list for PBXProject "LXD_KeyValueObserveDemo" */; 189 | compatibilityVersion = "Xcode 3.2"; 190 | developmentRegion = English; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | Base, 195 | ); 196 | mainGroup = EF324E001AB5298300069547; 197 | productRefGroup = EF324E0A1AB5298300069547 /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | EF324E081AB5298300069547 /* LXD_KeyValueObserveDemo */, 202 | EF324E211AB5298300069547 /* LXD_KeyValueObserveDemoTests */, 203 | ); 204 | }; 205 | /* End PBXProject section */ 206 | 207 | /* Begin PBXResourcesBuildPhase section */ 208 | EF324E071AB5298300069547 /* Resources */ = { 209 | isa = PBXResourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | EF324E181AB5298300069547 /* Main.storyboard in Resources */, 213 | EF324E1D1AB5298300069547 /* LaunchScreen.xib in Resources */, 214 | EF324E1A1AB5298300069547 /* Images.xcassets in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | EF324E201AB5298300069547 /* Resources */ = { 219 | isa = PBXResourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXResourcesBuildPhase section */ 226 | 227 | /* Begin PBXSourcesBuildPhase section */ 228 | EF324E051AB5298300069547 /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | EFD71F8D1AB6643500E919CB /* LXD_ObservedObject.m in Sources */, 233 | EF324E151AB5298300069547 /* ViewController.m in Sources */, 234 | EF324E121AB5298300069547 /* AppDelegate.m in Sources */, 235 | EF324E0F1AB5298300069547 /* main.m in Sources */, 236 | EF324E341AB52AFD00069547 /* NSObject+LXD_KVO.m in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | EF324E1E1AB5298300069547 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | EF324E291AB5298300069547 /* LXD_KeyValueObserveDemoTests.m in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXTargetDependency section */ 251 | EF324E241AB5298300069547 /* PBXTargetDependency */ = { 252 | isa = PBXTargetDependency; 253 | target = EF324E081AB5298300069547 /* LXD_KeyValueObserveDemo */; 254 | targetProxy = EF324E231AB5298300069547 /* PBXContainerItemProxy */; 255 | }; 256 | /* End PBXTargetDependency section */ 257 | 258 | /* Begin PBXVariantGroup section */ 259 | EF324E161AB5298300069547 /* Main.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | EF324E171AB5298300069547 /* Base */, 263 | ); 264 | name = Main.storyboard; 265 | sourceTree = ""; 266 | }; 267 | EF324E1B1AB5298300069547 /* LaunchScreen.xib */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | EF324E1C1AB5298300069547 /* Base */, 271 | ); 272 | name = LaunchScreen.xib; 273 | sourceTree = ""; 274 | }; 275 | /* End PBXVariantGroup section */ 276 | 277 | /* Begin XCBuildConfiguration section */ 278 | EF324E2A1AB5298300069547 /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ALWAYS_SEARCH_USER_PATHS = NO; 282 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 283 | CLANG_CXX_LIBRARY = "libc++"; 284 | CLANG_ENABLE_MODULES = YES; 285 | CLANG_ENABLE_OBJC_ARC = YES; 286 | CLANG_WARN_BOOL_CONVERSION = YES; 287 | CLANG_WARN_CONSTANT_CONVERSION = YES; 288 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | ENABLE_STRICT_OBJC_MSGSEND = YES; 298 | GCC_C_LANGUAGE_STANDARD = gnu99; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_OPTIMIZATION_LEVEL = 0; 301 | GCC_PREPROCESSOR_DEFINITIONS = ( 302 | "DEBUG=1", 303 | "$(inherited)", 304 | ); 305 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 313 | MTL_ENABLE_DEBUG_INFO = YES; 314 | ONLY_ACTIVE_ARCH = YES; 315 | SDKROOT = iphoneos; 316 | }; 317 | name = Debug; 318 | }; 319 | EF324E2B1AB5298300069547 /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 337 | COPY_PHASE_STRIP = YES; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 348 | MTL_ENABLE_DEBUG_INFO = NO; 349 | SDKROOT = iphoneos; 350 | VALIDATE_PRODUCT = YES; 351 | }; 352 | name = Release; 353 | }; 354 | EF324E2D1AB5298300069547 /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | INFOPLIST_FILE = LXD_KeyValueObserveDemo/Info.plist; 359 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | }; 362 | name = Debug; 363 | }; 364 | EF324E2E1AB5298300069547 /* Release */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 368 | INFOPLIST_FILE = LXD_KeyValueObserveDemo/Info.plist; 369 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | }; 372 | name = Release; 373 | }; 374 | EF324E301AB5298300069547 /* Debug */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | BUNDLE_LOADER = "$(TEST_HOST)"; 378 | FRAMEWORK_SEARCH_PATHS = ( 379 | "$(SDKROOT)/Developer/Library/Frameworks", 380 | "$(inherited)", 381 | ); 382 | GCC_PREPROCESSOR_DEFINITIONS = ( 383 | "DEBUG=1", 384 | "$(inherited)", 385 | ); 386 | INFOPLIST_FILE = LXD_KeyValueObserveDemoTests/Info.plist; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXD_KeyValueObserveDemo.app/LXD_KeyValueObserveDemo"; 390 | }; 391 | name = Debug; 392 | }; 393 | EF324E311AB5298300069547 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | BUNDLE_LOADER = "$(TEST_HOST)"; 397 | FRAMEWORK_SEARCH_PATHS = ( 398 | "$(SDKROOT)/Developer/Library/Frameworks", 399 | "$(inherited)", 400 | ); 401 | INFOPLIST_FILE = LXD_KeyValueObserveDemoTests/Info.plist; 402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXD_KeyValueObserveDemo.app/LXD_KeyValueObserveDemo"; 405 | }; 406 | name = Release; 407 | }; 408 | /* End XCBuildConfiguration section */ 409 | 410 | /* Begin XCConfigurationList section */ 411 | EF324E041AB5298300069547 /* Build configuration list for PBXProject "LXD_KeyValueObserveDemo" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | EF324E2A1AB5298300069547 /* Debug */, 415 | EF324E2B1AB5298300069547 /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | EF324E2C1AB5298300069547 /* Build configuration list for PBXNativeTarget "LXD_KeyValueObserveDemo" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | EF324E2D1AB5298300069547 /* Debug */, 424 | EF324E2E1AB5298300069547 /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | EF324E2F1AB5298300069547 /* Build configuration list for PBXNativeTarget "LXD_KeyValueObserveDemoTests" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | EF324E301AB5298300069547 /* Debug */, 433 | EF324E311AB5298300069547 /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | defaultConfigurationName = Release; 437 | }; 438 | /* End XCConfigurationList section */ 439 | }; 440 | rootObject = EF324E011AB5298300069547 /* Project object */; 441 | } 442 | --------------------------------------------------------------------------------