目标 C - 因保留周期而丢失

Posted

技术标签:

【中文标题】目标 C - 因保留周期而丢失【英文标题】:Objective C - Lost with retain cycles 【发布时间】:2015-02-02 03:38:32 【问题描述】:

在解雇我的 VC 时,我注意到我没有从内存中释放所有内容。我对如何找到我的保留周期感到非常迷茫。我正在使用 NSTimer 和 NSNotificationCenter,但我确保在退出之前使和 removeObservers 无效,并确保使用弱委托。

我的保留周期还会发生在哪里?在动画块中?像这样?

 [UIView animateWithDuration:.1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^
                self.setListTableViewVertConst.constant = 0;
                self.setListTableViewHeightConst.constant = 264;
             completion:^(BOOL finished) 

            ];

在使用 GCD 时,我确保使用 weakSelf。

__weak typeof(self) weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^
                [weakSelf.remotePasswordTextField resignFirstResponder];
            );

感谢您的帮助。

编辑:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    //Send the room code to be displayed on the respective view controllers. 
    if ([segue.identifier isEqualToString:@"toSetListRoomVC"]) 
        SetListRoomViewController *setListVC = segue.destinationViewController;
        setListVC.roomCode = self.roomCodeTextField.text;
    


viewWIllApear

[super viewWillAppear:YES];


    self.socket = [[SocketKeeperSingleton sharedInstance]socket];
    self.socketID = [[SocketKeeperSingleton sharedInstance]socketID];

    NSString *roomCodeAsHost = [[SocketKeeperSingleton sharedInstance]hostRoomCode];
    /////////HOST/////////
    if ([[SocketKeeperSingleton sharedInstance]isHost]) 

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveHostSongAddedNotification:)
                                                     name:kQueueAdd
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveUserJoinedNotification:)
                                                     name:kUserJoined
                                                   object:nil];

        NSLog(@"User is the host of this room");
        self.isHost = YES;
        [self viewForNoCurrentArtistAsHost];
        self.roomCodeLabel.text = roomCodeAsHost;

        if (!self.hostQueue) 
            self.hostQueue = [[NSMutableArray alloc]init];
        
        if (!self.hostCurrentArtist) 
            self.hostCurrentArtist = [[NSMutableDictionary alloc]init];
        

        if (!self.player) 
            self.player = [[AVPlayer alloc]init];
        
        if (!self.timer) 
            self.timer = [[NSTimer alloc]init];
        
    

    ///////NOT HOST///////
    else 
        // Add a notifcation observer and postNotification name for updating the tracks.
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveQueueUpdatedNotification:)
                                                     name:kQueueUpdated
                                                   object:nil];

        //Add a notifcation observer and postNotification name for updating current artist.
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveCurrentArtistUpdateNotification:)
                                                     name:kCurrentArtistUpdate
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveOnDisconnectNotification:)
                                                     name:kOnDisconnect
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(receiveHostDisconnectNotification:)
                                                     name:kHostDisconnect
                                                   object:nil];


        //Add some animations upon load up. Purple glow and tableview animation.
        double delay = .4;
        [self purpleGlowAnimationFromBottomWithDelay:&delay];
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationBottom];

        //Set Current artist, if there is one.
        NSDictionary *currentArtist = [[SocketKeeperSingleton sharedInstance]currentArtist];
        [self setCurrentArtistFromCurrentArtist:currentArtist];


        //Set the current tracks, if there is one.
        NSArray *setListTracks = [[SocketKeeperSingleton sharedInstance]setListTracks];
        if (setListTracks) 
            self.tracks = setListTracks;
        
    

tableVIEWs

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    if (tableView.tag == 1) 
        if (self.isHost) 
            return [self.hostQueue count];
        
        else return [self.tracks count];
    
    else return [self.searchTracks count];


-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return 1;

【问题讨论】:

之前的视图控制器(或其他东西)是否保留了对这个视图控制器的(强)引用?如果您在 self 中保留对使用块 self 的引用(也就是它们相互引用,因此保留循环),那么您只需要真正使用weakSelf,但我猜对于异步任务,线程可能不希望对 self 有强引用,如果即使线程没有执行代码,self 也需要解除分配 您在评论中说您正在使用 segues 在控制器之间移动。您是否正在使用 segue 回到以前的控制器?如果是这样,那不是流行音乐,除非您使用的是 unwind segue。使用除展开之外的任何其他操作返回上一个控制器会创建一个新实例,它不会返回到您认为的那个。 【参考方案1】:

我注意到您在某些 cmets 中使用了“popOut”一词,因此我将假设您使用的是导航控制器。

如果是这种情况,您的视图控制器将被嵌入它的导航控制器保留,并且不会被释放。导航控制器需要保存对您的 VC 的引用(请参阅 UINavigationController.viewControllers),以便在您弹出层次结构中的顶部/下一个 VC 时它可以返回到它。

这是预期行为。

【讨论】:

好的,但是每次推送和弹出似乎都会增加内存。所以仍然有一些泄漏。我早上得看看这个。 哦,是的,当然,内存中的峰值表明某些东西确实可能正在泄漏。老实说,如果在“推送”到下一个 VC 时出现峰值,我会首先检查新 VC 内部的分配。实际上,峰值可能来自分配新的 VC 本身。考虑过这种可能性吗?【参考方案2】:

在此代码示例中,您没有将__weak 用于self

 [UIView animateWithDuration:.1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^
                self.setListTableViewVertConst.constant = 0;
                self.setListTableViewHeightConst.constant = 264;
             completion:^(BOOL finished) 

            ];

但是,乍一看,这似乎并没有创建保留周期。您发布的代码似乎没有,所以它可能在其他地方。你确定你有保留周期吗?

无论如何,试试instrumenting 你的应用程序。这可能会有所帮助。

【讨论】:

当我从VC中弹出并返回时,所有数据都保留了下来。这是因为保留循环吧? 不,不是。实际上,如果有一个保留周期来保存您的数据,那么您将拥有一个 新的、干净的 VC,而旧的将在内存中徘徊,在应用程序的领域中永远消失。你是如何实例化你的 VC 的?你在缓存它吗?如果是这样,它将解释数据持久性。 不,我不是,从 VC 中跳出来是为了让它变得全新和干净吗?当我弹出时,我希望它是新的和干净的。 不,弹出不应该那样做,但是在推送时实例化它应该。再说一次,你是如何实例化你的 VC 的?另外,如何初始化它? 我正在使用故事板和转场。

以上是关于目标 C - 因保留周期而丢失的主要内容,如果未能解决你的问题,请参考以下文章

Mysqldump 备份恢复与xtrabackup备份

目标 C - 将图像从服务器下载到 iPhone 并保留日期

页面之间的 PHP 会话丢失 - 行为因服务器而异

源文件名和目标文件名相同怎么办

目标 C:保留/释放内联分配的对象

目标 C NSString 保留