Ios 在DetailViewController中保留循环-启用ARC

Ios 在DetailViewController中保留循环-启用ARC,ios,iphone,objective-c,memory-management,Ios,Iphone,Objective C,Memory Management,我在展示/解聘我的DetailViewController时有一个保留周期。如果您能在下面的代码中找到保留周期的机会,我们将不胜感激 问题:下面发布的代码中是否有保留周期 iOS:6 xcode:4.6 测试:iPhone4设备 ARC启用 编辑:添加仪器照片 DetailViewController.h DetailViewController.m 除了上面提到的计时器保留之外,在发布的代码中没有保留周期。我没有跟踪计时器的逻辑,但是很容易调试 作为补充说明,这是我见过的避免常见错误的一些最

我在展示/解聘我的DetailViewController时有一个保留周期。如果您能在下面的代码中找到保留周期的机会,我们将不胜感激

问题:下面发布的代码中是否有保留周期

iOS:6

xcode:4.6

测试:iPhone4设备

ARC启用

编辑:添加仪器照片

DetailViewController.h DetailViewController.m
除了上面提到的计时器保留之外,在发布的代码中没有保留周期。我没有跟踪计时器的逻辑,但是很容易调试


作为补充说明,这是我见过的避免常见错误的一些最好的代码。你的工作出色,应该受到表扬。

看来你有一个计时器:

@property (strong, nonatomic) NSTimer *retryTimer;

self.retryTimer  = [NSTimer scheduledTimerWithTimeInterval:(60*5)
                                                    target:self
                                                  selector:@selector(retry)
                                                  userInfo:nil
                                                   repeats:YES];
您需要在某个点设置self.retryTimer=nil,或者使计时器无效。这写得很奇怪(
timer
只是一个局部变量):

再想一想,
invalidate
应该会打破保留周期。虽然你确定你在调用这个方法


第三种想法是,您可能意外地创建了几个计时器,每个计时器都会给您一个新的计时器。如果发生这种情况,您将使最后一个无效,其余的将保持停止状态。那可能是你的问题

将工具与分配一起使用。然后,您可以查看保留/释放视图控制器的内容的历史记录。
NSTimer
s使用
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats创建,并对其目标进行强引用,直到计时器失效。我怀疑您的一个计时器没有失效。我也认为计时器是可能的问题,但只要始终调用
manageSafeClose
方法,它们似乎都会失效。但是
timer=nil
没有任何作用。好主意,但是计时器在
manageSafeClose
中无效,在解除VC之前会调用它。我上传了一张仪器分配的照片。自下而上:“我对profiler不是全新的,但我不确定这里发生了什么。看起来分配了1.31mb的对象,并且一个事件触发了一个从未发布的GCD调度块?”感谢您的帮助和赞扬。我上传了一张分析器输出的照片。我对profiler并不陌生,但我不确定这里发生了什么。看起来好像分配了一个1.31mb的对象,一个事件触发了一个永远不会释放的GCD调度块?
@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 > .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
@property (strong, nonatomic) NSTimer *retryTimer;

self.retryTimer  = [NSTimer scheduledTimerWithTimeInterval:(60*5)
                                                    target:self
                                                  selector:@selector(retry)
                                                  userInfo:nil
                                                   repeats:YES];
-(void)stopTimer: (NSTimer*)timer
{
    [timer invalidate];
    timer = nil;
}