iOS 开发底层面试攻略
以下是回顾之前上、中、下三篇底层面试题的补充,附上答案。
俗话说得好,底层不牢,地动山摇。
这些答案只是给大家一些参考,大家可以再结合自己理解进行回答,有需要的朋友们下面来一起看看吧。
本文收录:公众号【iOS 进阶宝典《iOS底层面试干货分享(补充)》】
iOS 开发中的加密方式
iOS 加密相关算法框架:CommonCrypto
。
1:对称加密: DES、3DES、AES
加密和解密使用同一个密钥。
加密解密过程:
明文->密钥加密->密文
,
密文->密钥解密->明文
。
优点:算法公开、计算量少、加密速度快、加密效率高、适合大批量数据加密;
缺点:双方使用相同的密钥,密钥传输的过程不安全,易被破解,因此为了保密其密钥需要经常更换。
AES:AES 又称高级加密标准,是下一代的加密算法标准,支持 128、192、256 位密钥的加密,加密和解密的密钥都是同一个。iOS 一般使用 ECB 模式,16 字节 128 位密钥。
AES 算法主要包括三个方面:轮变化、圈数和密钥扩展。
优点:高性能、高效率、灵活易用、安全级别高。
缺点:加密与解密的密钥相同,所以前后端利用 AES 进行加密的话,如何安全保存密钥就成了一个问题。
DES:数据加密标准,DES 算法的入口参数有三个:Key
、Data
、Mode
。
其中 Key 为 7 个字节共 56 位,是 DES 算法的工作密钥;Data 为 8 个字节 64 位,是要被加密或被解密的数据;Mode 为 DES 的工作方式,有两种:加密、解密。
缺点:与 AES 相比,安全性较低。
3DES:3DES 是 DES 加密算法的一种模式,它使用 3 条 64 位的密钥对数据进行三次加密。是 DES 向 AES 过渡的加密算法,是 DES 的一个更安全的变形。它以 DES 为基本模块,通过组合分组方法设计出分组加密算法。
2.非对称加密:RSA 加密
非对称加密算法需要成对出现的两个密钥,公开密钥(
publickey
) 和私有密钥(privatekey
) 。加密解密过程:对于一个私钥,有且只有一个与之对应的公钥。生成者负责生成私钥和公钥,并保存私钥,公开公钥。
公钥加密,私钥解密;或者私钥数字签名,公钥验证。公钥和私钥是成对的,它们互相解密。
特点:
1).对信息保密,防止中间人攻击:将明文通过接收人的公钥加密,传输给接收人,因为只有接收人拥有对应的私钥,别人不可能拥有或者不可能通过公钥推算出私钥,所以传输过程中无法被中间人截获。只有拥有私钥的接收人才能阅读。此方法通常用于交换对称密钥。
2). 身份验证和防止篡改:权限狗用自己的私钥加密一段授权明文,并将授权明文和加密后的密文,以及公钥一并发送出来,接收方只需要通过公钥将密文解密后与授权明文对比是否一致,就可以判断明文在中途是否被篡改过。此方法用于数字签名。
优点:加密强度小,加密时间长,常用于数字签名和加密密钥、安全性非常高、解决了对称加密保存密钥的安全问题。
缺点:加密解密速度远慢于对称加密,不适合大批量数据加密。
3. 哈希算法加密:MD5加密
、.SHA加密
、HMAC加密
哈希算法加密是通过哈希算法对数据加密,加密后的结果不可逆,即加密后不能再解密。
特点:不可逆、算法公开、相同数据加密结果一致。
作用:信息摘要,信息“指纹”,用来做数据识别的。如:用户密码加密、文件校验、数字签名、鉴权协议。
MD5 加密:对不同的数据加密的结果都是定长的 32 位字符。
.SHA 加密:安全哈希算法,主要适用于数字签名标准(DSS
)里面定义的数字签名算法(DSA)。对于长度小于2^64
位的消息,SHA1
会产生一个 160 位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。当然除了SHA1
还有SHA256
以及SHA512
等。
HMAC 加密:给定一个密钥,对明文加密,做两次“散列”,得到的结果还是 32 位字符串。
4. Base64 加密
一种编码方式,严格意义上来说不算加密算法。其作用就是将二进制数据编码成文本,方便网络传输。
用
base64
编码之后,数据长度会变大,增加了大约 1/3,但是好处是编码后的数据可以直接在邮件和网页中显示;
虽然
base64
可以作为加密,但是base64
能够逆运算,非常不安全!base64 编码有个非常显著的特点,末尾有个 ‘=’ 号。
原理:1). 将所有字符转化为 ASCII 码;2). 将 ASCII 码转化为 8 位二进制;3). 将二进制三位一组不足补 0,共 24 位,再拆分成 6 位一组共四组;4). 统一在 6 位二进制前补两个 0 到八位;5). 将补 0 后的二进制转为十进制;6). 最后从 Base64 编码表获取十进制对应的 Base64 编码。
App 安全,数字签名,App 签名,重签名
因为应用实际上是一个加壳的 ipa 文件,但是有可能被砸壳甚至越狱手机下载的 ipa 包直接就是脱壳的,可以直接反编译,所以不要在plist文件
、项目中的静态文件
中存储关键的信息。所以敏感信息对称加密存储或者就存储到keychain
里。而且加密密钥也要定期更换。
数字签名是通过HASH算法
和RSA加密
来实现的。 我们将明文数据加上通过RSA加密
的数据HASH值
一起传输给对方,对方可以解密拿出HASH值
来进行验证。这个通过 RSA 加密HASH值
数据,我们称之为数字签名。
App 签名
1.在 Mac 开发机器上生成一对公钥和私钥,这里称为公钥 L,私钥 L(L:Local)。
2.苹果自己有固定的一对公钥和私钥,私钥在苹果后台,公钥在每个 iOS 设备上。这里称为公钥 A,私钥 A(A:Apple)。
3.把开发机器上的公钥 L 传到苹果后台,用苹果后台的私钥 A 去签名公钥 L。得到一个包含公钥 L 以及其签名数据证书。
4.在苹果后台申请 AppID,配置好设备 ID 列表和 APP 可使用的权限,再加上第③步的证书,组成的数据用私钥 A 签名,把数据和签名一起组成一个
Provisioning Profile
描述文件,下载到本地 Mac 开发机器。
5.在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第④步得到的
Provisioning Profile
描述文件打包进 APP 里,文件名为embedded.mobileprovision
,把 APP 安装到手机上。6.在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证
embedded.mobileprovision
的数字签名是否正确,里面的证书签名也会再验一遍。
7.确保了
embedded.mobileprovision
里的数据都是苹果授权的以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证 APP 签名,验证设备 ID 是否在 ID 列表上,AppID 是否对应得上,权限开关是否跟 APP 里的 Entitlements 对应等。
OC 数据类型
① 基本数据类型
C 语言基本数据类型(如
short、int、float
等)在 OC 中都不是对象,只是一定字节的内存空间用于存储数值,他们都不具备对象的特性,没有属性方法可以被调用。OC 中的基本数据类型:
NSInteger
(相当于 long 型整数)、NSUInteger
(相当于 unsigned long 型整数)、CGFloat
(在 64 位系统相当于 double,32 位系统相当于 float)等。他们并不是类,只是用typedef
对基本数据类型进行了重定义,他们依然只是基本数据类型
。枚举类型:其本质是无符号整数。
BOOL 类型:是宏定义,OC 底层是使用 signed char 来代表 BOOL。
② 指针数据类型
指针数据类型包括: 类class
、id
。
类 class:
NSString
、NSSet
、NSArray
、NSMutableArray
、NSDictionary
、NSMutableDictionary
、NSValue
、NSNumber(继承NSValue)
等,都是 class,创建后便是对象,继承 NSObject。OC 中提供了NSValue
、NSNumber
来封装 C 语言的基本类型,这样我们就可以让他们具有面向对象的特征了。
id:
id
是指向 Objective-C 对象的指针,等价于 C 语言中的void*
,可以映射任何对象指针指向他,或者映射它指向其他的对象。常见的 id 类型就是类的 delegate 属性。
集合 NSSet 和数组 NSArray 区别:
都是存储不同的对象的地址;
但是 NSArray 是有序的集合,NSSet 是无序的集合,它们俩可以互相转换。
NSSet 会自动删除重复元素。
集合是一种哈希表,运用散列算法,查找集合中的元素比数组速度更快,但是它没有顺序。
③ 构造类型
构造类型包括:结构体、联合体
结构体:
struct
,将多个基本数据类型的变量组合成一个整体。结构体中访问内部成员用点运算符访问。联合体(共用体):
union
,有些类似结构体struct
的一种数据结构,联合体(union
)和结构体(struct
)同样可以包含很多种数据类型和变量。
结构体和联合体的区别:
结构体(
struct
)中所有变量是“共存”的,同一时刻每个成员都有值,其sizeof
为所以成员的和。
优点: 是“有容乃大”,全面;
缺点: 是 struct 内存空间的分配是粗放的,不管用不用,全
分配,会造成内存浪费。
联合体(
union
)中各变量是“互斥”的,同一时刻只有一个成员有值,其sizeof
为最长成员的sizeof
。
优点: 是内存使用更为精细灵活,也节省了内存空间。
缺点: 就是不够“包容”,修改其中一个成员时会覆盖原来的成员值;
property 和属性修饰符
@property 的本质是 ivar(实例变量)
+ setter
+ getter
.
我们每次增加一个属性时内部都做了什么:
1.系统都会在
ivar_list
中添加一个成员变量的描述;2.在
method_list
中增加setter
与getter
方法的描述;3.在属性列表中增加一个属性的描述;
4.然后计算该属性在对象中的偏移量;
5.给出
setter
与getter
方法对应的实现,在setter
方法中从偏移量的位置开始赋值,在getter
方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
修饰符:
MRC 下:
assign、retain、copy、readwrite、readonly、nonatomic、atomic
等。ARC 下:
assign、strong、weak、copy、readwrite、readonly、nonatomic、atomic、nonnull、nullable、null_resettable、_Null_unspecified
等。
下面分别解释
assign
:用于基本数据类型,不更改引用计数。如果修饰对象(对象在堆需手动释放内存,基本数据类型在栈系统自动释放内存),会导致对象释放后指针不置为 nil 出现野指针。
retain
:和 strong 一样,释放旧对象,传入的新对象引用计数+1;在 MRC 中和 release 成对出现。
strong
:在 ARC 中使用,告诉系统把这个对象保留在堆上,直到没有指针指向,并且 ARC 下不需要担心引用计数问题,系统会自动释放。
weak
:在被强引用之前,尽可能的保留,不改变引用计数;weak 引用是弱引用,你并没有持有它;它本质上是分配一个不被持有的属性,当引用者被销毁(dealloc)时,weak 引用的指针会自动被置为 nil。可以避免循环引用。
copy
:一般用来修饰不可变类型属性字段,如:NSString
、NSArray
、NSDictionary
等。用 copy 修饰可以防止本对象属性受外界影响,在NSMutableString
赋值给NSString
时,修改前者 会导致 后者的值跟着变化。还有block
也经常使用 copy 修饰符,但是其实在 ARC 中编译器会自动对 block 进行 copy 操作,和 strong 的效果是一样的。但是在 MRC 中方法内部的 block 是在栈区,使用 copy 可以把它放到堆区。
readwrite
:可以读、写;编译器会自动生成 setter/getter 方法。
readonly
:只读;会告诉编译器不用自动生成 setter 方法。属性不能被赋值。
nonatomic
:非原子性访问。用 nonatomic 意味着可以多线程访问变量,会导致读写线程不安全。但是会提高执行性能。
atomic
:原子性访问。编译器会自动生成互斥锁,对 setter 和 getter 方法进行加锁来保证属性的 赋值和取值 原子性操作是线程安全的,但不包括可变属性的操作和访问。比如我们对数组进行操作,给数组添加对象或者移除对象,是不在 atomic 的负责范围之内的,所以给被 atomic 修饰的数组添加对象或者移除对象是没办法保证线程安全的。原子性访问的缺点是会消耗性能导致执行效率慢。
nonnull
:设置属性或方法参数不能为空,专门用来修饰指针的,不能用于基本数据类型。
nullable
:设置属性或方法参数可以为空。
null_resettable
:设置属性,get 方法不能返回为空,set 方法可以赋值为空。
_Null_unspecified
:设置属性或方法参数不确定是否为空。
后四个属性应该主要就是为了提高开发规范,提示使用的人应该传什么样的值,如果违反了对规范值的要求,就会有警告。
weak 修饰的对象释放则自动被置为 nil 的实现原理:
Runtime
维护了一个weak表
,存储指向某个对象的所有weak指针
。weak表
其实是一个hash(哈希)表
,Key
是所指对象的地址,Value
是weak
指针的地址数组(这个地址的值是所指对象的地址)。
weak 的实现原理可以概括一下三步:
1、初始化时:
runtime
会调用objc_initWeak
函数,初始化一个新的 weak 指针指向对象的地址。2、添加引用时:
objc_initWeak
函数会调用objc_storeWeak()
函数,objc_storeWeak()
的作用是更新指针指向,创建对应的弱引用表。3、释放时,调用
clearDeallocating
函数。clearDeallocating
函数首先根据对象地址获取所有weak
指针地址的数组,然后遍历这个数组把其中的数据设为nil
,最后把这个 entry 从 weak 表中删除,最后清理对象的记录。
成员变量ivar
和属性property
的区别,以及不同关键字的作用
成员变量: 成员变量的默认修饰符是@protected
、不会自动生成 set 和 get 方法,需要手动实现、不能使用点语法调用,因为没有 set 和 get 方法,只能使用->
。
属性: 属性会默认生成带下划线的成员变量和setter/getter
方法、可以用点语法调用,实际调用的是 set 和 get 方法。
注意:分类中添加的属性是不会自动生成 **setter/getter**
方法的,必须要手动添加。
实例变量: class 类进行实例化出来的对象为实例对象
关键字作用:
访问范围关键字
@public
:声明公共实例变量,在任何地方都能直接访问对象的成员变量。
@private
:声明私有实例变量,只能在当前类的对象方法中直接访问,子类要访问需要调用父类的 get/set 方法。
@protected
:可以在当前类及其子类对象方法中直接访问(系统默认)。
@package
:在同一个包下就可以直接访问,比如说在同一个框架。关键字
@property
:声明属性,自动生成一个以下划线开头的成员变量_propertyName(默认用 @private 修饰)、属性 setter、getter 方法的声明、属性 setter、getter 方法的实现。**注意:**在协议@protocol
中只会生成 getter 和 setter 方法的声明,所以不仅需要手动实现 getter 和 setter 方法还需要手动定义变量。
@sythesize
:修改 @property 自动生成的_propertyName 成员变量名,@synthesize propertyName = newName;
。
@dynamic
:告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。**谨慎使用:**如果对属性赋值取值可以编译成功,但运行会造成程序崩溃,这就是常说的动态绑定。
@interface
:声明类
@implementation
:类的实现
@selecter
:创建一个 SEL,类成员指针
@protocol
:声明协议
@autoreleasepool
:ARC 中的自动释放池
@end
:类结束
类簇
类簇是 Foundation 框架中广泛使用的设计模式。类簇在公共抽象超类下对多个私有的具体子类进行分组。以这种方式对类进行分组简化了面向对象框架的公共可见体系结构,而不会降低其功能丰富度。类簇是基于抽象工厂设计模式的。
常见的类簇有 NSString
、NSArray
、NSDictionary
等。 **以数组为例:**不管创建的是可变还是不可变的数组,在alloc
之后得到的类都是 __NSPlaceholderArray
。而当我们 init
一个不可变的空数组之后,得到的是 __NSArray0
;如果有且只有一个元素,那就是 __NSSingleObjectArrayI
;有多个元素的,叫做 __NSArrayI
;init
出来一个可变数组的话,都是 __NSArrayM
。
优点:
可以将抽象基类背后的复杂细节隐藏起来。
程序员不会需要记住各种创建对象的具体类实现,简化了开发成本,提高了开发效率。
便于进行封装和组件化。
减少了 if-else 这样缺乏扩展性的代码。
增加新功能支持不影响其他代码。
缺点:
已有的类簇非常不好扩展。
我们运用类簇的场景:
出现 bug 时,可以通过崩溃报告中的类簇关键字,快速定位 bug 位置。
在实现一些固定且并不需要经常修改的事物时,可以高效的选择类簇去实现。例:
针对不同版本,不同机型往往需要不同的设置,这时可以选择使用类簇。
app 的设置页面这种并不需要经常修改的页面,可以使用类簇去创建大量重复的布局代码。
文末推荐:iOS 热门文集 &视频解析
① Swift
② iOS底层技术
③ iOS逆向防护
④ iOS面试合集
⑤ 大厂面试题+底层技术+逆向安防+Swift
喜欢的小伙伴记得点赞喔~
收藏等于白嫖,点赞才是真情ღ( ´・ᴗ・` )ღ
评论