2013-07-18 15:39:25 likandmydeer 阅读数 3653
前一段时间,在网上找如何释放内存的方法。可是,怎么找也找不到。今天我在不断的测试看view的父窗口

NSLog( @"%@" , self.view.superview.superview.superview.superview );

这时我就发现,每次页面转跳,父窗口和父窗口的子窗口还是存在,如果一直页面跳转,没有把没用的窗口的内存释放,内存就越来越多,就像天梯那样,永远都没有尽头。

废话不多说,进入重点。

- (void) viewWillDisappear:(BOOL)animated
{
    [ super viewWillDisappear: animated ];
    // 画面隐藏时停止timer
    if ( [ timer isValid ] )
    {
        [ timer invalidate ];
    }
//    NSLog( @"%@" , self.view.subviews );
//    [ self.view removeFromSuperview ];
    for ( UIView* temp in self.view.subviews )
    {
        [ temp removeFromSuperview ];
    }
    
    //--- 释放内存 ---//
    // 套题数组
//    allTheQuestions = nil ;
}

我是在页面将要消失之前释放没用的指针,我就举例,上面代码中

allTheQuestions 是一个可变数组(NSMutableArray)存储了40个元素。如果我在页面跳转之前没有释放这个数组的内存,如上代码。用Instrument来检测如下图:


当我把内存释放之后,如下图:


关键就是把没用的指针给它赋值未nil。
2018-06-22 15:04:51 nhfc99 阅读数 0

有很多人说在ARC模式下内存释放比如self.label = nil放在-(void)viewDidDisappear:(BOOL)animated中,这个地方可以是可以放但是有些时候是不可以放的比如使用了UINavigationcontroller,进行出栈的时候发现有的视图不见了。

其实在ARC下内存的释放和非ARC模式下的内存释放方式大同小异,只是系统帮助处理了retain和release,释放内存的时候依然使用-(void)dealloc,特别是在某个页面中占用内存较大的时候同时有代理存在的时候self.delegate = nil这个是很有必要的,
2014-09-23 11:07:56 u011156012 阅读数 3539

最近项目中遇到了很多关于内存泄露和缓存清理的问题,尤其是ARC这块内存什么时候释放,以及UIWebview的缓存清理,消耗了很多时间,特别做个总结:

1. 关于ARC内存释放猜想

我们都知道在ARC机制下,我们不需要手动管理内存,例如Autorelease和release等函数都不能调用,系统会自动为我们释放,我们需要做的,仅仅是把指针设置成nil就可以了,如果需要,再把相应的view remove掉就可以了。但是在实际代码过程中,会发现申请的指针在这个函数体执行完毕之后并没有把内存释放掉。在网上搜索了好久也没有什么好的解释。先说下具体的例子吧。

例如有个函数a,作用是生成一张图片,那在函数体里面我们需要new一个指针出来,指向生成的图片,UIImageView* img = [UIImage alloc]init ... 具体就不写了,在函数即将结束的时候,我们将img = nil。到此实际就完成了ARC所要求的用户操作了。但是我们debug时会发现,这快内存并没有实时释放掉。每当我们调用一下函数a,内存就会增大一部分。似乎是系统将我们生成的指针丢在了某块内存池中,但是这个池子什么时候释放掉,是系统说了算的,所以当函数执行完的时候,内存并没有被释放。

那如果解决呢?

我们申请一个全局指针,在整个程序的生命周期都存在,这样每当我们调用函数a的时候,就把这个全局指针指向生成的图片。这样就不会造成每次都要new一块内存出来而释放不掉的问题了。

2. UIWebView缓存清理

项目中要显示很多webview,但是当我们切换web的内容是,发现以前的缓存并没有释放掉,于是就会出现每加载一遍,内存就蹦很多的情况。在网上搜了下解决办法。

最简单的一种是每次在申请之前让webview load一个空的URL:

webView loadHTMLString:NULL baseURL:NULL;

但是这种方法似乎不能完全清理缓存,于是在stackoverflow上搜到了正确的解决办法:http://stackoverflow.com/questions/2523435/how-to-clear-uiwebview-cache

if (lastReq){
    [[NSURLCache sharedURLCache] removeCachedResponseForRequest:lastReq];
    [[NSURLCache sharedURLCache] removeAllCachedResponses];

}

lastReq=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:localUrl]
                                   cachePolicy:NSURLRequestReloadIgnoringCacheData
                               timeoutInterval:10000];
