app内存问题

[toc]

app内存问题

一、如何排查内存问题

参考文章:

2内存-③内存泄漏定位.md

二、优化前后数据对比

时刻 目前情况 取消图片缓存扩容设置 去掉TTF文件初始化
打开APP时 img img img
滑动瀑布流 img img img
优化效果说明 优化后,在滑动瀑布流的时候,内存从原来的稳定1.24G,降低到稳定的896M 优化后,打开APP时,内存明显再降低了80M左右

问1:滑动过程中的突刺是什么操作产生?

问2:滑动过程内存增加用户操作是?增加后,内存增加到峰值就没增加的原因?如果估算峰值/峰值是该数值有计算方法?

问3:去掉ttf后,内存降低的数值怎么计算?是1:1的方式?

三、下载的内存分析和优化

以下载以下文件为例,

网络资源:https://media.xihuanwu.com/applet/wishhouse/reward/svga/rocket.svga (13MB)

序号 步骤 Dio 5.2.1
(Flutter)
HTTP 0.13.6
(Flutter)
OkHttp3 4.10.0
(Android)
0 未下载时 同左 同左
1.1 rocket.svga–13MB
下载过程峰值
1.2 rocket.svga–13MB
下载结束后置放
1.3 rocket.svga–13MB
下载结束后切换后台
1.4 rocket.svga–13MB
下载结束后切换后台回前台
2.1 rocket.svga–13MB
下载过程峰值
2.2 rocket.svga–13MB
下载结束后
3.1 rocket.svga–13MB
下载过程峰值
3.2 rocket.svga–13MB
下载结束后
4.1 rocket.svga–13MB
下载过程峰值
4.2 rocket.svga–13MB
下载结束后
5.1 rocket.svga–13MB
下载过程峰值
5.2 rocket.svga–13MB
下载结束后
884M
小结 在5次下载后,内存占用达到884MB,尝试将应用切换至后台、置放、手动GC,内存均无变化。①每次下载内存增加?②下载的内存并没有办法得到释放? 原来的触发下载改为此项后,内存无影响变化

结论:

​ 通过以上数据可以看出,Flutter实现在每次下载时,内存都发生了显著的上升,而原生实现表现平稳,在执行以上操作后,Flutter实现的内存占用达到了 884MB,而原生为 284MB,相比Flutter实现 少了600MB的内存占用。

建议将现有下载转为原生实现,从而降低资源下载时的内存使用。

app发热问题

[toc]

app发热问题

其他参考文章:

CPU或GPU负荷过高是导致手机过度发热的主要原因之一,因此优化应用程序的CPU或GPU负荷是解决这个问题的关键。以下是一些常见的优化方法:

减少图形处理:如果您的应用程序涉及大量的图形处理,例如3D动画、特效等,可以尝试降低图形处理的质量或数量,以减轻GPU的负担。

优化算法和数据结构:通过优化算法和数据结构,可以减少CPU和GPU的计算量,从而降低负荷。例如,使用更高效的排序算法、数据缓存和内存管理等技术,可以提高应用程序的性能并减少计算量。

使用线程管理:使用线程管理技术可以将应用程序的不同部分分配到不同的线程中,从而减少CPU和GPU的负荷。例如,在主线程中处理用户界面,而在后台线程中处理数据下载和计算等任务。

延迟加载:延迟加载技术可以将应用程序的资源和数据分批加载,而不是一次性全部加载。这样可以减少CPU和GPU的负荷,同时也可以提高应用程序的响应速度和用户体验。

缓存数据:缓存数据可以减少应用程序对网络和磁盘的访问,从而降低CPU和GPU的负荷。例如,将数据缓存在内存中,而不是每次都从网络或磁盘中读取。

使用硬件加速:一些高端手机和平板电脑提供了硬件加速功能,例如GPU加速、硬件解码等。如果您的应用程序需要进行大量的图形或视频处理,可以考虑使用硬件加速来降低CPU和GPU的负荷。

离开web游戏页面停止计时器、停止渲染

调查:

