工欲善其事,必先利其器

-37 内存泄露监测

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上自动检测内存泄露

-36  __bridge_retained,__bridge,__bridge_transfer

总结下:__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 

 

- 35   fishhook 的原理 和动态库加载

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的目的。

-34  内存监控与分配

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     *

 

-32 OC 中new与alloc/init的区别

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是个什么东东呢?

        它是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时消耗很少的代价,提升了程序处理速度; 

-31 主线程队列判断

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

}

-30 源码剖析 Objc 消息派发流程

 https://www.jianshu.com/p/6679f4fa84cb

-29 组件化 与pods

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

-28 启动优化

http://yulingtianxia.com/blog/2016/10/30/Optimizing-App-Startup-Time/

优化 App 的启动时间  (最好)

 

-27 KSCrash中signal捕获异常的原理

https://www.jianshu.com/p/6ac9600a15fb

1)Mach异常方式

2)Unix信号方式

1signal(SIGSEGV,signalHandler);

3) 应用层  try catch 异常

 UncaughtExceptionHandler()

 

-26 操作系统:图文详解 内存映射 mmap()

https://blog.csdn.net/zhangbijun1230/article/details/81057319

  • 提高数据的读、写 & 传输的时间性能
    1. 减少了数据拷贝次数
    2. 用户空间 & 内核空间的高效交互(通过映射的区域 直接交互)
    3. 用内存读写 代替 I/O读写
  • 提高内存利用率:通过虚拟内存 & 共享对象

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 相关数据结构

 

-25

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 捕捉异常

 

-24 Xcode里调试 自定义 signal的信号 回调函数的执行

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 

 

-23   ipa 包瘦身

https://blog.csdn.net/bravegogo/article/details/86598837

-22 深入 fishhook 的内心世界

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 系统的延迟绑定机制

-21

  • 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 

-20 Mach-o文件结构

 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 

-19 LinkMap 文件构成和 编译项

