简体   繁体   English

实现KVO的类中的EXC_BAD_ACCESS

[英]`EXC_BAD_ACCESS` in Class implementing KVO

Objective-c noob here. Objective-C菜鸟在这里。 I'm experiencing a EXC_BAD_ACCESS error in my tests and can't figure out why. 我在测试中遇到了EXC_BAD_ACCESS错误,无法弄清原因。 I've written a Cart class for my application representing a user's cart. 我为我的应用程序编写了一个Cart类,代表用户的购物车。 A Cart can have multiple Order s, stored in an array. 一个Cart可以有多个Order ,它们存储在一个数组中。 I'm attempting to implement KVO compliance for the Order s array with the given code (Apologies for the long code snippet, want to make sure everything is present to resolve the problem). 我正在尝试使用给定代码对Order s数组实现KVO合规性(很长的代码段表示歉意,要确保提供一切解决问题的方法)。 The .h file: .h文件:

#import "Model.h"

@class Item;
@class Organization;
@class Order;

@interface Cart : NSObject

@property (copy, readonly, nonatomic) NSArray *orders;

- (instancetype) init __attribute__((unavailable("init not available. Use `sharedCart`.")));
+ (instancetype)sharedCart;

- (void)setItem:(Item *)item withQuantity:(NSNumber *)quantity;
- (void)removeItem:(Item *)item;
- (NSNumber *)itemCount;
- (Order *)orderForSeller:(Organization *)seller;

@end

And the .m : .m

@interface Cart()
- (void)addOrder:(Order *)order;
- (void)removeOrder:(Order *)order;
@property (copy, readwrite, nonatomic) NSArray *orders;
@end

NSString * const ordersKey = @"orders";

@implementation Cart
{
    NSMutableArray *_orders;
}

@synthesize orders = _orders;


+ (instancetype)sharedCart {
    static dispatch_once_t onceToken;
    static Cart *cart;

    dispatch_once(&onceToken, ^{
        cart = [[Cart alloc] initPrivate];
    }); 

    return cart;
}

- (void)insertObject:(Order *)order inOrdersAtIndex:(NSUInteger)index {
    [_orders insertObject:order atIndex:index];
}

- (void)removeObjectFromOrdersAtIndex:(NSUInteger)index {
    [_orders removeObjectAtIndex:index];
}

- (void)setOrders:(NSArray *)array {
    if (array != _orders) {
        _orders = [array mutableCopy];
    }   
}

- (instancetype)initPrivate {
    self = [super init];
    if (self) {
        self = [super init];
        _orders = [[NSMutableArray alloc] init];
    }   
    return self;
}
+ (instancetype)object {
    @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Can not create new Cart instance. Used [Cart sharedCart]" userInfo:nil];
}

-(Order *)orderForSeller:(Organization *)seller {
    for (Order *order in self.orders) {
        if ([order.seller is:seller]) {
            return order;
        }
    }
    return nil;
}

