Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 内存泄漏(ARC)_Ios_Objective C_Memory Management_Memory Leaks - Fatal编程技术网

Ios 内存泄漏(ARC)

Ios 内存泄漏(ARC),ios,objective-c,memory-management,memory-leaks,Ios,Objective C,Memory Management,Memory Leaks,在我的后续调查中,我得出结论,我确实存在内存泄漏。总之,内存从9.7MB开始,每运行10次动画,内存就会增加0.1MB,或者看起来是这样。我测试了大约12MB 使用仪器,我运行了一个测试,包括: 登记第一代 运行动画10次 登记下一代 重复几次 以下是我得到的: 所以记忆确实在增长。然而,检查这几代人,似乎我对这些泄漏不负责任。例如,检查统计面板时,列出的类别似乎表示CF、CG、NS等,以及Malloc和\uuuu NSMallocBlock\uuu 我还检查了调用树,并跟踪内存消耗最高的分支

在我的后续调查中,我得出结论,我确实存在内存泄漏。总之,内存从9.7MB开始,每运行10次动画,内存就会增加0.1MB,或者看起来是这样。我测试了大约12MB

使用仪器,我运行了一个测试,包括:

  • 登记第一代
  • 运行动画10次
  • 登记下一代
  • 重复几次
  • 以下是我得到的:

    所以记忆确实在增长。然而,检查这几代人,似乎我对这些泄漏不负责任。例如,检查统计面板时,列出的类别似乎表示CF、CG、NS等,以及
    Malloc
    \uuuu NSMallocBlock\uuu

    我还检查了调用树,并跟踪内存消耗最高的分支

    同样,大多数内存消耗似乎与CoreGraphics有关。在分配列表中,我可以更清楚地看到这些malloc是什么。结论是一样的

    提供完整的源代码并不实际,因为应用程序已经达到几千行。因此,我将概述什么似乎很重要:

    - (void)animateViewDidAppear
    {
        NSArray * buttons = @[self.registrationButton, self.facebookButton, self.twitterButton, self.linkedInButton];
    
        // [...] A bunch of GLfloat calculations here
    
        __block HyAnimationCollection * collection = [[HyAnimationCollection alloc] init];
    
        for (int it=0 ; it < [buttons count] ; ++it) {
    
            UIButton * button = [buttons objectAtIndex:it];
    
            // [...] More GLfloat stuff
    
            // Ease out back
            __block HyAnimation * easeOutBack = [[HyAnimation alloc] init];
            HyAnimationUpdateFunction easeOutBackUpdate = ^(id frame, BOOL done) {
                [button setFrame:[frame CGRectValue]];
            };
    
            [easeOutBack setDelay:it * kHyAuthenticationViewControllerAnimationDelayFactor];
            [easeOutBack setDuration:kHyAuthenticationViewControllerAnimationDuration];
            [easeOutBack setEasing:^(GLfloat t) { return [HyEasing easeOutBack:t]; }];
            [easeOutBack addRectAnimation:CGRectMake(origin.x, from, size.width, size.height)
                                       to:CGRectMake(origin.x, to, size.width, size.height)];
            [easeOutBack addUpdateFunction:easeOutBackUpdate];
            [collection addAnimation:easeOutBack];
        }
    
        [collection addLock:self.animationLock];
        [collection start];
    }
    
    由于
    self.updateFunctions
    是一个
    NSMutableArray
    ,因此它保留了对这些块的强引用。因此,如果未释放
    HyAnimation
    ,这些块也不会释放,这意味着创建它们的初始范围也不会释放。但是,HyAnimation是在一个方法中声明的,到目前为止,我看不出有什么理由不发布它

    这就是为什么我认为这应该是因为动画本身,即
    HyAnimationCollection
    [collection start]。有趣的是:

    for (HyAnimation * anim in self.animations) {
        [anim start];
    }
    
    到目前为止还不错。下面是
    HyAnimation
    start

    - (void)start
    {
        [NSTimer scheduledTimerWithTimeInterval:self.delay
                                         target:self
                                       selector:@selector(scheduleAnimationWithTimer:)
                                       userInfo:nil
                                        repeats:NO];
    
        // Send an udate notification
        if ([self shouldUpdateImmediatly]) {
            [self animateAt:0.0f done:NO];
        }
    }
    
    这几乎会延迟运行并委托给
    scheduleAnimationWithTimer:
    。但是,此方法设置了一个重复的计时器,因此将一直持续到动画结束(我希望不会再继续)

    现在
    animateWithTimer:

    - (void)animateWithTimer:(NSTimer*)timer
    {
        NSTimeInterval gone = [[NSDate date] timeIntervalSinceDate:self.initialDate];
        GLfloat t = gone / self.duration;
        BOOL done = gone >= self.duration;
    
        // Ease
        if (self.easing) {
            t = self.easing(t);
        }
    
        // Make sure the last position is exact. This does not mean that t does not go over 1.0f during the animation, just the end
        if (done && t > 1.0f) {
            t = 1.0f;
        }
    
        // Animate
        [self animateAt:t done:done];
    
        // Finish
        if (done) {
    
            // Stop the timer
            [timer invalidate];
    
            // Notify completion
            [self broadcastCompletion];
        }
    }
    
    最后,
    animateAt:done:

    - (void)animateAt:(GLfloat)t done:(BOOL)done
    {
        for (HyAnimationFunction anim in self.animations) {
            anim(t, done);
        }
    }
    
    也就是说,最后一个方法调用我前面在
    animateViewDidAppear
    中定义的块

    首先,我相信
    HyAnimation集合
    HyAnimation
    实例被困在块中,并且
    HyAnimation
    对这些块有很强的引用。你同意吗?我怎样才能解决这个问题?我尝试使用
    \uuu block
    声明这两个变量,但在这方面似乎没有效果

    无论如何,我也很难把仪器的记忆分析和这个问题联系起来,这就是为什么这篇文章这么长的原因

    谢谢你对我的宽容,我为你读了这么久的书而道歉

    更新:

    看来我是对的。继@Stephen Darlington在my上的帖子之后,我在
    HyAnimationCollection
    中重写了
    dealoc
    方法。与他的建议相反,我没有设置断点,而是编写了一个
    NSLog
    。到现在为止,我从未记录过任何东西

    - (void)dealloc
    {
        NSLog(@"dealloced");
    }
    
    我所做的是将另一个属性添加到
    HyAnimation
    应该清理完成。如果为true,
    animateWithTimer:
    在完成时调用此函数:

    - (void)cleanUp
    {
        // Get rid of everything
        self.animations = [[NSMutableArray alloc] init];
        self.updateFunctions = [[NSMutableArray alloc] init];
        self.completionFunctions = [[NSMutableArray alloc] init];
    }
    
    我立即在控制台上看到了日志,因此肯定存在一个保留周期。问题是,我怎样才能解决它?
    \u block
    不应该解决这个问题吗

    更新2

    我刚刚意识到这已经足够了:

    - (void)cleanUp
    {
        // Get rid of everything
    //    self.animations = [[NSMutableArray alloc] init];
    //    self.updateFunctions = [[NSMutableArray alloc] init];
        self.completionFunctions = [[NSMutableArray alloc] init];
    }
    
    这意味着,
    completionFunctions
    毕竟是创建闭包的函数。我目前使用的唯一地方是
    HyAnimationCollection
    ,具体如下:

    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            __block HyAnimationCollection * me = self;
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
    
                static unsigned int complete = 0;
    
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++complete;
    
                if (complete == [me.animations count]) {
    
                    // Reset, so the animation can be run again
                    complete = 0;
    
                    @synchronized(me) {
    
                        // Release all locks
                        [me.locks setLocked:NO];
    
                        // Done
                        me.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
    也就是说,保留周期必须在这里,对吗?但是在哪里呢?它可能是第一个
    @同步的
    块吗

    更新

    \uuu-weak
    替换
    \uu-block
    似乎成功了。即使没有
    cleanUp
    ,对象也会释放,但不会正确释放。首先,它发布
    HyAnimationCollection
    ,然后发布
    HyLockCollection
    ,最后发布所有
    HyAnimation
    s
    HyLock
    不应该发布,因为我在属性中保留了对它的强引用

    让我们再看看
    HyAnimationCollection
    addAnimation:

    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Prevent a strong circular reference
            __weak HyAnimationCollection * me = self;
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
    
                static unsigned int complete = 0;
    
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++complete;
    
                if (complete == [me.animations count]) {
    
                    // Reset, so the animation can be run again
                    complete = 0;
    
                    @synchronized(me) {
    
                        // Release all locks
                        [me.locks setLocked:NO];
    
                        // Done
                        me.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
    问题是,只有在动画完成时才会调用此闭包。因为
    HyAnimationCollection
    是第一个被释放的,这意味着当所有动画完成时,
    HyAnimationCollection
    已经被释放,正如您所看到的,这导致它不会释放锁


    现在我有了完全相反的问题=)编码是如此有趣好吧,问题似乎是这样的:

    • 如果在
      -addAnimation:
      内的块中有一个对
      self
      的强引用,则会得到一个保留周期
    • 如果在
      -addAnimation:
      内的块中对
      self
      的引用较弱,
      self
      会过早释放,并且完成块毫无意义
    您需要的是一种在运行时打破保留周期的方法:

    // Version 1:
    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
    
                static unsigned int complete = 0;
    
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++complete;
    
                if (complete == [self.animations count]) {
    
                    // Reset, so the animation can be run again
                    complete = 0;
    
                    @synchronized(self) {
    
                        // Release all locks
                        [self.locks setLocked:NO];
    
                        // Break retain cycle!!
                        [self.animations removeAllObjects];
    
                        // IF it still doesn't work, put a breakpoint at THIS LINE, and
                        // tell me if this code here runs ever.
    
                        // Done
                        self.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
  • self
    指向一个数组
  • 数组指向一个
    动画
  • 动画
    指向一个块
  • 该块指向
    self
  • 当其中一个连接断开时,整个循环就会中断。打破循环的最简单方法是通过调用数组上的
    -removeAllObjects
    来销毁数组指针(1)


    顺便说一句,代码的另一个问题是
    HyAnimationCollection
    中的
    static
    变量。当您同时运行多个
    HyAnimationCollection
    对象时,这将是一个问题。我只需要创建一个实例变量
    unsigned int\u completeCount

    (你可以
    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Prevent a strong circular reference
            __weak HyAnimationCollection * me = self;
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
    
                static unsigned int complete = 0;
    
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++complete;
    
                if (complete == [me.animations count]) {
    
                    // Reset, so the animation can be run again
                    complete = 0;
    
                    @synchronized(me) {
    
                        // Release all locks
                        [me.locks setLocked:NO];
    
                        // Done
                        me.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
    // Version 1:
    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
    
                static unsigned int complete = 0;
    
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++complete;
    
                if (complete == [self.animations count]) {
    
                    // Reset, so the animation can be run again
                    complete = 0;
    
                    @synchronized(self) {
    
                        // Release all locks
                        [self.locks setLocked:NO];
    
                        // Break retain cycle!!
                        [self.animations removeAllObjects];
    
                        // IF it still doesn't work, put a breakpoint at THIS LINE, and
                        // tell me if this code here runs ever.
    
                        // Done
                        self.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
    @interface HyAnimationCollection .....
    {
        unsigned int _completeCount; //is automatically initialized to 0 by the objc-runtime.
    }
    
    ...
    
    // Version 2:
    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
                @synchronized(self) {
                    // We are only interested in knowing when the animations complete, so we can release the locks
                    ++_completeCount;
    
                    if (_completeCount == [self.animations count]) {
    
                        // Reset, so the animation can be run again
                        _completeCount = 0;
    
                        // Release all locks
                        [self.locks setLocked:NO];
    
                        // Break retain cycle!!
                        NSLog(@"Breaking retain cycle (HyAnimationCollection)");
                        [self.animations removeAllObjects];
    
                        // Done
                        self.isRunning = NO;
                    }
                }
            }];
    
            return YES;
        }
    }
    
    // Version 3.
    - (BOOL)addAnimation:(HyAnimation*)animation
    {
        @synchronized(self) {
    
            if (self.isRunning) {
                return NO;
            }
    
            [self.animations addObject:animation];
    
            // Self-subscribe for updates so we know when the animations end
            [animation addCompletionFunction:^(HyAnimation * anim) {
                assert([NSThread isMainThread]);
                // We are only interested in knowing when the animations complete, so we can release the locks
                ++_completeCount;
    
                if (_completeCount == [self.animations count]) {
    
                    // Reset, so the animation can be run again
                    _completeCount = 0;
    
                    // Release all locks
                    [self.locks setLocked:NO];
    
                    // Break retain cycle!!
                    NSLog(@"Breaking retain cycle (HyAnimationCollection)");
                    [self.animations removeAllObjects];
    
                    // Done
                    self.isRunning = NO;
                }
            }];
    
            return YES;
        }
    }