[lastReq setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

[self.mainWebView loadRequest:lastReq];
其实给URLRequest设置一个cachePolicy,然后在申请新的web页面时,调用removeAllCacheResponse就可以了。

3. 优化效率

之前在做一个图片的指定颜色替换算法时,经过大师指点,其实已经把效率提高了好几倍,但是后来又站在汇编的角度上,更进一步地把算法优化提升了5,6倍。

优化前的算法:

if ([titles containsObject:areaName]) {
            areaPt.x = [[dic objectForKey:@"x"] floatValue];
            areaPt.y = [[dic objectForKey:@"y"] floatValue];
            areaSize.width = [[dic objectForKey:@"width"] floatValue];
            areaSize.height = [[dic objectForKey:@"height"] floatValue];
            areaColor = [dic objectForKey:@"color"];
            
            TheColor d;
            const CGFloat *componentsForAreaColor = CGColorGetComponents([self getColor:areaColor].CGColor);
            d.R = componentsForAreaColor[0] * 255.0;// components[0]*alpha
            d.G = componentsForAreaColor[1] * 255.0;
            d.B = componentsForAreaColor[2] * 255.0;
            
            endPtX = areaPt.x + areaSize.width;
            endPtY = areaPt.y + areaSize.height;
            
            // change color in (x,y, width,height)
            colColor = [colors objectAtIndex:[titles indexOfObject:areaName]];
            const CGFloat *components = CGColorGetComponents(colColor.CGColor);
            for (unsigned int i=areaPt.y; i<endPtY; i++) {
                for (unsigned int j=areaPt.x; j<endPtX; j++) {
                    index = 4*j + 4*i*imageMap.size.width;//每个像素由四个分量组成所以要乘以4
                    c.R = rawData[index];
                    c.G = rawData[index+1];
                    c.B = rawData[index+2];
                    
                    if ((c.R == d.R)&&(c.G == d.G)&&(c.B == d.B)) {
                        c.R = components[0] * 255.0;// components[0]*alpha
                        c.G = components[1] * 255.0;
                        c.B = components[2] * 255.0;
                        rawData[index] = c.R;
                        rawData[index+1] = c.G;
                        rawData[index+2] = c.B;
                    }
                }
            }
        }

优化后的算法:

if ([titles containsObject:areaName]) {
            areaPt.x = [[dic objectForKey:@"x"] floatValue];
            areaPt.y = [[dic objectForKey:@"y"] floatValue];
            areaSize.width = [[dic objectForKey:@"width"] floatValue];
            areaSize.height = [[dic objectForKey:@"height"] floatValue];
            areaColor = [dic objectForKey:@"color"];

            endPtX = areaPt.x + areaSize.width;
            endPtY = areaPt.y + areaSize.height;
            //用来查找地图区域的颜色
            const CGFloat *componentsForAreaColor = CGColorGetComponents([self getColor:areaColor].CGColor);
            oldColor = 255;
            oldColor = oldColor*256 + (unsigned char)(componentsForAreaColor[2]*255.0);
            oldColor = oldColor*256 + (unsigned char)(componentsForAreaColor[1]*255.0);
            oldColor = oldColor*256 + (unsigned char)(componentsForAreaColor[0]*255.0);
            //要更新成的颜色
            colColor = [colors objectAtIndex:[titles indexOfObject:areaName]];
            const CGFloat *components = CGColorGetComponents(colColor.CGColor);
            newColor = 255;
            newColor = newColor*256 + (unsigned char)(components[2]*255.0);
            newColor = newColor*256 + (unsigned char)(components[1]*255.0);
            newColor = newColor*256 + (unsigned char)(components[0]*255.0);
            
            for (unsigned int i=areaPt.y; i<endPtY; i++) {
                for (unsigned int j=areaPt.x; j<endPtX; j++) {
                    index = j + i*width;
                    if (rawData[index] == oldColor)
                        rawData[index] = newColor;
                }
            }
        }

原先是直接通过color.R/G/B值对比,需要取3次,并且在对比时,也需要进行&&的判断,这些在汇编中其实是好几行机器代码在执行,所以,改成优化后的算法,其实是比原先快了5,6倍的。直接从1.9秒提高到了0.3秒左右。


2014-08-22 11:25:15 allison162004 阅读数 0

转:点击打开链接

iOS提供了ARC功能,很大程度上简化了内存管理的代码。

但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露。

下面列举两种内存泄露的情况。

1,循环参照

A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。

这种问题常发生于把delegate声明为strong属性了。

例,

@interface SampleViewController
@property (nonatomic, strong) SampleClass *sampleClass;
@end

@interface SampleClass
@property (nonatomic, strong) SampleViewController *delegate;
@end

上例中,解决办法是把SampleClass 的delegate属性的strong改为assing即可。


ARC通过记录指向某对象的指针数量来判断该对象是否应该被释放(0代表可以释放)。因而如果出现循环引用,就会出现内存泄露。如下图所示。

 取消3->2的引用   导致泄露。

ARC的解决办法也和简单,使用弱引用(weak reference)。弱引用的申明方式举例如下:
id __weak P;
@property (weak) NSVIew *V;
还是以上面的例子做对比,结果如下:

 弱引用不增加计数, 从而消除leak。


2,死循环

如果某个ViewController中有无限循环,也会导致即使ViewController对应的view关掉了,ViewController也不能被释放。

这种问题常发生于animation处理。

例,

比如,

CATransition *transition = [CATransition animation];
transition.duration = 0.5;
tansition.repeatCount = HUGE_VALL;
[self.view.layer addAnimation:transition forKey:"myAnimation"];

上例中,animation重复次数设成HUGE_VALL,一个很大的数值,基本上等于无限循环了。

解决办法是,在ViewController关掉的时候,停止这个animation。

-(void)viewWillDisappear:(BOOL)animated {
    [self.view.layer removeAllAnimations];
}

内存泄露的情况当然不止以上两种。

即使用了ARC,我们也要深刻理解iOS的内存管理机制,这样才能有效避免内存泄露。


2016-08-04 14:00:37 PianZhideNanRen 阅读数 1240

很多同学因为没有经历过使用手动引用计数来管理内存,一直在ARC下愉快的开发导致对iOS内存管理方式的不理解,巧哥有篇专门说明iOS内存管理,我也是记录学习中的点滴,共勉之。

我刚接触iOS的时候ARC已经出现,但是大家对这种黑科技都保持怀疑态度,大部分人还是在使用手动管理内存,后来Xocde默认ARC模式才开始快速推广并被大家所认可。

ARC模式下虽然很好的解决手动管理内存的问题,但是还是有瑕疵的:1.对象间的循环引用;2.Core Foundation底层类库的对象需要手动管理内存;

对象间的循环引用

循环引用就是两个(或多个环形)对象都同时持有对方造成引用的对象不能释放导致内存泄漏;
解决方法有两个:1.手动显示断开引用;2.对象的弱引用(weak);

手动显示断开引用

这种方法是在程序员知道哪里会出现循环引用,在合适的位置把对象释放(nil)以此来结束循环引用释放所有相关对象。

对象的弱引用(weak)

在iOS里有强引用和弱引用之分,声明成员变量Strong是强引用,Weak是弱引用;强引用是当前对象释放的时候会同事释放因为当前对象持有这个变量;弱引用是不被当前对象持有的,在当前对象被释放的时候这个变量会设置成nil,这样也避免了向已经销毁的对象发送消息产生崩溃的问题。
大家现在都喜欢使用block,大量的block容易产生循环引用,使用弱引用:__weak ViewController *weakSelf = self
在声明delegate的时候,使用Weak来修饰;
这些都可以解决在开发中遇到的循环引用问题。

Core Foundation底层类库的对象内存管理

iOS的底层类库Core Foundation的对象不支持ARC,在使用的时候就需要我们自己手动控制了;例如:CGColorRef,CFFountRef这些要使用CGColorRetain+1、CGColorRelease-1;
CGColorSpaceRef cmykSpace = CGColorSpaceCreateDeviceCMYK();
        CGFloat cmykValue[] = {1, 1, 0, 0, 1};      // blue
        CGColorRef colorCMYK = CGColorCreate(cmykSpace, cmykValue);
        CGColorSpaceRelease(cmykSpace);
        CGColorRetain(colorCMYK);
        CGColorRelease(colorCMYK);
        NSLog(@"colorCMYK: %@", colorCMYK);

如果使用CGColor对象转换成OC对象引入的有关键字bridge,大家可以查看相关资料;

Xcode检测循环引用

Xcode的内置工具Leaks可以让我们发现程序里的内存泄漏,Product——>Profile,选择Leaks,确认Choose,启动模拟器.


再次选择Profile


上图小红X就是说明有循环引用的问题,双击标记2的地方会直接进入错误代码位置。

虽然现在大家都使用ARC但是也要知道内存管理相关的知识,这些是开发的基础,大家一起学习吧!

iOS中ARC机制详解

阅读数 21216