直播的时候也热,正常性能消耗高
网络耗流量

原生交互?指引擎初始化?

CPU
渲染 GPU 图片批量处理,而不是单单
图片处理 图片大小。

接口:http - sockt
定时器个数、
循环精度、

iOS、Android webView的缓存大小

第2节:Swift扩展(Extensions)

[TOC]

入门二

  • 一、Swift扩展(Extensions)

一、Swift扩展(Extensions)

Swift基础部门:http://www.swift51.com/swift4.0/chapter2/01_The_Basics.html

详见:http://www.swift51.com/swift4.0/chapter2/21_Extensions.html中的2.21扩展

在swift中extension(扩展)与Objective-C的category(分类/扩展/类别)有点类似,但是extension比起category来说更加强大和灵活,它不仅可以扩展某种类型或结构体的方法,同时它还可以与protocol等结合使用,编写出更加灵活和强大的代码。

二、

Swift3.0语法变化

swift 枚举的取值

Swift中协议的可选方法的实现判断

二、第三方库

OC第三方库需要在桥街文件中引入,swift第三方库需要import model方式引入!桥街文件不能引入swift第三方库

  • Network:

Alamofire:著名的AFNetworking网络基础库Swift版 - Alamofire/Alamofire · GitHub

SwiftyJSON:最为开发者认可的JSON解析类 - SwiftyJSON/SwiftyJSON · GitHub

  • Storage:

SQLite.swift:简单、轻量,使用上最SQL的SQLite封装库 - stephencelis/SQLite.swift · GitHub

SugarRecord:基于CoreData与REALM的好用封装 - SugarRecord/SugarRecord · GitHub

  • UI:

SweetAlert:带动画效果弹窗封装类 - codestergit/SweetAlert-iOS · GitHub

RAMAnimatedTabBarController:灵动的动画标签栏类库 - Ramotion/animated-tab-bar · GitHub

PNChart-Swift:带动画效果的图表控件库 - kevinzhow/PNChart-Swift · GitHub

LTMorphingLabel:各种文字动画效果 - lexrus/LTMorphingLabel · GitHub

Cartography:用代码解决麻烦的AutoLayout - robb/Cartography · GitHub

other:

pod ‘SVProgressHUD’

pod ‘MJRefresh’

pod ‘SnapKit’

Swift常用第三方库

2017最受欢迎的30个Swift 库,你关注到了吗?

Swift常用第三方库

基础知识

iOS 宏定义-获取状态栏、导航栏、tabBar高度

https://www.jianshu.com/p/801bdea428f8

image-20201121001108987

1
2
3
4
5
6
7
let screenHeight = UIScreen.main.bounds.size.height
let statusBarHeight = UIApplication.shared.statusBarFrame.size.height
let navigationBarHeight = self.navigationController?.navigationBar.frame.size.height ?? 0
let tabbarHeight = self.tabBarController?.tabBar.frame.height ?? 0
let screenWidth = UIScreen.main.bounds.size.width
let iphoneX = screenWidth >= 375.0 && screenHeight >= 812.0
let bottomGuideHeight: CGFloat = iphoneX ? 34 : 0

开发随笔

[toc]

开发随笔

使用AVPlayerViewController之前先导入AVKit头文件

支持的视频编码格式很有限:H.264、MPEG-4,扩展名(压缩格式):.mp4、.mov、.m4v、.m2v、.3gp、.3g2等,如果是RMVB就不行了,需要借助第三方的框架来实现更多格式的支持。

iOS视频播放的基本方法

https://www.jianshu.com/p/b304694af77a

浅谈iOS的AVPlayerViewController播放视频

https://www.jianshu.com/p/3f8e5045c087

AVPlayer 循环播放本地视频

https://blog.csdn.net/tailoffairylu/article/details/83142802

开发随笔

[toc]

开发随笔

iOS研发助手DoraemonKit

iOS研发助手DoraemonKit技术实现(一)

https://www.jianshu.com/p/00763123dbc4

iOS 常用调试方法:LLDB命令

iOS 线程保活

iOS RunLoop(一)

iOS中NSOperation详解

iOS 逆向开发16:HOOK原理上(HOOK 系统C函数)

另类iOS上的C函数hook

iOS被开发者遗忘在角落的NSException-其实它很强大

UITextView对齐

textView中的文字默认离顶部和底部是有间距的

1
2
3
4
UITextView *textView = [[UITextView alloc] init];
NSLog(@"...%@", NSStringFromUIEdgeInsets(textView.textContainerInset)); // 默认值{8, 0, 8, 0}

textView.textContainerInset = UIEdgeInsetsZero;

iOS 侧滑返回详解FDFullscreenPopGesture

https://www.jianshu.com/p/62015b4c9076

哆啦A梦 只能显示nslog,无法显示debugPrint

podfile中 use_frameworks! 和 #use_frameworks!区别

https://www.jianshu.com/p/ac629a1cb8f5

iOS 效果处理(内阴影、外阴影、外发光、内发光、投影)

https://blog.csdn.net/qq_34534179/article/details/109180717

IOS微信分享调起微信后立刻返回到app中无法分享的问题

https://blog.csdn.net/qq_35153373/article/details/112285261

iOS 14 popToRootViewControllerAnimated 底部tabbar消失

https://www.jianshu.com/p/c6b3ccff9e5b

美图秀秀的拼图功能

https://github.com/hxxyyangyong/MeituDemo

iOS 不规则(多边形)图形,贝塞尔曲线绘制自定义图形

https://www.jianshu.com/p/6786cc4d28a7?from=groupmessage

iOS UIButton 渐变色、边框渐变色、字体渐变色

https://blog.csdn.net/liwenjie0912/article/details/87548692

textfield

1、不要delloc

2、不要delegate = self. shouldchange不调用

3、二分法查找

iOS根据网络图片的size大小设置UIImageView的大小

https://www.cnblogs.com/sunfuyou/p/6284986.html

iOS 如何让button上的字体居左居右对齐

https://www.jianshu.com/p/737553cd8eb5

iOS 使用CGAffineTransform 使视图平移|旋转|缩放

https://www.jianshu.com/p/58d810cec41d

UILabel *titleLabel = [UILabel alloc]; 空指针

UILabel *titleLabel = [[UILabel alloc] init];

[container addSubview:titleLabel];

iOS开发 怎么删除UICollectionView的cell

https://jingyan.baidu.com/article/ceb9fb10bd32a08cac2ba053.html

UICollectionView执行performBatchUpdates 奔溃

https://blog.csdn.net/jamy08/article/details/50505859?utm_source=blogxgwz7

ios扩大按钮的点击区域

https://www.jianshu.com/p/9107be4cd84a

iOS 利用UICollectionView横向滚动、余弦函数曲线特性实现居中放大的卡片浏览工具 XLCardSwitch

https://blog.csdn.net/u013282507/article/details/54136812

iOS之事件穿透

https://www.jianshu.com/p/0bece5f27650

iOS 13-beta presentViewController 样式变化

https://www.jianshu.com/p/67901ae2323d

cocoapods 1.8.0版本之后,CDN: trunk 推荐解决方法

https://blog.csdn.net/ZHFDBK/article/details/106949342

Commond + alt + /

Pasted Graphic.png

让你不知道怎么死的RAC

[TOC]

前言:RAC学习起来的特点

  • 学习起来比较难
  • 团队开发的时候需要谨慎使用
  • 团队代码需要不断的评审,保证团队中所有人代码的风格一致!避免阅读代码的困难

一、RAC双向绑定UITextField的正确姿势

1、先说结果

1
2
3
4
5
6
7
8
9
// textField1: 键盘修改textField有问题的例子
RACChannelTo(self.viewModel, text1) = RACChannelTo(self.textField1, text);

// textField2: 代码修改textField有问题的例子
RACChannelTo(self.viewModel, text2) = self.textField2.rac_newTextChannel;

// textField3: 键盘和代码修改textField都没问题的例子
RACChannelTo(self.viewModel, text3) = RACChannelTo(self.textField3, text);
[self.textField3.rac_textSignal subscribe:RACChannelTo(self.textField3, text)];

比较结果如下列表所示:

未完整的双向绑定1 未完整的双向绑定2 完整的双向绑定
代码文本 RACBindTextField1 RACBindTextField2 RACBindTextField3
代码截图
textField1: 键盘修改textField有问题的例子```
```RACChannelTo(self.viewModel, text1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| textField               | textField1: 键盘修改textField有问题的例子                    | textField2: 代码修改textField有问题的例子                    | textField3: 键盘和代码修改textField都没问题的例子            |
| (通过代码)改变model时 | textField会改变 | textField会改变 | textField会改变 |
| 通过代码改变textField时 | model会改变 | <u>model不会改变</u> | model会改变 |
| 通过键盘改变textField时 | <u>model不会改变</u> | model会改变 | model会改变 |

#### 2、分析原因

要弄清为什么通过如上两种方式分别对textField1和textField2进行双向绑定会有问题,那么你需要先认识一下以下两个与textField有关的值的比较

| | | |
| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| | ```RACChannelTo(self.textField, text)``` | ```self.textField.rac_newTextChannel``` |
| 原理 | 当通过code改变self.textField.text的值的时候,才会把RACChannelTo(self.textField, text)这个值发送出去 | 当通过键盘改变self.textField.text的值的时候,才会把self.textField.rac_newTextChannel这个值发送出去 |
| 后果 | 所以只使用这个时,键盘修改textField会有问题 | 所以只使用这个时,代码修改textField会有问题 |



如果你还存疑惑,那么我们拿`textField1: 键盘修改textField有问题的例子`来说明:

```objective-c
// textField1: 键盘修改textField有问题的例子
RACChannelTo(self.viewModel, text1) = RACChannelTo(self.textField1, text);

我们在RACKVOProxy中的observeValueForKeyPath处设置断点,会发现,当我们只设置如上代码时候,通过键盘改变textField的值的时,其并未走入所设断点中,即其此时并未能检测到文本框的文本已经改变了。所以,也就出现了只设置如上代码,会出现当通过键盘改变文本框(键盘未收起)的时候,viewModel中的值没法改变的情况。

RACKVOProxy observeValueForKeyPath

同理,另一个的验证也如此。

3、解决问题

下面我们对RACChannelTo(self.viewModel, text3) = RACChannelTo(self.textField3, text);绑定方式进行键盘修改的完善,完善方式如下:

1
2
3
4
5
6
RACChannelTo(self.viewModel, text3) = RACChannelTo(self.textField3, text);
@weakify(self);
[self.textField3.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
@strongify(self);
self.viewModel.text3 = x;
}];

该部分代码,可简化为:

1
2
RACChannelTo(self.viewModel, text3) = RACChannelTo(self.textField3, text);
[self.textField3.rac_textSignal subscribe:RACChannelTo(self.textField3, text)];

即我们上诉开头时候的正确代码。

4、实际应用中还存在的bindViewModel的问题

4.1、在RAC绑定常见view中的textField的例子RACBindNorTextFieldViewController
1
2
3
4
5
6
7
8
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = NSLocalizedString(@"RAC Bind Normal TextField", nil);
[self setupViews];

[self bindViewModel];
}
4.2、在RAC绑定tableView中的textField的例子RACBindTvTextFieldViewController
1
2
3
4
5
6
7
8
9
10
11
12
13
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RACTextFieldBindTableViewCell *cell = (RACTextFieldBindTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"RACTextFieldBindTableViewCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor greenColor];

if (indexPath.row == 0) {
self.textField1 = cell.textField;
} else if (indexPath.row == 1) {
self.textField2 = cell.textField;
} else if (indexPath.row == 2) {
self.textField3 = cell.textField;
}
return cell;
}

那么bindViewModel的时机比较容易出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = NSLocalizedString(@"RAC Bind TableView TextField", nil);

[self setupViews];
//[self bindViewModel]; //textField未获取,无法进行绑定
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//[self bindViewModel]; //textField未获取,无法进行绑定
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self bindViewModel]; //tableView中的textField绑定的正确时机
}

5、遗留问题

遗留问题1:为什么只有键盘没回收时候的修改文本框值才有问题,而键盘回收时候不会也存在问题?

二、RAC监听的属性如何正确改变值(KVO的坑)

假设有如下需求,根据viewModel中的@property (nonatomic, assign) BOOL textFieldValid;值修改UIViewController中textField的自定义属性leftButtonSelected的值,怎么做???

答:RAC(self.textField, leftButtonSelected) = RACObserve(viewModel, textFieldValid);

问题是:是不是只要实现了这行代码就没什么问题了?或者说在实现这行代码前,你有什么需要注意的?如果你不知道,你不搞清楚原因,那么有一天RAC让你怎么死的,你都不知道。

1、下面将常见的你修改属性时候使用的代码及其效果,列举如下:

修改属性时候使用的代码 代码位置 效果
self.inTextFieldValid1 = inTextFieldValid1; viewModel中 正确
_inTextFieldValid2 = inTextFieldValid2; viewModel中 错误
[self setValue:@(inTextFieldValid3) forKey:@"inTextFieldValid3"]; viewModel中 正确
[self setValue:@(inTextFieldValid4) forKey:@”_inTextFieldValid4”]; viewModel中 错误
self.viewModel.outTextFieldValid1 = outTextFieldValid1; UIViewController中 正确
[self.viewModel setValue:@(outTextFieldValid2) forKey:@"outTextFieldValid2"]; UIViewController中 正确
[self.viewModel setValue:@(outTextFieldValid3) forKey:@”_outTextFieldValid3”]; UIViewController中 错误

上面正确与否的判断标准是什么?

其实如果熟悉KVO机制的你,应该知道KVO的本质是通过isa-swizzling新建了一个子类,并且重写了属性的setter方法,在setter方法的头和尾分别执行了willChangeValueForKey:didChangevlueForKey:两个方法来实现监听的。

所以,如果你修改属性时候使用的代码不会走setter方法,那么也就无法触发监听了。因而也就出现了你明明监听了属性,却无法正确运行的情况。

如上表格中的错误方法皆是不会走setter的。

下面是一张别人的图:

img

2、有无规避方法

问:通过如上解析,我们知道问题的根源是没调用setter,那我们可否通过编译器提示不能使用_xxx来规避???

在这里我们补充讲下@synthesize

写法1 写法2
源代码 @synthesize student; @synthesize student = _student;
等价代码 @synthesize student = student; —-同上—-

synthesize的作用就是让student = ?中的后者这个变量来“代替”属性,从而可以通过操作变量来进行属性的操作。但是有一点最关键的是,使用变量进行操作,属性本身的引用计数是不会增加的,因为没有经过调用setter方法或者是getter方法。但是如果使用self.student这种操作方式的话,实质上是通过setter或者是getter方法进行操作,引用计数会随着不同的操作而改变,了解了这点后就能够更好的避免内存泄露问题。

三、RAC监听数组的变化(KVO的坑)

iOS默认不支持KVO的形式来监听数组的变化,数组改变的时候,只是数组里面的值变化,但数组的地址没有变化,KVO监听的对象地址的变化。

由于不支持KVO来监听数组变化,就无法使用RAC来监听数组。

1、传统方式(我们不需要监听时候常使用的代码):
1
2
3
4
// 只是修改数组,无法触发监听
if (self.flawArray.count) {
[self.flawArray removeLastObject];
}
2、需要使用监听时候的数组修改
1
2
3
4
5
   // 修改数组时候同时能确保触发KVO的操作
if (self.okArray.count) {
NSMutableArray *kvo_okArray = [self mutableArrayValueForKey:@"okArray"];
[kvo_okArray removeLastObject];
}

四、RAC中监听通知的坑

请查看:RAC中监听通知的坑!

五、结束语

暂时到此!感谢查阅!