http://www.alonemonkey.com/2016/05/15/fbretaincycledetector-analyse/ FBRetainCycleDetector源码分析
https://blog.csdn.net/java2013liu/article/details/52242969 精准 iOS 内存泄露检测工具
MLeaksFinder 一开始从 UIViewController 入手。我们知道,当一个 UIViewController 被 pop 或 dismiss 后,该 UIViewController 包括它的 view,view 的 subviews 等等将很快被释放(除非你把它设计成单例,或者持有它的强引用,但一般很少这样做)。于是,我们只需在一个 ViewController 被 pop 或 dismiss 一小段时间后,看看该 UIViewController,它的 view,view 的 subviews 等等是否还存在。
具体的方法是,为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。
http://www.cocoachina.com/ios/20160419/15954.html 分享:在iOS上自动检测内存泄露
总结下:__bridge_retained是在桥接后让Core Foundation对象变量持有对象,即让对象引用计数+1,__bridge_transfer桥接后让Core Foundation对象变量释放所持有的对象,即让对象引用计数-1。而__bridge除了桥接其他什么操作都不做。在ARC环境下,下面的操作是对等的:
cfObject = (__bridge CFMutableArrayRef)obj + CFRetain(cfObject) 与 cfObject = (__bridge_retained CFMutableArrayRef)obj
obj = (__bridge id)cfObject + CFRelease(cfObject) 与 obj = (__bridge_transfer id)cfObject
链接:https://www.jianshu.com/p/df74a54b6b75
__bridge用以将 CF 对象转换为 OC 对象,或者 OC 对象转换为 CF 对象,但是不会对对象的 Retain Count、所有权产生任何影响。
__bridge 可以理解为:只是为了让编译通过,其他毫无影响。__bridge_transfer等价于
CFBridgingRelease()
,将CF
对象转换为OC
对象,并将所有权转移给ARC
。
所有权转移给ARC
的本质含义是:最终CF
对象会被ARC
生成的代码进行Retain Count -1
操作或者释放,所以不需要手动调用CFRelease
。__bridge_retained等价于
CFBridgingRetain()
,用以将OC
对象转换为CF
对象,并且Retain Count
立即+1
。和__bridge_transfer
转移所有权的差别,__bridge_retained
不存在转移所有权,而应当是赋予CF
所有权
链接:https://www.jianshu.com/p/206fcb84b48f
https://www.jianshu.com/p/c2e6c18293cb
fishHook其实 就是:
通过 Load commands 中的 linkedit -> Symbol table ,string table -> _DATA的 Lazy Symbol Pointers 与 Non-Lazy Symbol Pointers 。修改懒加载表(Lazy Symbol Pointers)、非懒加载表(Non-Lazy Symbol Pointers)中的符号地址的指向,从而达到hook的目的。
https://www.jianshu.com/p/66bf2294a56b alloc & dealloc 内部实现流程图
https://www.jianshu.com/p/7a2480071615
https://ddrccw.github.io/2017/12/30/2017-12-30-reverse-xcode-with-lldb-and-hopper-disassembler/
小试Xcode逆向:app内存监控原理初探
https://www.valiantcat.cn/index.php/2018/10/06/64.html iOS Memory Deep Dive
https://blog.csdn.net/kwanson/article/details/79954530?utm_source=blogxgwz3 内存泄漏检测原理
https://www.jianshu.com/p/deab6550553a 先弄清楚这里的学问,再来谈 iOS 内存管理与优化
https://www.jianshu.com/p/f95b9bfda4a0 先弄清楚这里的学问,再来谈 iOS 内存管理与优化
https://www.jianshu.com/p/efb4e8ba2a7e [iOS] 线上内存泄漏检测方案与结果
-33 KVO的实现原理
KVO
是通过isa-swizzling
技术实现的(这句话是整个KVO
实现的重点)。在运行时根据原类创建一个中间类,这个中间类是原类的子类,并动态修改当前对象的isa
指向中间类。并且将class
方法重写,返回原类的Class
。所以苹果建议在开发中不应该依赖isa
指针,而是通过class
实例方法来获取对象类型。
https://www.jianshu.com/p/badf5cac0130
https://blog.csdn.net/bravegogo/article/details/51488635 *
https://blog.csdn.net/aaidong/article/details/46998799
+ new
{
id newObject = (*_alloc)((Class)self, 0);
Class metaClass = self->isa;
if (class_getVersion(metaClass) > 1)
return [newObject init];
else
return newObject;
}
//而 alloc/init 像这样:
+ alloc
{
return (*_zoneAlloc)((Class)self, 0, malloc_default_zone());
}
- init
{
return self;
}
通过源码中我们发现,[className new]基本等同于[[className alloc] init];
区别只在于alloc分配内存的时候使用了zone.
这个zone是个什么东东呢?
它是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度;
https://www.jianshu.com/p/7f68a3d5b07d
A 、dispatch_queue_set_specific() & dispatch_get_specific();
B、dispatch_queue_get_label()
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL),dispatch_queue_get_label(dispatch_get_main_queue())) == 0)
{
// do something in main thread
} else {
// do something in other thread
}
https://www.jianshu.com/p/6679f4fa84cb
https://www.jianshu.com/p/e59c4a3c39a1
http://yulingtianxia.com/blog/2016/10/30/Optimizing-App-Startup-Time/
优化 App 的启动时间 (最好)
https://www.jianshu.com/p/6ac9600a15fb
1)Mach异常方式
2)Unix信号方式
1signal(SIGSEGV,signalHandler);
3) 应用层 try catch 异常
UncaughtExceptionHandler()
https://blog.csdn.net/zhangbijun1230/article/details/81057319
https://www.jianshu.com/p/b668b2520dbb mmap和direct io和write和fwrite区别
https://www.jianshu.com/p/4624cb9e3321 mmap与直接IO(read、write)的效率比较
https://www.jianshu.com/p/32527dc70bee 文件 Cache 相关数据结构
https://www.jianshu.com/p/133fd6f20563 KSCrash中signal捕获异常的原理
https://www.jianshu.com/p/133fd6f20563 iOS Mach异常和signal信号
https://www.jianshu.com/p/953f0961157a iOS崩溃堆栈信息的符号化解析
https://www.jianshu.com/p/809ae3022cfd UncaughtExceptionHandler 捕捉异常
https://blog.csdn.net/bravegogo/article/details/81168444
signal信号:
signal是一种软中断信号,提供异步事件处理机制。
signal是进程间相互传递信息的一种粗糙方法,使用场景: 进程终止相关;
终端交互;
编程错误或硬件错误相关,系统遇到不可恢复的错误时触发崩溃机制让程序退出,比如:除0、内存写入错误等。
这里我们主要考虑系统遇到不可恢复的错误时即Crash时,信号相关的应用。signal信号处理是UNIX操作系统机制,可以基于signal来捕获Android Native Crash。
signal注册和处理signal():
注册signal handler;
调用成功时,会移除signo信号当前的操作,以handler指定的新信号处理程序替代;
信号处理函数返回void,因为没有地方给该函数返回。注册自定义信号处理函数,构造Crash后,发出信号并执行自定义信号处理逻辑。
Xcode Debug运行时,添加断点,在Crash触发前,执行pro hand -p true -s false SIGABRT命令。
pthread_kill 向线程发送 signal
https://blog.csdn.net/bravegogo/article/details/86598837
https://www.jianshu.com/p/c16b8e101cfd fishhook代码分析
https://blog.csdn.net/baidu_32977561/article/details/79183282 Mach-O文件格式
https://www.jianshu.com/p/857855cda602 iOS 系统的延迟绑定机制
1 解析mach-o文件,确定该文件是一个有效的Mach-O文件,所以内核为程序(fork)创建一个进程并开始程序执行过程(execve)
2加载命令。内核配合动态连接器,进入加载指令指定分配的地址空间。段的虚拟内存保护标志也按指示添加上(例如__TEXT是只读)
3 根据动态库加载信息,把__DATA段section __nl_symbol_ptr区占位符换为调用dylib的dyld_stub_binder函数的汇编指令。
4 根据LC_MAIN的entry point调用指定entry offset偏移地址执行entry offset相关汇编指令。
5 第一次运行到动态库函数时,进行一次懒加载动态绑定,并且动态链接器自动修改_la_symbol_ptr区的地址,指向动态库对应符号的地址。
6 第二次运行到动态库函数时,直接jmp到指定的符号地址
链接:https://www.jianshu.com/p/e839543675b5
https://www.jianshu.com/p/aec6e9afd6f2
http://hawk0620.github.io/blog/2018/03/22/study-mach-o-file/#如何用%20MachO%20文件关联类的方法名 (这个比较好)
链接:https://www.jianshu.com/p/e839543675b5
`-ObjC` : 链接器会把静态库中所有的类和分类都加载到最后的可执行文件中
`-force_load` : 需要指定要进行全部加载的库文件的路径,避免引用多个第三方库时会出现类名重叠的冲突
`-all_load` : 让链接器把所有找到的目标文件都加载到可执行文件中,不建议使用
`-dead_strip` : 删除多余的库符号,不建议使用
1.首先列出来的是目标文件列表(中括号内为文件编号):
2.接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)
3.接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间
4、已废弃&多余重复的字段
1.首先列出来的是目标文件列表(中括号内为文件编号):
# Path: /Users/mxr/Library/Developer/Xcode/DerivedData/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/Build/Products/Debug-iphoneos/4dBookCity.app/4dBookCity
# Arch: arm64
# Object files:
[ 0] linker synthesized
[ 1] /Users/mxr/Library/Developer/Xcode/DerivedData/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/Build/Intermediates.noindex/huashida_home.build/Debug-iphoneos/4dBookCity.build/Objects-normal/arm64/Bulk_Arrays_12.o
[ 2] /Users/mxr/Library/Developer/Xcode/DerivedData/huashida_home.xcodeproj-fvbzvmahuzlfgqbzehannctanrbl/Build/Intermediates.noindex/huashida_home.build/Debug-iphoneos/4dBookCity.build/Objects-normal/arm64/MXRSnapLearnInviteView.o
2.接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)
# Sections:
# Address Size Segment Section
0x100005B00 0x0304A29C __TEXT __text
0x10304FD9C 0x00004BC0 __TEXT __stubs
0x10305495C 0x000044E8 __TEXT __stub_helper
0x103058E50 0x0021563C __TEXT __cstring
0x10326E48C 0x000AD400 __TEXT __objc_methname
0x10331B88C 0x0000E6BA __TEXT __objc_classname
0x103329F46 0x000166E3 __TEXT __objc_methtype
0x103340640 0x002A0B60 __TEXT __const
0x1035E11A0 0x001346D4 __TEXT __gcc_except_tab
0x103715874 0x00008C78 __TEXT __ustring
0x10371E4EC 0x0004D80C __TEXT __unwind_info
0x10376BCF8 0x00000300 __TEXT __eh_frame
0x10376C000 0x000015D8 __DATA __got
0x10376D5D8 0x00003280 __DATA __la_symbol_ptr
0x103770858 0x00001838 __DATA __mod_init_func
0x103772090 0x000FF7F8 __DATA __const
0x103871888 0x0006F9C0 __DATA __cfstring
0x1038E1248 0x00004778 __DATA __objc_classlist
0x1038E59C0 0x00000290 __DATA __objc_nlclslist
0x1038E5C50 0x00000708 __DATA __objc_catlist
0x1038E6358 0x00000038 __DATA __objc_nlcatlist
0x1038E6390 0x00000910 __DATA __objc_protolist
0x1038E6CA0 0x00000008 __DATA __objc_imageinfo
0x1038E6CA8 0x00206C58 __DATA __objc_const
0x103AED900 0x00027F28 __DATA __objc_selrefs
0x103B15828 0x000000C0 __DATA __objc_protorefs
0x103B158E8 0x000041B8 __DATA __objc_classrefs
0x103B19AA0 0x000030C0 __DATA __objc_superrefs
0x103B1CB60 0x0000BB54 __DATA __objc_ivar
0x103B286B8 0x0002CB00 __DATA __objc_data
0x103B551C0 0x01D52748 __DATA __data
0x1058A7920 0x00714878 __DATA __bss
0x105FBD000 0x0012B978 __DATA __common
首列是数据在文件的偏移位置,第二列是这一段占用大小,第三列是段类型,代码段和数据段,第四列是段名称。
每一行的数据都紧跟在上一行后面,如第二行__stubs的地址0x10304FD9C就是第一行__text的地址0x100005B00加上大小0x0304A29C,整个可执行文件大致数据分布就是这样。
3.接着就是按上表顺序,列出具体的按每个文件列出每个对应字段的位置和占用空间
# Symbols:
# Address Size File Name
0x100005B00 0x000000EC [ 2] -[MXRSnapLearnInviteView drawRect:]
0x100005BEC 0x0000024C [ 2] -[MXRSnapLearnInviteView generatorlogoImageQRCode]
0x100005E38 0x0000005C [ 2] _CGRectMake
0x100005E94 0x00000034 [ 2] -[MXRSnapLearnInviteView inviteCode]
0x100005EC8 0x00000050 [ 2] -[MXRSnapLearnInviteView setInviteCode:]
0x100005F18 0x0000003C [ 2] -[MXRSnapLearnInviteView .cxx_destruct]
0x100005F54 0x000001D8 [ 3] -[MXRPKHomeCellViewModel initWithModel:]
0x10000612C 0x0000016C [ 3] -[MXRPKHomeCellViewModel encodeWithCoder:]
0x100006298 0x00000268 [ 3] -[MXRPKHomeCellViewModel initWithCoder:]
0x100006500 0x00000040 [ 3] -[MXRPKHomeCellViewModel desc]
0x100006540 0x00000044 [ 3] -[MXRPKHomeCellViewModel setDesc:]
0x100006584 0x00000040 [ 3] -[MXRPKHomeCellViewModel name]
0x1000065C4 0x00000044 [ 3] -[MXRPKHomeCellViewModel setName:]
0x100006608 0x00000040 [ 3] -[MXRPKHomeCellViewModel pic]
0x100006648 0x00000044 [ 3] -[MXRPKHomeCellViewModel setPic:]
0x10000668C 0x00000040 [ 3] -[MXRPKHomeCellViewModel classifyId]
0x1000066CC 0x00000044 [ 3] -[MXRPKHomeCellViewModel setClassifyId:]
0x100006710 0x000000B8 [ 3] -[MXRPKHomeCellViewModel .cxx_destruct]
...
0x1060C82D0 0x000000C0 [3391] _jerrenv
0x1060C8390 0x000204E0 [4793] _GC_arrays
0x1060E8870 0x00000100 [4793] _GC_bm_table
0x1060E8970 0x00000008 [4793] _GC_noop_sink
同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。
4、已废弃&多余重复的字段
# Dead Stripped Symbols:
# Size File Name
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
...
<
<
<
<
<
<
<
<
<
<
<
链接:https://www.jianshu.com/p/4bd6d1315104
Objective-C 中的 Hook 又被称作 Method Swizzling,这是动态语言大都具有的特性。在 Objective-C 中经常会把 Hook 的逻辑写在 + load 方法中,这是利用它调用时机较提前等性质。
有时候需要 Hook 子类和父类的同一个方法,但是它们的 + load 方法调用顺序不同。一个常见的顺序可能是:父类->子类->子类类别->父类类别。所以 Hook 的顺序并不能保证,就不能保证 Hook 后方法调用的顺序是对的。而且使用不同方法 Method Swizzling 也会带来不同的结果。本文将会对这些情况下的 Hook 结果进行分析和总结。
Method Swizzling的使用问题。介绍如下:
1、不是线程安全的(Method swizzling is not atomic)
通常在 load方法中交换方法实现,如果在其他时机交换方法实现,需要考虑线程安全的问题。
RSSwizzle利用了自旋锁OSSpinLock保证线程安全。可以在任意时机交换方法实现。
2、 改变了代码本来的行为(Changes behavior of un-owned code)
这正是Swizzle的目标。但是在Swizzle方法中,我们保留*调用原始实现的好习惯,能避免绝大多数问题。我们利用Swizzle,一般是为了在原始实现基础上,添加某些自己的业务需求,并不想刻意去破坏原有实现。
RSSwizzle提供调用原来实现的宏RSSWCallOriginal,很方便。
3、潜在的命名冲突(Possible naming conflicts)#####
通常在替换的方法名前加前缀,可以很大程度上避免命名冲突冲突问题。
RSSwizzle在自定义的swizzle的静态方法完成方法替换,完全避免了命名冲突问题。
4、改变方法的参数(Swizzling changes the method's arguments)
参数 _cmd 被篡改,正常调用Swizzle 的方法有问题。
//调用方法
[self qs_setFrame:frame];
//发消息
objc_msgSend(self, @selector(qs_setFrame:), frame);
说明:在运行时,寻找qs_setFrame:的方法实现, _cmd参数虽然是 qs_setFrame: ,但是实际上找到的方法实现是原始的 setFrame: 实现。
RSSwizzle的自定义的swizzle的静态方法解决这个问题。
5、继承问题(The order of swizzles matters)
多个有继承关系的类的对象Swizzle时,先从父对象开始。 这样才能保证子类方法拿到父类中的被Swizzle的实现。
在load中Swizzle不用担心这种问题,因为load类方法会默认从父类开始调用。
6、难以理解 (Difficult to understand)
主要表现在调用原始实现,看起来像递归,有点懵。
RSSwizzle提供的宏RSSWCallOriginal让调用原始实现更容易,代码阅读性更强。
7、难以调试(Difficult to debug)
Debug时候打印出的backtrace(回溯),其中掺杂着被swizzle的方法名,看起来比较乱,所以命名清晰很重要;
RSSwizzle打印出来的命名很清晰,此外Swizzle了什么,最好有文档记录。
参考:
https://www.jianshu.com/p/5f2e38a40aa4 iOS 札记1:Method Swizzling小记
iOS的hook方案: Method Swizzling
http://yulingtianxia.com/blog/2017/04/17/Objective-C-Method-Swizzling/
Objective-C Method Swizzling
https://www.jianshu.com/p/0a26f1cd0b34
iOS界的毒瘤-MethodSwizzling
Method swizzling的正确姿势
https://www.jianshu.com/p/1f83481a05e0
https://www.jianshu.com/p/a6b675f4d073
12- Runtime基础使用场景-拦截替换方法(class_addMethod ,class_replaceMethod和method_exchangeImplementations)
Swizzle应用性研究
https://www.jianshu.com/p/4807e9745397
携程网络优化 https://www.infoq.cn/article/how-ctrip-improves-app-networking-performance
Perfect smooth scrolling in UITableViews
http://southpeak.github.io/2015/12/20/perfect-smooth-scrolling-in-uitableviews/
iOS UIWebView小整理(三)(利用NSURLProtocol加载本地js、css资源) https://www.jianshu.com/p/731f49e74742
iOS开发 最新的NSURLProtocol之webView的离线缓存,缓存webView,离线加载 https://blog.csdn.net/horisea/article/details/53815596
https://segmentfault.com/a/1190000006733978
弱网下移动端网络连接处理策略
https://github.com/WeMobileDev/article/blob/master/微信客户端怎样应对弱网络.pdf
https://www.cnblogs.com/jb2011/p/8473230.html
现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障
https://yunxin.163.com/blog/52im-2/
史上最全移动弱网络优化方法总结
https://mp.weixin.qq.com/s/PnICVDyVuMSyvpvTrdEpSQ
微信终端跨平台组件 mars 系列(二) - 信令传输超时设计
https://www.meiwen.com.cn/subject/ayopzttx.html
重传,ARQ和HARQ
https://wenku.baidu.com/view/2c35ba05ba68a98271fe910ef12d2af90242a888.html
mars 系列-网络优化的设计与思考
https://www.cnblogs.com/wuchanming/p/4422779.html
https://blog.csdn.net/jtracydy/article/details/52366461
TCP的拥塞控制
https://blog.csdn.net/xiaofei0859/article/details/51051999
TCP/IP协议:最大报文段长度(MSS)是如何确定的(1)
http://www.52im.net/thread-697-1-1.html
如何大幅压缩移动网络下APP的流量消耗
https://www.jianshu.com/p/a470ab485e39
iOS网络深度优化总结
http://www.52im.net/thread-283-1-1.html
理论联系实际:一套典型的IM通信协议设计详解
GMTC—《微信客户端怎样应对弱网络》
https://www.jianshu.com/p/da2a0030962a
网络优化实践探索文章
https://blog.csdn.net/tq08g2z/article/details/77311721
https://www.jianshu.com/p/0042d8eb67c0 WebView与JS的几种交互
https://www.jianshu.com/p/3f5dc8042dfc 深入浅出 JavaScriptCore
https://www.jianshu.com/p/6ec9571a0105 WKWebView与JS
1、使用 JSContext ,首先获取当前webview JS上下文
1 |
|
2.JS调用原生方法
__weak typeof(self) weakSelf = self;
context[@"nativeTitle"] = ^(NSString *navTitle) {
LRHLog(@"nativeTitle :%@",navTitle);
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.webViewController setNaviTitle:navTitle];
//统计web停留时间
[strongSelf mobClickWebLoadingTimeWith:navTitle];
});
};
block中用来接收js的传值,“nativeTitle” 为定义好的方法名
这里要注意两个问题:第一、block中注意jsContext对象的循环引用; 第二、block中是在子线程,因此执行UI操作时,需要回到主线程。
3.原生调用JS
1 |
|
“jsHandleNativeResult” 为定义好的方法名
把js需要的参数进行拼接,通过定义好的方法传给js。
https://www.cnblogs.com/Zhang-wj/p/5907534.html
https://www.jianshu.com/p/d08c369b53e1
https://www.jianshu.com/p/73f1d8bc38cb
https://www.cnblogs.com/yusenwu/p/4579167.html
https://www.jianshu.com/p/6a5d273f3223
http://www.cnblogs.com/jiangzhaowei/p/8261174.html
https://segmentfault.com/a/1190000010838127
https://blog.csdn.net/bravegogo/article/details/53517610
https://www.jianshu.com/p/f29b59f4c2b9 阿里数据iOS端启动速度优化的一些经验
https://mp.weixin.qq.com/s/tTgjTywGaTFbB9RGh4kVeQ 有货IOS编译优化
https://mp.weixin.qq.com/s/wBZFv_-l7MDtTdofIxS13A 今日头条iOS客户端启动速度优化
https://icetime17.github.io/2018/01/01/2018-01/APP启动优化的一次实践/ APP启动优化的一次实践
https://www.jianshu.com/p/4a3623e0c021?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation APP优化之--启动速度优化
https://www.jianshu.com/p/0858878e331f 优化 App 的启动时间实践 iOS
一、对于main()调用之前的耗时我们可以优化的点有:
- 减少不必要的framework,因为动态链接比较耗时
- check framework应当设为optional和required,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查
- 合并或者删减一些OC类,关于清理项目中没用到的类:{1.删减一些无用的静态变量 2.删减没有被调用到或者已经废弃的方法 3.将不必须在+load方法中做的事情延迟到+initialize中 4.尽量不要用C++虚函数(创建虚函数表有开销)}
二、main函数调用之后的加载时间
在main()被调用之后,App的主要工作就是初始化必要的服务,显示首页内容等。而我们的优化也是围绕如何能够快速展现首页来开展。 App通常在AppDelegate类中的- (BOOL)Application:(UIApplication )Application didFinishLaunchingWithOptions:(NSDictionary )launchOptions方法中创建首页需要展示的view,然后在当前runloop的末尾,主动调用CA::Transaction::commit完成视图的渲染。
而视图的渲染主要涉及三个阶段:
- 准备阶段 这里主要是图片的解码
- 布局阶段 首页所有UIView的- (void)layoutSubViews()运行
- 绘制阶段 首页所有UIView的- (void)drawRect:(CGRect)rect运行
再加上启动之后必要服务的启动、必要数据的创建和读取,这些就是我们可以尝试优化的地方因此,对于main()函数调用之后我们可以优化的点有:
- 不使用xib,直接视用代码加载首页视图
- NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时,这个影响需要评估,如果耗时很大的话需要拆分(需考虑老版本覆盖安装兼容问题)
- 每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log
- 梳理应用启动时发送的所有网络请求,是否可以统一在异步线程请求
- 首页方法对于viewDidLoad以及viewWillAppear方法中尽量去尝试少做,晚做,不做。
-9 UITableView 的 estimatedRowHeight
https://blog.csdn.net/bravegogo/article/details/84108038
Class cls = NSClassFromString(@"WKBrowsingContextController"); SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:"); if ([(id)cls respondsToSelector:sel]) { // 把 http 和 https 请求交给 NSURLProtocol 处理 [(id)cls performSelector:sel withObject:@"http"]; [(id)cls performSelector:sel withObject:@"https"]; } // 这下 AwesomeURLProtocol 就可以用啦 [NSURLProtocol registerClass:[AwesomeURLProtocol class]];
https://www.jianshu.com/p/95d224c4d13e
堆排序 :https://www.cnblogs.com/chengxiao/p/6129630.html
归并: https://www.cnblogs.com/chengxiao/p/6194356.html
快排:https://www.cnblogs.com/chengxiao/p/6262208.html
简单排序:https://www.cnblogs.com/chengxiao/p/6103002.html
希尔:https://www.cnblogs.com/chengxiao/p/6104371.html
二叉树遍历 https://blog.csdn.net/ailunlee/article/details/80755357
二叉树遍历:是以 根节点为参照进行的。
深度与广度 遍历 :https://www.cnblogs.com/llguanli/p/7363657.html
https://www.cnblogs.com/xiaolovewei/p/7763867.html
广度优先遍历: 仅仅须要一个队列就可以。先在队列中增加根结点。之后对于随意一个结点来说。在其出队列的时候,訪问之。同一时候假设左孩子和右孩子有不为空的
深度优先遍历:事实上深度遍历就是上面的前序、中序和后序。可是为了保证与广度优先遍历相照顾,也写在这。代码也比較好理解,事实上就是前序遍历,
https://www.jianshu.com/p/03cd195d79d9
https://www.cnblogs.com/little-YTMM/p/5058354.html
A 、清除旧的数据,重建表
优点:简单
缺点:数据丢失
B、在已有表的基础上对表结构进行修改
优点:能够保留数据
缺点:规则比较繁琐,要建立一个数据库的字段配置文件,然后读取配置文件,执行SQL修改表结构、约束和主键等等,涉及到跨多个版本的数据库升级就变得繁琐并且麻烦了
C、创建临时表,把旧的数据拷贝到临时表,然后删除旧的数据表并且把临时表设置为数据表。
优点:能够保留数据,支持表结构的修改,约束、主键的变更,实现起来比较简单
缺点:实现的步骤比较多,需要进行数据的内容的迁移。
https://www.jianshu.com/p/744235bbf891
mach_absolute_time()可能用到的同学比较少,但这个概念非常重要。
前面提到我们需要找到一个均匀变化的属性值来描述时间,而在我们的iPhone上刚好有一个这样的值存在,就是CPU的时钟周期数(ticks)。这个tick的数值可以用来描述时间,而mach_absolute_time()返回的就是CPU已经运行的tick的数量。将这个tick数经过一定的转换就可以变成秒数,或者纳秒数,这样就和时间直接关联了。
不过这个tick数,在每次手机重启之后,会重新开始计数,而且iPhone锁屏进入休眠之后tick也会暂停计数。
mach_absolute_time()不会受系统时间影响,只受设备重启和休眠行为影响。
CACurrentMediaTime()可能接触到的同学会多一些,先看下官方定义:
/* Returns the current CoreAnimation absolute time. This is the result of
* calling mach_absolute_time () and converting the units to seconds. */CFTimeInterval CACurrentMediaTime (void)
CACurrentMediaTime()就是将上面mach_absolute_time()的CPU tick数转化成秒数的结果。以下代码:
double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);
返回的就是开机后设备一共运行了(设备休眠不统计在内)多少秒,另一个API也能返回相同的值:
NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);
CACurrentMediaTime()也不会受系统时间影响,只受设备重启和休眠行为影响。
https://www.jianshu.com/p/a00f34ed2c16
可以以三种方式开始事务:
begin deferred(延迟) :
默认就是这种模式,以这种模式开始的事务一开始不获取任何锁,是处于unlocked状态的。对数据库第一次读的时候,获取共享锁,对数据库第一次写的时候,获取保留锁。
begin immediate:事务开始的时候尝试获取保留锁,获取成功后别的连接就不能写了,但是可以读,commit的时候可能会返回SQLITE_BUSY,这意味着需要等待其他读操作完成。
begin exclusive:事务开始的时候尝试获取独占锁,这样其他连接无法进行读写。
SQLite
通过五种锁状态来完成事务
UNLOCKED
,无锁状态。数据库文件没有被加锁。SHARED
共享状态。数据库文件被加了共享锁。可以多线程执行读操作,但不能进行写操作。RESERVED
保留状态。数据库文件被加保留锁。表示数据库将要进行写操作。PENDING
未决状态。表示即将写入数据库,正在等待其他读线程释放SHARED
锁。一旦某个线程持有PENDING
锁,其他线程就不能获取SHARED
锁。这样一来,只要等所有读线程完成,释放SHARED
锁后,它就可以进入EXCLUSIVE
状态了。EXCLUSIVE
独占锁。表示它可以写入数据库了。进入这个状态后,其他任何线程都不能访问数据库文件。因此为了并发性,它的持有时间越短越好。
锁的转换(读 写)
https://blog.csdn.net/hello_hwc/article/details/79647470
死锁
两个连接A和B:
B开始事务,尝试写入,这时候获取保留锁,开始操作Pager。此时A获取共享锁,A尝试写入,尝试转换到保留锁失败,这时候A处于共享状态,等待升级为可写入状态。B commit的时候,因为共享锁的存在,所以无法进入独占锁,导致死锁。
一个语句在遍历表的时候,同时进行写入表,也可能会造成死锁。
原文:https://blog.csdn.net/hello_hwc/article/details/79647470
经典
http://zhoulingyu.com/2017/02/08/iOS进阶——iOS-Memory-Block/
Block源码分析系列(一)— 2分钟明白Block究竟是什么?https://blog.csdn.net/Deft_MKJing/article/details/53143076
Block源码分析系列(二)— 局部变量的截获以及__block的作用和理解 https://blog.csdn.net/deft_mkjing/article/details/53149629
Block源码分析系列(三)— 隐藏的三种Block本体以及为什么要使用copy修饰符 https://blog.csdn.net/deft_mkjing/article/details/53165799
Block源码分析系列(四)— __block和Block的循环引用 https://blog.csdn.net/Deft_MKJing/article/details/53165799
int main()
{
void (^block)(void) = ^(void)
{printf("aaa\n");};
block();
return 0;
}
(1) __block 修饰符
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
block 的 body
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block 内部 方法调用
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
printf("aaa\n");}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main()
{
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
一个SQLite数据库文件有5种锁的状态:
UNLOCKED:表示数据库此时并未被读写。
SHARED:表示数据库可以被读取。SHARED锁可以同时被多个线程拥有。一旦某个线程持有SHARED锁,就没有任何线程可以进行写操作。
当线程拥有SHARED锁,说明这个线程拥有对数据库的读的操作,对数据库进行写操作的线程能进行互斥操作
RESERVED:表示准备写入数据库。RESERVED锁最多只能被一个线程拥有,此后它可以进入PENDING状态。
PENDING:表示即将写入数据库,正在等待其他读线程释放SHARED锁。一旦某个线程持有PENDING锁,其他线程就不能获取SHARED锁。这样一来,只要等所有读线程完成,释放SHARED锁后,它就可以进入EXCLUSIVE状态了。
EXCLUSIVE:表示它可以写入数据库了。进入这个状态后,其他任何线程都不能访问数据库文件。因此为了并发性,它的持有时间越短越好。
一个线程只有在拥有低级别的锁的时候,才能获取更高一级的锁。SQLite就是靠这5种类型的锁,巧妙地实现了读写线程的互斥。同时也可看出,写操作必须进入EXCLUSIVE状态,此时并发数被降到1,这也是SQLite被认为并发插入性能不好的原因。另外,read-uncommitted和WAL模式会影响这个锁的机制。在这2种模式下,读线程不会被写线程阻塞,即使写线程持有PENDING或EXCLUSIVE锁。
链接:https://www.jianshu.com/p/d1bc6ec56821
https://blog.csdn.net/qq_37940313/article/details/80403545
http://www.cocoachina.com/ios/20161110/18030.html