ios - 第一次尝试实现键值观察,出现一些错误

标签 ios objective-c nsmutablearray key-value-observing

在尝试遵循 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/

相关文章:

ios - 如何检查对象是否具有存储属性?

ios - 如何检查 NSMutableArray 中的状态

ios - 应用程序在检查计数 NSMutableArray 时崩溃

iOS 应用程序将不再在 iOS 12 : Unrecoverable CT signature issue 下的设备上运行

ios - 如何结束开关盒

objective-c - 解析套件构建项目问题

objective-c - 在 UIPanGestureRecognizer 和重新启用的 UIScrollView 之间连续滚动

iOS7 - TableView 标题中的多行文本?

ios - 无法添加到 NSMutableArray

ios - 圆形 BezierPath 相对于给定的进度值填充太多 - iOS Swift