`-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
<<dead>>    0x00000001  [  1] literal string: 
<<dead>>    0x00000005  [  3] literal string: desc
<<dead>>    0x00000005  [  3] literal string: name
<<dead>>    0x00000004  [  3] literal string: pic
<<dead>>    0x0000000B  [  3] literal string: classifyId
<<dead>>    0x0000000E  [  3] literal string: .cxx_destruct
<<dead>>    0x0000000B  [  3] literal string: v24@0:8@16
<<dead>>    0x00000008  [  3] literal string: v16@0:8
<<dead>>    0x00000008  [  3] literal string: @16@0:8
<<dead>>    0x00000001  [  4] literal string: 
<<dead>>    0x00000007  [  4] literal string: System
<<dead>>    0x0000000C  [  4] literal string: UnityEngine
<<dead>>    0x0000000A  [  4] literal string: System.IO
<<dead>>    0x00000008  [  5] 8-byte-literal
<<dead>>    0x0000000C  [  5] literal string: PRIMARY KEY
<<dead>>    0x0000000C  [  5] literal string: FOREIGN KEY
<<dead>>    0x00000001  [  5] literal string: 
<<dead>>    0x00000020  [  5] CFString
<<dead>>    0x00000020  [  5] CFString
<<dead>>    0x00000008  [  5] _LKSQL_Type_Text
<<dead>>    0x00000008  [  5] _LKSQL_Type_Int
<<dead>>    0x00000008  [  5] _LKSQL_Type_Double
<<dead>>    0x00000008  [  5] _LKSQL_Type_Blob
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_NotNull
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_PrimaryKey
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_Default
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_Unique
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_Check
<<dead>>    0x00000008  [  5] _LKSQL_Attribute_ForeignKey
<<dead>>    0x00000008  [  5] _LKSQL_Convert_FloatType
<<dead>>    0x00000008  [  5] _LKSQL_Convert_IntType
<<dead>>    0x00000008  [  5] _LKSQL_Convert_BlobType
<<dead>>    0x00000008  [  5] _LKSQL_Mapping_Inherit
<<dead>>    0x00000008  [  5] _LKSQL_Mapping_Binding
<<dead>>    0x00000008  [  5] _LKSQL_Mapping_UserCalculate
<<dead>>    0x00000008  [  5] _LKDB_TypeKey
<<dead>>    0x00000008  [  5] _LKDB_TypeKey_Model
<<dead>>    0x00000008  [  5] _LKDB_TypeKey_JSON
<<dead>>    0x00000008  [  5] _LKDB_TypeKey_Combo
...
<<dead>>    0x00000004  [4311] 4-byte-literal
<<dead>>    0x00000004  [4311] 4-byte-literal
<<dead>>    0x00000004  [4311] 4-byte-literal
<<dead>>    0x00000004  [4311] 4-byte-literal
<<dead>>    0x00000008  [4311] 8-byte-literal
<<dead>>    0x00000008  [4312] 8-byte-literal
<<dead>>    0x00000014  [4320] __ZN15PxcConvexMeshHLC2EP17PxConvexMeshData_
<<dead>>    0x00000004  [4320] 4-byte-literal
<<dead>>    0x00000004  [4320] 4-byte-literal
<<dead>>    0x00000004  [4319] 4-byte-literal
<<dead>>    0x00000076  [4319] literal string: /Applications/buildAgent/work/3d1e9e6e6eefaa7f/SDKs/compiler/iphone/../../../LowLevel/common/include/utils/PxcArray.h
 

 
链接:https://www.jianshu.com/p/4bd6d1315104

-18 Method Swizzling的使用问题

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

-17 UITableView 流畅优化

Perfect smooth scrolling in UITableViews

 http://southpeak.github.io/2015/12/20/perfect-smooth-scrolling-in-uitableviews/

-16 NSUrlProtocol的使用场景

iOS UIWebView小整理(三)(利用NSURLProtocol加载本地js、css资源)  https://www.jianshu.com/p/731f49e74742

iOS开发 最新的NSURLProtocol之webView的离线缓存,缓存webView,离线加载  https://blog.csdn.net/horisea/article/details/53815596

 

-15 网络优化

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
 

-14  iOS原生与JS互调

 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

JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; 

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

[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"jsHandleNativeResult(/%@/)",jsStr]];

“jsHandleNativeResult” 为定义好的方法名

把js需要的参数进行拼接,通过定义好的方法传给js。 

-13 网络分层模型 ,RTP协议分析

https://www.cnblogs.com/Zhang-wj/p/5907534.html

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

https://www.jianshu.com/p/73f1d8bc38cb

 

-12 Linux下简单的socket

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

-11  数据库完整性检查和性能

 https://blog.csdn.net/bravegogo/article/details/53517610

-10 数据iOS端启动速度优化

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()调用之前的耗时我们可以优化的点有:

  1. 减少不必要的framework,因为动态链接比较耗时
  2. check framework应当设为optional和required,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查
  3. 合并或者删减一些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完成视图的渲染。
而视图的渲染主要涉及三个阶段:

  1. 准备阶段 这里主要是图片的解码
  2. 布局阶段 首页所有UIView的- (void)layoutSubViews()运行
  3. 绘制阶段 首页所有UIView的- (void)drawRect:(CGRect)rect运行
    再加上启动之后必要服务的启动、必要数据的创建和读取,这些就是我们可以尝试优化的地方

因此,对于main()函数调用之后我们可以优化的点有:

  1. 不使用xib,直接视用代码加载首页视图
  2. NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时,这个影响需要评估,如果耗时很大的话需要拆分(需考虑老版本覆盖安装兼容问题)
  3. 每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log
  4. 梳理应用启动时发送的所有网络请求,是否可以统一在异步线程请求
  5. 首页方法对于viewDidLoad以及viewWillAppear方法中尽量去尝试少做,晚做,不做。

 

-9 UITableView 的 estimatedRowHeight

https://blog.csdn.net/bravegogo/article/details/84108038

-8 WKWebview 拦截 http 请求

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]];

-7 七大查找算法

   https://www.jianshu.com/p/95d224c4d13e

-5 排序

   堆排序 :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

广度优先遍历: 仅仅须要一个队列就可以。先在队列中增加根结点。之后对于随意一个结点来说。在其出队列的时候,訪问之。同一时候假设左孩子和右孩子有不为空的

深度优先遍历:事实上深度遍历就是上面的前序、中序和后序。可是为了保证与广度优先遍历相照顾,也写在这。代码也比較好理解,事实上就是前序遍历,

 

-4 关于CPU位数,OS位数以及内存大小关系

    https://www.jianshu.com/p/03cd195d79d9

    https://www.cnblogs.com/little-YTMM/p/5058354.html

-3  数据库迁移

  
A 、清除旧的数据,重建表
优点:简单
缺点:数据丢失
B、在已有表的基础上对表结构进行修改
优点:能够保留数据
缺点:规则比较繁琐,要建立一个数据库的字段配置文件,然后读取配置文件,执行SQL修改表结构、约束和主键等等,涉及到跨多个版本的数据库升级就变得繁琐并且麻烦了
C、创建临时表,把旧的数据拷贝到临时表,然后删除旧的数据表并且把临时表设置为数据表。
优点:能够保留数据,支持表结构的修改,约束、主键的变更,实现起来比较简单
缺点:实现的步骤比较多,需要进行数据的内容的迁移。

-2 tickcout CPU 时钟的使用

https://www.jianshu.com/p/744235bbf891

-1 tick count  CPU 时钟

  • mach_absolute_time()

mach_absolute_time()可能用到的同学比较少,但这个概念非常重要。

前面提到我们需要找到一个均匀变化的属性值来描述时间,而在我们的iPhone上刚好有一个这样的值存在,就是CPU的时钟周期数(ticks)。这个tick的数值可以用来描述时间,而mach_absolute_time()返回的就是CPU已经运行的tick的数量。将这个tick数经过一定的转换就可以变成秒数,或者纳秒数,这样就和时间直接关联了。

不过这个tick数,在每次手机重启之后,会重新开始计数,而且iPhone锁屏进入休眠之后tick也会暂停计数。

mach_absolute_time()不会受系统时间影响,只受设备重启和休眠行为影响。

  • CACurrentMediaTime()

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()也不会受系统时间影响,只受设备重启和休眠行为影响。

0 c实现的几种定时器

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

 

1 事务模式

可以以三种方式开始事务:

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 
 

2 Block源码分析系列 

经典

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;
}

 

 

 

3 数据库锁状态

一个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

4 sqlite 数据库 读写,日志回滚,事务 介绍

 https://blog.csdn.net/qq_37940313/article/details/80403545  

http://www.cocoachina.com/ios/20161110/18030.html

 

 

 

 

 

 

 


 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值