- (void)setItem:(Item *)item withQuantity:(NSNumber *)quantity {

    Order *order = [self orderForSeller:item.seller];

    if (!order) {
        order = [Order object];
        order.seller = item.seller;
        order.user = [User currentUser];
        [self addOrder:order];
    }

    [order setItem:item withQuantity:quantity];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {


    // If an order is ever empty, remove it from cart.
    if ([object isKindOfClass:[Order class]] && [keyPath isEqualToString:@"orderItems"]) {
        Order *order = (Order *)object;
        if ([[order itemCount] integerValue] == 0) {
            [self removeOrder:order];
        }
    }

    // When orders are saved, remove them from cart.
    if ([object isKindOfClass:[Order class]] && [keyPath isEqualToString:@"isNew"]) {
        Order *order = (Order *)object;
        BOOL orderHasBeenPlaced = ![order isNew];
        if (orderHasBeenPlaced) {
            [self removeOrder:order];
        }
    }
}
- (void)addOrder:(Order *)order {
    [order addObserver:self forKeyPath:@"orderItems" options:NSKeyValueObservingOptionNew context:nil];
    [order addObserver:self forKeyPath:@"isNew" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    [self insertObject:order inOrdersAtIndex:[_orders count]];
}

-(void)removeOrder:(Order *)order {
    [order removeObserver:self forKeyPath:@"orderItems"];
    [order removeObserver:self forKeyPath:@"isNew"];
    NSUInteger index = [_orders indexOfObject:order];
    [self removeObjectFromOrdersAtIndex:index];
}

-(void)removeItem:(Item *)item {
    Order *order = [self orderForSeller:item.seller];
    if (order) {
        [order removeItem:item];
    }
}

-(NSNumber *)itemCount {
    return [self.orders valueForKeyPath:@"@sum.itemCount"];
}

- (void)dealloc {
    for (Order *order in self.orders) {
        [order removeObserver:self forKeyPath:@"isNew"];
        [order removeObserver:self forKeyPath:@"orderItems"];
    }
}

@end

The addOrder: method is producing the EXC_BAD_ACCESS exception on this line: addOrder:方法在此行上产生EXC_BAD_ACCESS异常:

[self insertObject:order inOrdersAtIndex:[_orders count]];

Debugging, the _orders array is a valid NSMutableArray . 调试时, _orders数组是有效的NSMutableArray Anyone have any idea what's going on? 任何人都知道发生了什么事吗? Much appreciated. 非常感激。

` `

I'm pretty sure I suggested this on one of your other questions: the problem is presumably with something that is key-value observing the orders property of an instance of this Cart class. 我很确定我在您的其他问题中提出了一个建议:问题可能出在关键值观察此Cart类实例的orders属性上。 Probably an observer did not stop observing before it was deallocated. 释放之前,观察者可能没有停止观察。 So, when you modify your orders property, KVO tries to send a change notification to a no-longer-existing object, which crashes. 因此,当您修改orders属性时,KVO尝试将更改通知发送到不再存在的对象,该对象会崩溃。

Nimrod's suggestion to run your app under the Zombies instrument is a good one. Nimrod建议在Zombies工具下运行您的应用程序是一个很好的建议。

By the way, for this Cart class: 顺便说一下,对于这个Cart类:

  • You should always specify a unique context value when calling -addObserver:forKeyPath:options:context: and check for it in your -observeValueForKeyPath:ofObject:change:context: method. 调用-addObserver:forKeyPath:options:context:时,应始终指定唯一的context值,并在-observeValueForKeyPath:ofObject:change:context:方法中进行检查。 For contexts other than the one you're using, call through to super and return. 对于除您正在使用的上下文以外的上下文,请调用super并返回。 The frameworks are permitted to make your objects observe stuff, too, and if you fail to do this you will interfere with their functioning. 允许框架使您的对象也观察事物,如果不这样做,则会干扰其功能。

  • You should move the -addObserver:... and -removeObserver:... calls from -addOrder: and -removeOrder: to the property mutation methods, -insertObject:inOrdersAtIndex: , -removeObjectFromOrdersAtIndex: , and -setOrders: . 您应该将-addObserver:...-removeObserver:...调用从-addOrder:-removeOrder:移至属性突变方法-insertObject:inOrdersAtIndex: ,- -removeObjectFromOrdersAtIndex:-setOrders: Basically, you should move the code to the same place where you add and remove items in the actual array, so you're sure that you're always observing all objects in the array and you stop observing objects removed from the array. 基本上,您应该将代码移动到在实际数组中添加和删除项目的相同位置,因此,您可以确保始终观察数组中的所有对象,并停止观察从数组中删除的对象。 In particular, you neglected the -setOrders: case, although I suspect you never use that method. 尤其是,您忽略了-setOrders:情况,尽管我怀疑您从未使用过该方法。

    In any case, having the array manipulation in a separate place from the observation manipulation leaves you open to doing one without the other. 在任何情况下,将数组操作与观察操作放置在不同的位置都可以让您自由地进行操作。 For example, you might add future calls to -insertObject:inOrdersAtIndex: that don't go through -addOrder: and you'd fail to start observing that order. 例如,您可能会将以后的调用添加到-insertObject:inOrdersAtIndex: ,而不会通过-addOrder:进行-addOrder: ,您将无法开始观察该顺序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM