目录:
- 内存泄漏 Memory Graph
- 手动下载远程库来pod install / pod update
- podspec文件如果引入了其他模块引入的库也要声明
- app在后台时keyboard监听也会被触发
- if define防止头文件重复引入
- 打包release debug证书区别
- 淘口令
- 内存泄漏的一些情况
- presentVC iOS13适配
1. 内存泄漏 Memory Graph
内存泄漏的检查工具有好多啦,例如MLeaksFinder之类的,但是除了形成了cycle互相持有的那种,其他如果单纯知道了某个对象leak了,如何知道是什么持有了它呢?
Xcode有个很好用的工具,就是Memory Graph,可以看到现在的各种对象的持有关系:
memory graph
通过这个东东就能从左边找到你想知道有木有泄漏的对象(filter),然后右边就会出现它的持有关系啦~ 是不是超级棒!!
2. 手动下载远程库来pod install / pod update
远程办公最怕的就是库版本发生变化,最近一拉最新代码经常就要pod install / pod repo update,每次都会卡死总要有各种问题,解决的方法大概就是github源就用4G,Google源就用代理+wifi,但是仍旧有的时候下不下来,于是我就在这里说一下如何直接从github下载然后让pod过去。
首先,如果是卡在了pod repo update,也就是从远端拿这些库最新的地址tag之类的,这个时候可以让能够过这一步的同事把/Users/用户名/.cocoapods/repos/trunk/Specs
整个文件夹压缩打包发给你,直接替换就好,这样就可以成功的过update那关啦。
如果木有同事装好,可以直接从git上下载最新的:
https://github.com/CocoaPods/Specs
Specs里面存了哪些东西呢?我们以一会儿想要实验的库libwebp为例吧:
libwebp的各个版本
其实里面就是各个版本有个json以及json tag文件,我们打开json看一下:
{
"name": "libwebp",
"version": "1.1.0",
"summary": "Library to encode and decode images in WebP format.",
"homepage": "https://developers.google.com/speed/webp/",
"authors": "Google Inc.",
"license": {
"type": "BSD",
"file": "COPYING"
},
"source": {
"git": "https://chromium.googlesource.com/webm/libwebp",
"tag": "v1.1.0"
},
"compiler_flags": "-D_THREAD_SAFE",
"requires_arc": false,
"platforms": {
"osx": "10.8",
"ios": "6.0",
"tvos": "9.0",
"watchos": "2.0"
},
"pod_target_xcconfig": {
"USER_HEADER_SEARCH_PATHS": "$(inherited) ${PODS_ROOT}/libwebp/ ${PODS_TARGET_SRCROOT}/"
},
"preserve_paths": "src",
"default_subspecs": [
"webp",
"demux",
"mux"
],
"prepare_command": "sed -i.bak 's/<inttypes.h>/<stdint.h>/g' './src/webp/types.h'",
"subspecs": [
{
"name": "webp",
"source_files": [
"src/webp/decode.h",
"src/webp/encode.h",
"src/webp/types.h",
"src/webp/mux_types.h",
"src/webp/format_constants.h",
"src/utils/*.{h,c}",
"src/dsp/*.{h,c}",
"src/dec/*.{h,c}",
"src/enc/*.{h,c}"
],
"public_header_files": [
"src/webp/decode.h",
"src/webp/encode.h",
"src/webp/types.h",
"src/webp/mux_types.h",
"src/webp/format_constants.h"
]
},
{
"name": "demux",
"dependencies": {
"libwebp/webp": [
]
},
"source_files": [
"src/demux/*.{h,c}",
"src/webp/demux.h"
],
"public_header_files": "src/webp/demux.h"
},
{
"name": "mux",
"dependencies": {
"libwebp/demux": [
]
},
"source_files": [
"src/mux/*.{h,c}",
"src/webp/mux.h"
],
"public_header_files": "src/webp/mux.h"
}
]
}
然后我们就可以去source的git下载相应tag的库啦
"source": {
"git": "https://chromium.googlesource.com/webm/libwebp",
"tag": "v1.1.0"
},
git库
下载tar.gz或者zip都OK,然后解压放到我们project的本地pod里面:
工程里面的pod目录
注意哦你放进去的就是你从库仓库下载下来的文件,可能和你看到本地这个库的内容不一样,没关系的,你放进去再pod install过了以后就一样啦。
现在我们实现了下载需要的版本,并放入本地缓存,之后就可以来看如何pod install啦。在pod install的过程中,其实它就是比较了Podfile.lock
文件和本地缓存文件版本差异,如果有区别就去远端下载,所以现在我们需要让本地缓存的库和podfile.lock
里面一致,当然有一种方法是你把podfile.lock
里面的库版本降低到你本地的版本,这样你也不用从远端下载就可以成功pod install啦,只是这样的话你需要经常stash着这个变化。
那么本地缓存的版本由什么管理呢?其实就是项目/Pods/Manifest.lock
文件,你打开会发现和Podfile.lock
文件非常一致,只要把自己替换的库例如libwebp相关的内容都从Podfile.lock
拷到项目/Pods/Manifest.lock
即可,如果Podfile.lock
里面的版本不是最新的,你就手动改一下版本号以及spec checksum即可。
可参考:https://www.jianshu.com/p/06f507d2987d
例如podfile.lock里需要改成酱紫,manifest.lock必须相关的和podfile.lock保持一致,包括checksum的编号:
- libwebp (1.1.0):
- libwebp/demux (= 1.1.0)
- libwebp/mux (= 1.1.0)
- libwebp/webp (= 1.1.0)
- libwebp/demux (1.1.0):
- libwebp/webp
- libwebp/mux (1.1.0):
- libwebp/demux
- libwebp/webp (1.1.0)
SPEC CHECKSUMS:
……
KVOController: d72ace34afea42468329623b3379ab3cd1d286b6
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
那么CHECKSUMS的编号是如何生成的呢?我最开始以为是远程仓库的commit号,后来查了一下看到上面的refer文章才知道是这么生成的:
pod ipc spec /Users/用户名/.cocoapods/repos/trunk/Specs/1/9/2/libwebp/1.1.0/libwebp.podspec.json | openssl sha1
946cb3063cea9236285f7e9a8505d806d30e07f3
然后你再pod install就可以成功了吼~ (P.S. 想要知道自己卡在哪里,加上--verbose就可以啦)
3. podspec文件如果引入了其他模块引入的库也要声明
如果很多人做好多个模块,可能我们自己的模块依赖了其他组引入的某个库,那么如果我们也需要在自己模块的podspec里面声明依赖了这个库哦,不能直接用了人家引入的不声明,因为如果他们团队之后不用了,那么我们也就没有啦~
4. app在后台时keyboard监听也会被触发
这个问题是我昨天晚上QA小姐姐发现的,就是如果我们注册了observer监听UIKeyboardDidHideNotification
以及UIKeyboardWillShowNotification
,那么即使我们的app在后台,当我们在其他app例如微信里面调用了键盘,自己app里面监听keyboard的方法仍旧会被触发,这样在两个app来回切换并且有微信键盘的时候就看着很奇怪。
于是我Google了一下 (https://stackoverflow.com/questions/34409566/keyboardwillshow-gets-called-for-other-apps-keyboards),解决方案就是在监听触发的handler函数里面最好先判断自己的app是不是active的,如果不是就return不去处理这个监听:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowNotificationHandler) name:UIKeyboardWillShowNotification object:nil];
- (void) keyboardWillShowNotificationHandler {
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
return;
}
……
}
5. if define防止头文件重复引入
我们创建一个.h文件会自动生成下面的代码:
#ifndef HeaderName_h
#define HeaderName_h
// 这里面通常写各种宏定义、其他头文件的包含
#endif
这样子其实为了防止该头文件被重复引用~
被重复引用是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。
比如:存在a.h文件#include "c.h",而b.cpp文件同时#include "a.h" 和#include "c.h",此时就会造成c.h被b.cpp重复引用。
头文件被重复引用引起的后果:有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些。但是对于大工程而言,编译效率低下那将是一件多么痛苦的事情。
而有些头文件重复包含,则会引起错误,比如:在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的),这种头文件重复包含会引起全局变量的重复定义。
在OC中
#import
保证了每个文件只会被import一次,即使我们没有加上面的ifdef也没有问题,但最好要加上啊,毕竟你不知道别人怎么import你的头文件,下面来尝试一下~
可以编译过的版本:
// Header1.h
static const NSString * header1Name = @"header1";
// Header2.h
#import "Header1.h"
static const NSString * header2Name = @"header2";
// vc
#import "Header1.h"
#import "Header2.h"
不可以编译过会报错redefine的版本:
// Header1.h
static const NSString * header1Name = @"header1";
// Header2.h
#include "Header1.h"
static const NSString * header2Name = @"header2";
// vc
#include "Header1.h"
#include "Header2.h"
改一下让它可以编译过:
// Header1.h
#ifndef Header1_h
#define Header1_h
static const NSString * header1Name = @"header1";
#endif /* Header1_h */
// Header2.h
#include "Header1.h"
static const NSString * header2Name = @"header2";
// vc
#include "Header1.h"
#include "Header2.h"
6. 打包release debug证书区别
这个真的不太熟,是偶尔看到的:https://blog.csdn.net/weixin_34221112/article/details/92325150
所以Release版本如果"Debug executable"就可以断点,但无法使用Assert、Trace等功能。因此,Release版本下调试操作实际并没有任何意义,调试结果没有任何参考价值。
development和distribution证书:development证书开发时使用,distribution证书发布时使用。它们跟Xcode的debug和release没有一毛钱关系。development证书允许开发者将app直接"Run"进真机,而distribution证书则不可以。这就是它们直接的主要区别。
7. 淘口令
各种复制口令到app然后会打开一个页面,最早感觉就是淘宝这么做的,它的是咋实现的呢?first step是从你的剪贴板拿到你复制的东西,second step我猜是把那串神奇的字符串发给后端,我们只要pop up一个webview,内容后端会来填充页面。
所以我们先来看一下从剪贴板拿你的magic string:
// 程序进入前台
-(void)applicationWillEnterForeground:(UIApplication*)application {
UIPasteboard* pasteboard= [UIPasteboard generalPasteboard];
NSLog(@"pasteboard: %@", pasteboard.string);
}
我拿拼多多的app来fake了一下,然后复制了一个口令打开拼多多看到的页面层级是这样的:(神奇的发现陌陌的反fake比拼多多做的好。。。
口令拼多多页面层级
所以弹出的页面是原生的不是web哈~
但是如果你无网复制粘贴进拼多多就没有反应,所以虽然不是直接pop一个web让后端做,但应该是从剪贴板拿到string以后发给了后端,后端会告诉前端弹哪种view,并把需要的填充数据返还。
8. 内存泄漏的一些情况
-
对象循环引用
@class ,Strong,weak -
block循环引用
__weak typeof(self) weakself = self; -
NSNotification的观察者忘记移除
[[NSNotificationCenter defaultCenter] removeObserver:self]; -
delegate循环引用问题
@property (nonatomic, weak) id delegate; -
NSTimer循环引用
使用GCD -
非OC对象内存处理
CGImageRef类型变量非OC对象,其需要手动执行释放操作CGImageRelease(ref),否则会造成大量的内存泄漏导致程序崩溃。其他的对于CoreFoundation框架下的某些对象或变量需要手动释放、C语言代码中的malloc等需要对应free等都需要注意 -
使用过多的UIWebView
参考:https://www.jianshu.com/p/9866294b39c6
改为WKWebView -
大次数循环内存暴涨问题
for (int i = 0; i < 100000; i++) {
NSString *string = @“Abc”;
string = [string lowercaseString];
string = [string stringByAppendingString:@“xyz”];
NSLog(@"%@", string);
}
改:
for (int i = 0; i < 100000; i++) {
@autoreleasepool {
NSString *string = @“Abc”;
string = [string lowercaseString];
string = [string stringByAppendingString:@“xyz”];
NSLog(@"%@", string);
}
}
-
加载大图片或者多个图片
[UIImage imageNamed:@""]
,次方法使用了系统缓存来缓存图像,会长时间占用内存,最好使用imageWithContentsOfFile
方法 -
ANF的AFHTTPSessionManager
参考:https://www.jianshu.com/p/922b043b244e -
地图类
9. presentVC iOS13适配
参考:https://www.cnblogs.com/guoshaobin/p/11167191.html
其实就是presentViewController现在默认是一个叠在现有页面的感觉,iOS13改了好多0.0,如果想让他恢复全屏就酱紫:
ViewController *vc = [[ViewController alloc] init];
vc.title = @"presentVC";
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
nav.modalPresentationStyle = UIModalPresentationFullScreen;
[self.window.rootViewController presentViewController:nav animated:YES completion:nil];
Refer: https://blog.csdn.net/heqiang2015/article/details/84575047
网友评论