iOS内存优化

本文介绍了iOS应用中常见的内存管理问题及优化策略,包括如何使用NSZombieEnabled检测悬挂指针,通过Analyze工具静态分析内存泄漏,利用Leaks Instrument定位内存泄露,以及解决UITextField内存泄漏、CGMutablePathRef内存管理、NSTimer循环引用等问题。此外,还提到了图片加载的内存优化技巧和SDWebImage的配置建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、运行MemoryProblems后,运行崩溃出现EXC_BAD_ACCESS,启动NSZombieEnabled,选中Edit Scheme并点击,Run -> Diagnostics -> Enable Zombie Objects(悬挂指针的检测),设置完之后,再次运行和点击页面,虽然会再次crash,但这次控制台打印了有用信息,点击Continue program execution按钮继续运行,对比找到相同地址并修改(启动MallocStackLogging

常见原因:某变量被assign修饰,对变量值后,它的对象就马上释放,而变量也不是strong而是weak,此时仍然使用就会导致程序crash


2、手动静态分析:应用Product—>Analyze或捷键shift + command + b进行内存泄漏的初步检测

    自动静态分析:在Build Settings启用Analyze During 'Build',每次编译时都会自动静态分析


3、可以在xcode的build setting中打开implicit retain of ‘self’ within blocks,xcode编译器会给出警告,逐个排查警告


4、应用Leak Instrument进行内存泄露查找:点击Xcode的菜单栏的 Product -> Profile 启动Instruments,出现Instruments的工具集,选中Leaks子工具点击,点击红色圆点按钮启动Leaks工具,在Leaks工具启动同时,模拟器或真机也跟着启动,启动Leaks工具后,它会在程序运行时记录内存分配信息和检查是否发生内存泄露

首先点击Leak Checks时间条那个红色叉,点击红色叉后,下面显示Leaks By Backtrace,双击某行内存泄露调用栈,会直接跳到内存泄露代码位置

Leak Instrument有Cycles & Roots界面:Persistent Bytes和#Persistent。#Persistent是object的数量,也就是allocation的次数,而Persistent Bytes是具体的内存大小。#Persistent是我们需要关注的,内存有没有泄露也是看这个值是不是只增不减。

Allocations:启动Allocations,勾选列表最上边的,右边设置勾选:Discard unrecorded data upon stop、Identify virtual C++ objects、* isContain…Record 

列表勾选VM

Generation Analysis

这个功能是非常有用的,一般是这样用的:进入一个页面前mark一下,在退出这个页面的时候再mark一下可以比较哪些内容增加了,就可以具体分析哪些内存没有被释放


Call Tree:需要我们把列表展示类型切换成Call Trees,能够非常清晰的看到调用树

Separate by Category:按照类别隔开,我们钩上看看效果

Separate by Thread:按照线程划分,我个人不是很喜欢这种划分,因为我不是很关心线程

Invert Call Tree:反转调用,我们给一张对比图就不需要解释了

Hide System Libraries:这个似乎是必钩的,因为我们目前只关心自己的方法,不关心系统的

Flatten Recursion:扁平化递归


Data Mining:数据挖掘,这是一个很具有噱头的功能

点击Symbol、Library会自动把你选中的行的符号、库加到小框中

符号和库有两个选项,就是是否过滤改行;点击Restore会去掉小框中的选中行



5、通过查看dealloc是否调用查看某个class是否泄漏问题

- (void)dealloc

{

    NSLog(@"release XXXXViewController");

}

方法: __weak XXXXViewController *weakSelf = self;在Block里用weakSelf


常见问题:

1、UITextField在iOS 11内存泄漏问题:UITextField没释放原因使用secureTextEntry属性,解决方案

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {

    if (textField == self.passWordTextField) {

        textField.secureTextEntry = YES;

    }else {

        textField.secureTextEntry = NO;

    }

    return YES;

}


2、使用CGMutablePathRef path = CGPathCreateMutable();时出现Potential leak of an object stored into 'path’解决方案

CGPathRelease(path);

creat,copy作为关键字的函数都是需要释放内存的,注意配对使用。比如:CGColorCreate<-->CGColorRelease


3、The 'viewWillDisappear:' instance method in UIViewController subclass 

XXX is missing a [super viewWillDisappear:] callm,解决方案

- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];

}


4、调用

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

方法之后在不需要NSTimer时及时调用[self.timer invalidate];千万不要在dealloc方法中调用,因为NSTimer强引用self,所以不会执行dealloc方法。


5、对象之间的循环引用:例子:两个ViewController都需要使用对方,这个时候可以用@class ; 

说明:在 .h 中引入某个类, @class 指的是 当前文件 只是引入类名, 并没有使用类里面的东西. 想要在 .m 里面使用 类的内容的话, 还是要 #import <>, 这种情况跟 上面的对象之间的防止循环引 有点不一样


6、如果是C申请的内存,注意new delete, malloc free的配对处理。


7、图片相关:

缓存:imageNamed:

只需传入文件名.扩展名即可。

可以加载bundle中任意位置的图片,包括main bundle中其他bundle的。

imageNamed方法创建对象的步骤如下:

7.1根据图片文件名在缓存池中查找图片数据,如存在,则创建对象并返回;

7.2如果不存在,则从bundle中加载图片数据,创建对象并返回;

7.3如果相应的图片数据不存在,返回nil。

不缓存:imageWithContentsOfFile:

必须传入图片文件的全名(全路径+文件名)

无法加载Images.xcassets中的图片。

对于大的图片且偶尔需要显示的应放到工程目录下,不要放到Assets.xcassets中;并使用imageWithContentsOfFile加载不让系统缓存

background.image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/*.png"]];

对于经常需要展示的小图片放到Assets.xcassets中让系统缓存,使用imageNamed加载

background.image = [UIImage imageNamed:@"*.png"];


不常用大图:将imageView.image = [UIImage imageNamed:nameArr[index]];

改为imageView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:nameArr[index] ofType:@"png"]];


8、出现VM:CG raster data,SDWebImage的问题

需要在Appdelegate中设置一下

[[SDImageCache sharedImageCache] setShouldDecompressImages:NO];

[[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];

[[SDImageCache sharedImageCache] setShouldCacheImagesInMemory:NO];


9、VM:CoreAnimation

Found out that animation caused by the inner pages.

Inside the pageViewController(viewController that added to the scrollView as a page) on viewWillDisappear:(BOOL)animated method I added this

for (CALayer* layer in [self.view.layer sublayers]) {

        [layer removeAllAnimations];

}

it resolved the problem.


10、@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;出现Property of mutable type 'NSMutableURLRequest' has 'copy' attribute; an immutable object will be stored instead,解决方案

@property (readwrite, nonatomic, strong) NSMutableURLRequest *request;


### iOS 开发中内存优化的技术与最佳实践 在 iOS 开发中,内存优化是确保应用性能和用户体验的关键环节。以下是一些技术和最佳实践,能够帮助开发者有效管理内存资源。 #### 使用 NSCache 管理缓存 为了减少内存占用,可以使用 `NSCache` 类来管理图片或其他数据的缓存。`NSCache` 会在系统内存不足时自动释放一些缓存对象,从而避免因缓存过多导致的内存泄漏问题[^2]。此外,开发者也可以根据实际需求手动控制缓存的清理操作。 ```swift let imageCache = NSCache<NSString, UIImage>() func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) { if let cachedImage = imageCache.object(forKey: url.absoluteString as NSString) { completion(cachedImage) return } // 下载并缓存图片 URLSession.shared.dataTask(with: url) { data, _, _ in guard let data = data, let image = UIImage(data: data) else { completion(nil) return } imageCache.setObject(image, forKey: url.absoluteString as NSString) DispatchQueue.main.async { completion(image) } }.resume() } ``` #### 及时释放不再使用的资源 在应用运行过程中,应及时释放不再使用的资源,例如图片、数据缓存等。对于临时创建的大对象,在使用完毕后应尽快将其设置为 `nil`,以便 ARC(Automatic Reference Counting)能够及时回收内存。 ```swift class ViewController: UIViewController { var largeObject: LargeObject? override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) largeObject = nil // 手动释放大对象 } } ``` #### 优化数据结构以减少内存占用 选择合适的数据结构可以显著减少内存占用。例如,使用 `Array` 或 `Dictionary` 的紧凑形式存储数据,或者通过压缩算法减小数据体积。 ```swift // 使用紧凑数组存储数据 var compactArray: [UInt8] = [] compactArray.append(contentsOf: someData.compactMap { $0 }) ``` #### 减少不必要的图形绘制 应用的图形处理对 GPU 的使用率较高,会增加耗电量。可以通过减少不必要的图形绘制、优化纹理资源、降低图像分辨率等方式来降低 GPU 的渲染负担,从而减少耗电量[^3]。 ```swift // 优化图像分辨率 func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(targetSize, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } image.draw(in: CGRect(origin: .zero, size: targetSize)) return UIGraphicsGetImageFromCurrentImageContext() } ``` #### 避免循环引用 循环引用会导致内存无法被释放,因此需要特别注意。通过使用 `weak` 或 `unowned` 关键字可以打破循环引用关系[^1]。 ```swift class Parent { var child: Child? } class Child { weak var parent: Parent? // 使用 weak 避免循环引用 } let parent = Parent() let child = Child() parent.child = child child.parent = parent ``` #### 使用 Instruments 工具检测内存泄漏 Xcode 提供了强大的 Instruments 工具,可以帮助开发者检测内存泄漏和高内存占用问题。通过分析内存分配情况,可以定位并修复潜在的内存问题[^1]。 ```bash # 在 Xcode 中打开 Instruments 工具 xcrun instruments -t "<template>" <path-to-app> ``` #### 总结 通过遵循上述技术和最佳实践,开发者可以在 iOS 应用开发中有效进行内存优化,提升应用性能和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值