虚拟内存和物理内存
计算机组成原理里可知,32位CPU对应32位OS,它的寻址空间是2的32次方,约为4GB,意味着在32位的计算机系统中,理论支持的物理最大寻址空间是4GB。
1 | 32位CPU只有32根地址总线,所以最大的寻址能力是2的32次方 |
计算机组成原理里可知,32位CPU对应32位OS,它的寻址空间是2的32次方,约为4GB,意味着在32位的计算机系统中,理论支持的物理最大寻址空间是4GB。
1 | 32位CPU只有32根地址总线,所以最大的寻址能力是2的32次方 |
1 | NSInteger i = 0xFFFFFFFFFFFFFF; |
由于NSNumber
继承自NSObject
,所有它有isa
指针,加上内存对齐的处理,系统给NSNumber
对象分配了 32 个字节内存。通过 LLDB
指令读取它的内存,实际上它并没有用完 32 个字节。
iPhone 5s开始采用64bit cpu架构,也就是指针占用4个字节,32bit的寻址范围是4G,显然64bit的指针存在空间浪费,对于小对象,比如NSNumer
,NSDate
,NSString
,TaggeddPointer
不再是地址,而是真正的值。这种方式可以节省内存,提高执行效率
引入 Tagged Pointer
技术之后NSNumber
等对象的指针中存储的数据变成了Tag+Data形式(Tag为特殊标记,用于区分NSNumber
、NSDate
、NSString
等对象类型;Data
为对象的值)。这样使用一个NSNumber
对象只需要 8 个字节指针内存。当指针的 8 个字节不够存储数据时,才会在将对象存储在堆上。
1 | - (void)viewDidLoad { |
number1
~number3
指针为Tagged Pointer
类型,可以看到对象的值都存储在了指针中,对应倒数第二位开始的1、2、4f。而number4
由于数据过大,指针的8个字节不够存储,所以在堆中分配了内存。
最后一位用来表示数据类型。
第一位b
的二进制为1011
,其中第一位1是Tagged Pointer
标识位。后面的011
是类标识位,对应十进制为3
,表示NSNumber
类。
下图是iOS下NSNumber
的Tagged Pointer
位视图
1 | @property (nonatomic, strong) NSString * name; |
1 | @property (nonatomic, strong) NSString * name; |
第一段代码Crash,而第二段却没有问题。
分别打印两段代码的self.name
类型看看,原来第一段代码中self.name为__NSCFString
类型,而第二段代码中为NSTaggedPointerString
类型。
__NSCFString
存储在堆上,它是个正常对象,需要维护引用计数的。self.name
通过setter
方法为其赋值。而setter方法的实现如下:
1 |
|
异步并发执行setter
方法,可能就会有多条线程同时执行[_name release]
,连续release
两次就会造成对象的过度释放,导致Crash
。
解决办法:
1 | @property (atomic, strong) NSString * name; |
第二段代码中的NSString为NSTaggedPointerString
类型,在objc_release
函数中会判断指针是不是TaggedPointer
类型,是的话就不对对象进行release
操作,也就避免了因过度释放对象而导致的Crash
,因为根本就没执行释放操作。
1 | __attribute__((aligned(16), flatten, noinline)) |
class
地址class
地址同理64位存储一个内存地址是种浪费,isa
是一个公用体结构union
,NONPOINTER_ISA
是给对象分配了内存空间,但是不使用sideTable
管理引用计数,而是把引用计数存在了isa
当中。
1 | union isa_t |
SideTable
包含了引用计数表,弱引用计数表,以及一个自旋锁。结构如下
1 | struct SideTable { |
是用hash
表实现的,引用计数会存在多张sideTable
中,修改引用计数,需要经过两次hash
算法,第一次是从sideTables
中找到具体的sideTable
,第二次是从sideTable
中找到对应的引用计数。之所以设计成多张sideTable
而不是一张sideTables
,是因为每次操作都需要加锁,减锁操作,多张可以分离锁,加快操作速度。
1 | SideTable &table = SideTables()[this]; |
苹果使用sideTables
保存所有的weak
引用,key就是对象地址,weak_entry_t
作为值,weak_entry_t
中保存了所有指向该对象的弱引用。
1 | struct weak_table_t { |
目前OC管理内存的方式有两种Taged Pointer 和 isa指针
1 | @interface ViewController : UIViewController |
苹果公司在2011年的全球开发者大会上指出,90%的应用崩溃与内存管理有关,其中最主要的原因是错误的内存访问和保留环所引起的内存泄漏。
上一篇文章里可以看到,砸壳(Dumpdecrypted破壳)之后的ipa包使用class-dump分析之后,App跟裸奔一样,所以需要做代码混淆来添加逆向的难度。
前段时间把<<硅谷>>追完了,剧中撒旦教崇拜者Gilfoyle,当时破解了智能冰箱的固件,仔细想想,似忽不存在无法破解的软件,就好像不存在不透风的墙一样,开发人员开发,编译,链接生成一个可执行文件,Mach-O或者ELF,PE,其实本质上都是二进制文件,这当然就可以编辑,不过是在运行的时候添加了非对称加密的认证,对原始文件的一致性会进行认证。可是又有谁能保证非对称加密(RSA)的无法破解性呢?最后一季,他们的去中心化系统通过深度学习,充分发挥分布式计算的算力成功破解了最先进的加密算法!
iPhone的音质一般,音量偏小,作为听个响系列,表现也是不置可否,这篇文章总结一下,iOS本地录音生成Wav文件格式遇到的一个问题。
知道为什么iOS系统可以使用3年,还能保证系统的流畅度么?除了苹果定制的CPU领先业界两年的水平,软件层面,苹果通过签名机制控制了安装在iOS上的APP都是经过苹果官方允许的,这样可以规范控制App在iOS的行为。
在iOS中音频按照播放形式可以分为音效播放和音乐播放。音效主要指的是一些短音频,通常作为点缀音频,如提示音,对于这类音频不需要进行进度,循环等控制。音乐主要指的是一些较长的音频,通常是主音频,对于这类音频播放通常需要精确的控制。在iOS中播放音效一般使用AudioToolbox.framework这个框架,播放音乐一般使用AVFoundation.framework