Note that there are some explanatory texts on larger screens.

plurals
  1. POUnderstanding this Xcode 4.6 Allocations Instruments Output: Retain Cycle Detection
    primarykey
    data
    text
    <p>I think I'm getting a retain cycle. It happens as I push and dismiss a VC. Every time I push and dismiss, similar objects build up. </p> <p><img src="https://i.stack.imgur.com/OkZbt.png" alt="enter image description here"></p> <p>Can someone help me understand this profiler output. (photo above)</p> <p><strong>Question 1 + 1.5:</strong> It seems like an event triggers a GCD dispatch block that never gets released. Admitably I don't fully understand everything that is going on. What is this object (1.31mb highlighted object) and the the series of events in its life-cycle? Any idea why it is not being deallocated? </p> <p><strong>Question 2:</strong> Do you see any opportunities for Retain Cycles in the code below?</p> <p>iOS: 6</p> <p>xcode: 4.6</p> <p>tested: iphone 4 device</p> <h2>DetailViewController.h</h2> <pre><code>@interface SRDetailViewController : UIViewController @property (weak, nonatomic) IBOutlet UILabel *roomTitle; @property (weak, nonatomic) IBOutlet UIView *userScreenContainer; @property (weak, nonatomic) IBOutlet UIView *opponentScreenContainer; @property (weak, nonatomic) IBOutlet UILabel *statusLabel; @property (weak, nonatomic) IBOutlet UIButton *retryButton; @property (weak, nonatomic) IBOutlet UIProgressView *progressBar; @property (strong, nonatomic) NSTimer *progressTimer; @property (strong, nonatomic) NSTimer *retryTimer; @property (weak, nonatomic) IBOutlet UIView *bottomViewContainer; @property (strong, nonatomic) SRRoom *room; @property (strong, nonatomic) SROpenTokVideoHandler *openTokHandler; </code></pre> <h2>DetailViewController.m</h2> <pre><code>@interface SRDetailViewController () @property (strong, nonatomic) NSString* kApiKey; @property (strong, nonatomic) NSString* kSessionId; @property (strong, nonatomic) NSString* kToken; @end @implementation SRDetailViewController - (void)viewDidLoad { [super viewDidLoad]; [self configOpentTok]; [self performGetRoomRequest]; [self configNavBar]; [self configNotifcations]; [self configProgressBar]; } -(void)configSocialSharing { //check if it already exists for(UIView *subview in self.view.subviews){ if([subview isKindOfClass:[SRSocialSharing class]]){ return; } } //add off screen CGRect frame = CGRectMake(0, [[UIScreen mainScreen] bounds].size.height, [[UIScreen mainScreen] bounds].size.width, 44); SRSocialSharing *share = [[SRSocialSharing alloc] initWithFrame:frame]; [self.view addSubview:share]; share.sharingURL = [self createUrlForSharing]; //animate in frame = CGRectMake(0, [[UIScreen mainScreen] bounds].size.height-100, [[UIScreen mainScreen] bounds].size.width, 44); [UIView animateWithDuration:3 delay:2 options:UIViewAnimationOptionCurveEaseOut animations:^{ share.frame = frame; } completion:nil]; } -(NSURL *)createUrlForSharing { NSRange range = NSMakeRange(self.room.sessionId.length-7, 6); NSString *shortSessionId = [self.room.sessionId substringWithRange:range]; NSString *urlString = [NSString stringWithFormat:@"url/invites/%@/%@?sessionId=%@",self.room.topicId, [self opposingPosition:self.room.position],shortSessionId]; return [NSURL URLWithString:urlString]; } -(NSString *)opposingPosition:(NSString*)position { return ([position isEqualToString:@"agree"])? @"disagree" : @"agree"; } -(void) configOpentTok{ [self.openTokHandler registerUserVideoStreamContainer:self.userScreenContainer]; self.openTokHandler.userVideoStreamConatinerName = self.room.position; [self.openTokHandler registerOpponentOneVideoStreamContainer:self.opponentScreenContainer]; self.openTokHandler.opponentOneVideoStreamConatinerName = [self opposingPosition:self.room.position]; self.openTokHandler.shouldPublish = YES; self.openTokHandler.isObserving = NO; } -(void)configNavBar { UIImage *backButtonImage = [UIImage imageWithContentsOfFile:@"backButton"]; UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; [backButton setFrame:CGRectMake(0, 0, 47, 32)]; [backButton setImage:backButtonImage forState:UIControlStateNormal]; [backButton addTarget:self action:@selector(pressBackButton) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *navBackButton = [[UIBarButtonItem alloc] initWithCustomView:backButton]; [self.navigationItem setLeftBarButtonItem:navBackButton]; self.title = [self.room.position stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[self.room.position substringToIndex:1] capitalizedString]]; } -(void)pressBackButton{ self.navigationItem.leftBarButtonItem.enabled = NO; [self manageSafeClose]; double delayInSeconds = 3; //[self updateStatusLabel:@"Disconnecting" withColor:[UIColor grayColor]]; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self.navigationController popViewControllerAnimated:YES]; }); } -(void)configNotifcations { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(recieveNotifications:) name:kSROpenTokVideoHandlerNotifcations object:nil ]; } -(void)recieveNotifications:(NSNotification *)notification { if ([[notification name] isEqualToString:kSROpenTokVideoHandlerNotifcations]){ NSDictionary *userInfo = notification.userInfo; NSNumber *message = [userInfo objectForKey:@"message"]; [self statusMessage: message]; } } -(void)statusMessage:(NSNumber*)message{ NSString *result = nil; switch([message intValue]) { case 0: result = @"Disconnected"; break; case 1: result = @"Connecting..."; [self startRetryTimer]; break; case 2: result = @"Publishing Your Video..."; break; case 3: result = @"Searching for Idiots..."; break; case 4: result = @"Start!"; [self startProgressBar]; [self stopTimer:self.retryTimer]; break; case 5: [self stopTimer:self.progressTimer]; result = @"Stopped!"; break; case 6: [self stopTimer:self.progressTimer]; result = @"Disconnecting..."; break; case 7: result = @"Opponent failed to join. Retrying..."; [self performSelector:@selector(retry) withObject:nil afterDelay:4]; break; default: result = @"Retry"; } [self updateStatusLabel:result withColor:[self statusLabelColorPicker:message] animated:YES]; NSLog(@"STATUS LABEL UPDATE: %@", message); } -(UIColor*)statusLabelColorPicker:(NSString *)Message{ return [UIColor whiteColor]; } -(void)performGetRoomRequest{ __weak typeof(self) weakSelf = self; [[RKObjectManager sharedManager] getObject:weakSelf.room path:nil parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){ weakSelf.openTokHandler.kToken = weakSelf.room.token; weakSelf.openTokHandler.kSessionId = weakSelf.room.sessionId; weakSelf.roomTitle.text = weakSelf.room.title; weakSelf.navigationController.title = weakSelf.room.position; [weakSelf configSocialSharing]; [weakSelf.openTokHandler doConnectToRoomWithSession]; }failure:^(RKObjectRequestOperation *operation, NSError *error){ }]; } -(void)dealloc { self.room = nil; } -(void)manageSafeClose{ [self stopTimer:self.retryTimer]; [self stopTimer:self.progressTimer]; [self.openTokHandler safetlyCloseSession]; [[RKObjectManager sharedManager].operationQueue cancelAllOperations]; self.openTokHandler = nil; self.title = nil; self.navigationItem.leftBarButtonItem = nil; self.kApiKey = nil; self.kSessionId= nil; self.kToken= nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; [self doCloseRoom]; } -(void)doCloseRoom { __weak typeof(self) weakSelf = self; [[RKObjectManager sharedManager] deleteObject:weakSelf.room path:nil parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){ //NSLog(@"Mapping result %@", mappingResult); } failure:nil ]; } -(void)startRetryTimer { NSLog(@"Timer Started"); self.retryTimer = [NSTimer scheduledTimerWithTimeInterval:(60*5) target:self selector:@selector(retry) userInfo:nil repeats:YES]; } -(void)retry { [self doCloseRoom]; [self performSelector:@selector(performGetRoomRequest) withObject:nil afterDelay:4]; } #pragma mark - label - (void)updateStatusLabel:(NSString *) message withColor:(UIColor*) color animated:(bool) animated { self.statusLabel.text = message; if (animated) { [self fadeOutFadeInAnimation:self.statusLabel andColor:color]; } else{ [SRAnimationHelper stopAnimations:self.statusLabel]; } } - (void)fadeOutFadeInAnimation:(UILabel *)label andColor:(UIColor*)color { //add animation [label.layer addAnimation:[SRAnimationHelper fadeOfRoomStatusLabel] forKey:nil]; //change label color label.textColor = color; } #pragma mark - Progress Bar -(void)configProgressBar { self.progressBar.progressTintColor = [UIColor orangeColor]; } -(void)startProgressBar { self.progressBar.hidden = NO; self.progressBar.progress = 0; self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:@selector(changeProgressValue) userInfo:nil repeats:YES]; } -(void)stopTimer: (NSTimer*)timer { [timer invalidate]; timer = nil; } - (void)changeProgressValue { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ float progressValue = self.progressBar.progress; progressValue += .00834; if (progressValue &gt; .99) { progressValue = 1; [self stopTimer:self.progressTimer]; return; } NSString* time =[NSString stringWithFormat:@"%.0f", 60 - ceil(progressValue*60)]; NSLog(@"Progress Value %f Time %@", progressValue, time); NSString *message = [NSString stringWithFormat:@"Time Left: %@", time]; dispatch_async(dispatch_get_main_queue(), ^(void) { self.progressBar.progress = progressValue; [self updateStatusLabel:message withColor:[UIColor whiteColor] animated:NO]; }); }); } @end </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload