3

I have a such code:

NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(updateFrame)]];
[invocation setTarget:self];
[invocation setSelector:@selector(updateFrame)];
displayLink_ = [[CADisplayLink displayLinkWithTarget:invocation selector:@selector(invoke)] retain];
[displayLink_ setFrameInterval:1];
[displayLink_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

At iOS 6.0 (in 5.1 this code works ok) when this code calling I have two variants: EXC_BAD_ACCESS or 'call to unrecognized selector "invoke"'. It is seem that displayLinkWithTarget:selector: method doesn't retain target. When i add [invocation retain] line, code become to work ok. Is it bug of iOS 6.0?

jhabbott
  • 18,461
  • 9
  • 58
  • 95
Siarhei Fedartsou
  • 1,843
  • 6
  • 30
  • 44
  • This is a very unusual way of using CADisplayLink. Is there something wrong with `[CADisplayLink displayLinkWithTarget:self selector:@selector(updateFrame)];`? I haven't used manual memory management in over a year so I can't answer your question directly.... – borrrden Sep 21 '12 at 14:26
  • @borrrden but displayLinkWithTarget:selector: retains its target. So, i have to use NSInvocation to avoid memory leaks. – Siarhei Fedartsou Sep 21 '12 at 14:27
  • I see. Well, the documentation does indeed state that it should retain its target. It looks like the behavior has changed. It is either a bug or Apple has changed the way CADisplayLink works (and not updated their docs yet). – borrrden Sep 21 '12 at 14:32
  • what's wrong with the displaylink retaining the target ? It will release the target when you invalidate the displaylink. – Bastian Sep 21 '12 at 14:40
  • @Bastian yeah, it is ok. But in my case displaylink doesn't retain target. Such code http://pastie.org/4772592 prints COUNT 1, COUNT 1. But in separated test project this code works ok. – Siarhei Fedartsou Sep 21 '12 at 14:58

2 Answers2

4

This is useful related information, not an answer.

Rather than use NSInvokation you can use a weak proxy as I have done in my actual answer to this question. It's very simple, here's the code:

JAWeakProxy.h:

#import <Foundation/Foundation.h>

@interface JAWeakProxy : NSObject

@property (weak, nonatomic) id  target;

+ (JAWeakProxy*)weakProxyWithTarget:(id)target;

@end

JAWeakProxy.m:

#import "JAWeakProxy.h"

@implementation JAWeakProxy

+ (JAWeakProxy*)weakProxyWithTarget:(id)target
{
    JAWeakProxy* newObj = [self new];
    newObj.target = target;
    return newObj;
}

- (BOOL)respondsToSelector:(SEL)sel
{
    return [_target respondsToSelector:sel] || [super respondsToSelector:sel];
}

- (id)forwardingTargetForSelector:(SEL)sel
{
    return _target;
}

@end

NOTE: This is ARC code, you'll need to autorelease in weakProxyWithTarget: if not using ARC.

jhabbott
  • 18,461
  • 9
  • 58
  • 95
2

I had the same problem. I actually want a weak reference but since it's documented as strong and behaves that way in other versions of iOS I use a weak proxy object to forward the selector to where I really want it to go. To ensure the proxy object is retained I had to figure out a way to safely retain it on broken versions of iOS without over-retaining it for non-broken versions. I came up with a very elegant one-line solution (the line after the four lines of comments explaining it):

JAWeakProxy*    weakSelf = [JAWeakProxy weakProxyWithTarget:self];
_displayLink = [CADisplayLink displayLinkWithTarget:weakSelf selector:@selector(displayLinkUpdate:)];
// Due to a bug in iOS 6, CADisplayLink doesn't retain its target (which it should and is
// documented to do) so we need to ensure a strong reference to the weak proxy object is
// created somewhere. We do this by adding it as an associated object to the display link
// which means that it gets retained for as long as the display link object is alive.
objc_setAssociatedObject(_displayLink, @"Retain the target, bitch!", weakSelf, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

Remember to #import <objc/runtime.h>. Using an associated object is great because it gets released when the display link is dealloced and on non-broken versions of the OS it simply means the object is retained twice by the display link.

jhabbott
  • 18,461
  • 9
  • 58
  • 95