├── 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 |
--------------------------------------------------------------------------------