Tirth
Tirth

Reputation: 7789

KVO mechanism throwing error?

I m implementing one photo edit app in which i have one camera captured image and one is output image which is i shown beside of captured image. Whenever i change my captured image scaling or rotation then i want notify this changes to outputImage. For this senario i written following code,

-(void)imageCapturedByCameraAPI:(UIImage *)theCapturedImage{
[[arOverlayVC view] removeFromSuperview];
capturedImageView.image = theCapturedImage;
[self.view addSubview:imageEditorView];

rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotatePiece:)];
[capturedImageView addGestureRecognizer:rotationGesture];
**//KVO added for rotation**
[capturedImageView addObserver:self forKeyPath:@"transform.rotate" options:NSKeyValueObservingOptionNew context:NULL];

pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(scalePiece:)];
[pinchGesture setDelegate:self];
[capturedImageView addGestureRecognizer:pinchGesture];
**//KVO added for scaling**
[capturedImageView addObserver:self forKeyPath:@"transform.scale" options:NSKeyValueObservingOptionNew context:NULL];

}

   - (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer {
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];

if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
    [gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
    [self editedImage];
    [gestureRecognizer setRotation:0];
}
 }

- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
    lastScale = [gestureRecognizer scale];
}

if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
    [gestureRecognizer state] == UIGestureRecognizerStateChanged) {

    CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue];

    const CGFloat kMaxScale = 2.0;
    const CGFloat kMinScale = 1.0;

    CGFloat newScale = 1 -  (lastScale - [gestureRecognizer scale]);
    newScale = MIN(newScale, kMaxScale / currentScale);
    newScale = MAX(newScale, kMinScale / currentScale);
    CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
    [gestureRecognizer view].transform = transform;
    lastScale = [gestureRecognizer scale];  
}
 }

  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
// if the gesture recognizers are on different views, don't allow simultaneous recognition

if (gestureRecognizer.view != otherGestureRecognizer.view)
    return NO;

// if either of the gesture recognizers is the long press, don't allow simultaneous recognition
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
    return NO;

return YES;
   }

 - (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer    {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
    UIView *piece = gestureRecognizer.view;
    CGPoint locationInView = [gestureRecognizer locationInView:piece];
    CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];

    piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
    piece.center = locationInSuperview;
}
  }

Now my observation method is,

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
   {
if(object == capturedImageView && ([keyPath isEqualToString:@"transform.rotate"] || [keyPath isEqualToString:@"transform.scale"])) {
    NSLog(@"KVO detected");
     }
  }

I found following error,

An instance 0x189f40 of class NSConcreteValue 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: ( Context: 0x0, Property: 0x189ff0>

An instance 0x188cb0 of class NSConcreteValue 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: ( Context: 0x0, Property: 0x188d80>

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot update for observer for the key path "transform.scale" from , most likely because the value for the key "transform" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the UIImageView class.'

Upvotes: 0

Views: 2081

Answers (2)

Daij-Djan
Daij-Djan

Reputation: 50089

arc only handles memory for NSObject&Co, for everything else (e.g. KVO or network connections or file writes) you need to manually clean up

overwrite -(void)dealloc for that

- (void)dealloc {
    [capturedImageView removeObserver:self forKeyPath:@"transform.scale"];   
} 

Upvotes: 1

Bryan Chen
Bryan Chen

Reputation: 46578

as the error message said

An instance of class was deallocated while key value observers were still registered with it

you need to remove the observation before capturedImageView is deallocated using removeObserver:forKeyPath:context:

It is very important to make sure you have balance the count of register/deregister in order to not break KVO.

I you want to know more about KVO, this link is helpful: http://www.mikeash.com/pyblog/key-value-observing-done-right.html

and part2: http://www.mikeash.com/pyblog/friday-qa-2012-03-02-key-value-observing-done-right-take-2.html

Upvotes: 1

Related Questions