10

This is a new compiler warning that only showed up when I updated XCode to 4.6. My code is lifted directly from Apple's documentation (this is my iOS 6 code btw).

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [self setLastError:error];
    if(localPlayer.authenticated){

Warning--Capturing 'localPlayer' strongly in this block is likely to lead to a retain cycle

mevdev
  • 701
  • 8
  • 16

2 Answers2

25

The issue is that the localPlayer object is keeping a strong reference to itself - when localPlayer is "captured" for use within the authenticateHandler block, it is retained (when objective-c objects are referred to within a block, the compiler under ARC calls retain for you). Now, even when all other references to the localPlayer cease to exist, it will still have a retain count of 1 and hence the memory will never be freed. This is why the compiler is giving you a warning.

Refer to it with a weak reference, like:

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

__weak GKLocalPlayer *blockLocalPlayer = localPlayer;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [self setLastError:error];
    if (blockLocalPlayer.authenticated) {
        ...

Given that the lifetime of the authenticateHandler and the localPlayer are tightly linked (i.e. when the localPlayer is deallocated, so is the authenticateHandler) there's no need for it to maintain a strong reference within the authenticateHandler. Using Xcode 4.6, this no longer generates the warning you mentioned.

Chris McGrath
  • 1,936
  • 16
  • 17
  • 1
    Thanks! I understand the block retain cycle a bit more now. This was perfect. – mevdev Jan 30 '13 at 23:27
  • 2
    You can also not create the localPlayer variable and always use `[GKLocalPlayer localPlayer]`, so you don't keep any reference. – tothemario May 27 '14 at 18:48
1

The compiler is just helping you out with code that was already a problem, it just didn't know about it before.

You can read about retain cycles here: http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

Basically you just need to change your code to something like:

GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

__weak MyViewController *blockSelf = self;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
    [blockSelf setLastError:error];
    if(localPlayer.authenticated){
Eric Reid
  • 457
  • 3
  • 10
  • That doesn't seem to work for me. Can I just weakly create localPlayer? I am doing this authentication in my AppDelegate so there isn't a corresponding viewController. – mevdev Jan 30 '13 at 22:57
  • Yeah, I read your question wrong and thought it was complaining about capturing self. Use what Chris McGrath posted. – Eric Reid Jan 30 '13 at 23:21