2015-05-02 2 views
1

Цель-c noob здесь. Я испытываю ошибку EXC_BAD_ACCESS в своих тестах и ​​не могу понять, почему. Я написал класс Cart для моего приложения, представляющего корзину пользователя. A Cart может иметь несколько Order s, хранящихся в массиве. Я пытаюсь реализовать соответствие KVO для массива Order s с заданным кодом (извинения за длинный фрагмент кода, хочу убедиться, что все проблемы устранены). .h файл:`EXC_BAD_ACCESS` в классе, реализующем KVO

#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 

И .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 

метод addOrder: продюсирует EXC_BAD_ACCESS исключение на этой линии:

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

Debugging, то _orders массив является действительным NSMutableArray , Кто-нибудь знает, что происходит? Очень признателен.

`

+0

Запустите свой код с помощью диагностического знака NSZombieEnable, который поможет вам найти причину такого плохого доступа. https: // StackOverflow.com/questions/5386160/how-to-enable-nszombie-in-xcode –

ответ

1

Я уверен, что я предложил это на одном из других вопросов: проблема, предположительно, с чем-то ключ-значение наблюдения orders свойство экземпляра этого класса Cart. Вероятно, наблюдатель не прекратил наблюдать, пока он не был освобожден. Таким образом, при изменении вашего свойства orders, KVO пытается отправить уведомление об изменении на объект с более длинным именем, который сбой.

Предложение Nimrod для запуска вашего приложения под инструментом Zombies является хорошим.

Кстати, для этого Cart класса:

  • Вы должны всегда указать уникальное context значение при вызове -addObserver:forKeyPath:options:context: и проверить его в вашем методе -observeValueForKeyPath:ofObject:change:context:. Для контекстов, отличных от того, который вы используете, перейдите к супер и возвратитесь. Структуры разрешены, чтобы ваши объекты также наблюдали за вещами, и если вы этого не сделаете, вы будете мешать их функционированию.

  • Вы должны переместить -addObserver:... и -removeObserver:... звонки от -addOrder: и -removeOrder: к методам мутации собственности, -insertObject:inOrdersAtIndex:, -removeObjectFromOrdersAtIndex: и -setOrders:. В основном, вы должны переместить код в том же месте, где вы добавляете и удаляете элементы в фактическом массиве, поэтому вы уверены, что всегда наблюдаете все объекты в массиве и перестаете наблюдать объекты, удаленные из массива. В частности, вы проигнорировали случай -setOrders:, хотя я подозреваю, что вы никогда не используете этот метод.

    В любом случае, манипуляции с массивом в отдельном месте от манипуляций с наблюдениями оставляют вас открытыми для совершения одного без другого. Например, вы можете добавить будущие вызовы в -insertObject:inOrdersAtIndex:, которые не пройдут через -addOrder:, и вы не сможете начать наблюдение за этим заказом.

+0

Спасибо за советы, вы были очень полезны в процессе обучения! – Bibs

Смежные вопросы