在尝试遵循 MVC 模式时,我在实现键值观察器时遇到了问题。我有一个 Controller 类、一个模型类和一个 View 类。我从 Controller 类更新我的模型,我想在我的 View 类中放置一个键值观察器来监视 NSMutableArray 何时在模型中发生变化(比如通过 addObject),然后自动重绘自身。我在此线程中使用答案来指导我:How to add observer on NSMutableArray?
到目前为止的代码:
来 self 的场景(如果重要的话使用 sprite kit)。字母的设置将从 Ctrl 类完成,这只是为了测试。
BarCtrl *barCtrl = [[BarCtrl alloc] init];
BarModel *barModel = [[BarModel alloc] init];
BarView *barView = [[BarView alloc] init];
barCtrl.barModel = barModel;
barCtrl.barView = barView;
barView.barModel = barModel;
ScrabbleDeck *sd = [[ScrabbleDeck alloc] init];
if([barModel addLetter:[sd getLetter] onSide:BarModelSideRight])
NSLog(@"Added letter");
条形图.h
#import <Foundation/Foundation.h>
#import "Letter.h"
typedef NS_ENUM(int, BarModelSide) {
BarModelSideLeft,
BarModelSideRight
};
@interface BarModel : NSObject
@property (nonatomic, strong) NSMutableArray *addedLetters;
- (instancetype)init;
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side;
@end
条形图.m
#import "BarModel.h"
@interface BarModel ()
@property (nonatomic) int capacity;
@end
@implementation BarModel
- (instancetype)init
{
self = [super init];
if (self) {
self.capacity = letterCapacity;
_addedLetters = [[NSMutableArray alloc] init];
}
return self;
}
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:@"arrayLetter"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side{
if([_addedLetters count] > _capacity){
return FALSE;
}
switch (side) {
case BarModelSideLeft:
[_addedLetters insertObject:letter atIndex:0];
return TRUE;
break;
case BarModelSideRight:
[_addedLetters addObject:letter];
return TRUE;
break;
default:
return FALSE;
break;
}
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.addedLetters[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.addedLetters removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.addedLetters[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.addedLetters objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.addedLetters count];
}
@end
BarView.h
#import <SpriteKit/SpriteKit.h>
#import "BarModel.h"
@interface BarView : SKSpriteNode
@property (nonatomic, strong) BarModel *barModel;
@end
BarView.m
#import "BarView.h"
@implementation BarView
static char MyObservationContext;
- (instancetype)init
{
self = [super init];
if (self) {
//_barModel = [[BarModel alloc] init];
}
return self;
}
-(void)setBarModel:(BarModel *)barModel{
if(_barModel != barModel)
_barModel = barModel;
[_barModel addObserver:self
forKeyPath:@"arrayLetter"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:@"arrayLetter"]) {
// Verify we're observing the correct object
if (object == self.barModel) {
[self draw:change];
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) draw: (NSDictionary*) change{
NSLog(@"KVO for our container property, change dictionary is %@", change);
}
@end
当我运行这个时,我得到这个“错误”:
2014-08-31 00:23:02.828 Testing[329:60b] Added letter
2014-08-31 00:23:02.830 Testing[329:60b] An instance 0x17803d340 of class BarModel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x17804eb50> (
<NSKeyValueObservance 0x1780cf180: Observer: 0x178111670, Key path: arrayLetter, Options: <New: YES, Old: YES, Prior: NO> Context: 0x100101428, Property: 0x17804eb80>
我试图按照错误的说明进行操作,但找不到设置断点的位置。请帮我解决这个问题!
最佳答案
该错误非常具有描述性。您将 self
添加为 BarModel
对象的观察者。在某个时候,该对象被释放。但是您永远不会通过调用 removeObserver:forKeyPath:context:
来删除作为观察者的 self
。你需要这样做。
首先,在 setBarModel
中,确保删除 self
作为 _barModel
先前值的观察者。
接下来,您可能需要添加一个执行相同操作的dealloc
方法。
关于ios - 第一次尝试实现键值观察,出现一些错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25587273/