OC
内存管理
内存管理的原理和原则
- 基本数据类型(
int
,float
,double
,enum
,struct
,union
等)和C语言的类型存储在栈区,由系统分配释放 - 继承自
NSObject
的类属于OC
类型,都遵循内存管理原则 - 谁创建,谁释放(
MRC
程序员管理,ARC
系统封装了编译时插入retain
和release
自动释放池等自动管理内存) OC
方法调用的本质其实是给对象发送消息,需要引用时发送retain
引用计数加1
,释放时发送release
引用计数减1
,每个OC
对象都有一个retainCount
计数器占有4
个字节MRC
设置setter
需要判断,存在旧值,则需要先把旧值释放release
,然后新值retain
之后赋值
内存管理中容易出现的问题和概念
内存泄漏: 开辟了内存空间创建了对象却没有得到真正的使用,也没有得到释放并常驻在内存中,造成对内存的浪费(不用的记得释放,养成合理使用内存的习惯,才能有效避免类似问题)
悬垂指针(也叫迷途指针): 就是指针指向的那块内存已经销毁(对象已经释放了),但是引用它的指针还在,然后指针自己也不知道指向的是什么鬼👻了,对象释放时需要顺带也要把引用它的指针也置为
nil
(OC
中任何对象给nil
发消息都不会有反应的)野指针: 还没有初始化的指针
僵尸对象: 一个已经被释放的对象
空指针: nil,NULL
一个NSObject占用多少内存?查看OC对象占用至少多少字节
1、系统分配了16个字节给NSObject对象(可以通过malloc_size函数得到)
2、但NSObject对象内部只使用了8个字节空间(在64bit环境下,可以通过class_getInstanceSize函数获得)
OC中的几个小常识
#import
, #include
,@class
的区别在哪里?
#include
是C
语言中的导入头文件的方式,会出现重复导入的情况#import
是``#include`的改进版,针对同一个头文件只会导入一次,避免递归导入问题@class
类的声明,可以避免循环导入互相引用头文件报错的问题,还有就是提高编译效率,如果头文件链式引用的话容易出现重复编译的问题(可优化编译耗时)
static
,extern
,const
的使用
static
可以修饰
- 静态局部变量: 保证局部变量永远只初始化一次,在程序的运行过程中永远有且只有一份内存, 生命周期类似全局变量了,但是作用域不变。
1 | //push到这个页面,页面添加按钮点击进行累加操作 点两下pop页面,再次进入继续点击按钮累加 观察控制台打印 |
修饰静态全局变量: 使全局变量的作用域仅限于当前文件内部,即当前文件内部才能访问该全局变量
例如单例:
1 | static ShareManager *_manager = nil; |
- 修饰静态函数:
static
修饰的函数叫静态函数,外部文件无法访问这个函数,函数当前文件可以访问
1 | static inline void hello(){ |
extern
外部引用的意思,其实是用于修饰外部全局变量的关键字,在iOS
中被定义了多种别名,其实本质还是extern
,当然自己开发的时候也可以自定义别名,可以增加代码可读性.
1 | FOUNDATION_EXPORT |
OC中@property
的几个关键字用法 iOS面试之@property
查看代码示例 show the code
// @property = iVar + setter + getter
// 默认是atomic原子的 读写线程安全但耗性能 一般设置nonatomic是非原子的 节省开销
//基本数据类型
@property (nonatomic,assign) int age;
@property (nonatomic,assign) float distance;
@property (nonatomic,assign) double aValue;
@property (nonatomic,assign) NSInteger count1;
@property (nonatomic,assign) NSUInteger count2;
@property (nonatomic,assign) CGFloat number1;
//枚举类型
@property (nonatomic,assign) NSTextAlignment align;
//结构体
@property (nonatomic,assign) CGSize size;
@property (nonatomic,assign) CGRect frame;
//代理委托
@property (nonatomic,assign) id assignDelegate;
@property (nonatomic,weak) id weakDelegate;
//弱引用属性
@property (nonatomic,weak) ShareManager *weakManager;
//IB拉线视图
@property (nonatomic,weak) IBOutlet UIView *testView;
//字符串
@property (nonatomic,copy) NSString *name;
//block/闭包
@property (nonatomic,copy) dispatch_block_t block;
//强引用对象
@property (nonatomic,strong) NSArray *arr;
@property (nonatomic,strong) NSMutableArray *arrM;
@property (nonatomic,strong) NSDictionary *dic;
@property (nonatomic,strong) NSMutableArray *dicM;
@property (nonatomic,strong) ShareManager *strongManager;
//默认是readwrite 对于外部引用是可读可写 设置readonly是只读
@property (nonatomic,strong,readonly) ShareManager *strongManager2;
NSObject
中类方法load
和initialize
load和initialize
这两个类方法会在类被使用时主动调用,但是调用时机和调用顺序却截然不同。
initialize
方法是在该类接收到第一个消息之前调用.- 父类的
initialize
方法先于子类的initialize
方法调用. - 如果子类中没有实现
initialize
方法,或者子类显示调用父类实现[super initialize]
, 那么则会调用其父类的实现。也就是说,父类的initialize
可能会被调用多次。 - 在应用程序生命周期里,
runtime
只会向每个类发送一次initialize
消息. 所以如果category中实现了initialize
方法,那么原来类中的则不会被调用. load
在类或者category
被添加到runtime
的时候调用,该调用发生在main函数之前- 父类
load
方法先于子类调用,类本身load
方法先于category
中调用。 - 不需要在
load
和initialize
方法中显式的去调用父类的方法。
UI
一些常见的知识点
UIView和CALayer的区别?
view: 负责用户交互,事件响应
layer: 负责绘制展示内容,隐式动画
如何高效绘制圆角?
1 | //采用贝塞尔曲线绘制 上下左右四个角 可大可小/可有可无/易于控制 |
布局
更新布局
layoutIfNeeded
强制更新布局setNeedsDisplay
重绘,适用于需要重写drawRect:
绘制的视图updateConstraintsIfNeeded
标记需要更新约束,然后调用layoutIfNeeded
才会生效
纯代码
- 系统AutoLayout
- frame布局
PS:bounds与frame有一定区别。
bounds只用来描述视图的尺寸,就像一页A4纸,不论把它放在桌子上还是地板上,它的bounds都不发生变化。
frame除了能够描述视图的尺寸外还能描述视图的位置。再如A4纸,从桌子上挪到地板上,它的frame就发生变化了
Masonry约束布局
StackView布局
基于YogaKit的Flexbox布局
其他第三方布局库
IB拉线
XIB结合代码
StoryBoard结合代码
利用
IB_DESIGNABLE
和IBInspectable
修饰,然后再重写setter,在IB面板上即可出现添加可勾选☑️的属性列表,此方法适用自定义类,不建议在category中设置,category的话是比较耗性能,时不时爆个红~
1 | IB_DESIGNABLE |
生命周期
UIViewController
生命周期
1 | // 初始化构造方法 |
动画
UIView动画 UIView动画UIView Animation总结
- 常用的块动画
1 | /// 参数一 : 时长 |
Spring
弹性动画
1 | /// 参数一: 执行时长 |
- 视图转场动画
1 | UIView *aView = nil;//替换项目中实际的view |
核心动画
网络
http
超文本传输协议,是一种建立在TCP上的无状态连接,整个基本的工作流程是客户端发送一个HTTP请求,说明客户端想要访问的资源和请求的动作,服务端收到请求之后,服务端开始处理请求,并根据请求做出相应的动作访问服务器资源,最后通过发送HTTP响应把结果返回给客户端
HEAD
工作中只用过一次,用于检测CDN回源,主要是获取Header中的字段值进行判断是否上报
GET
参数拼接到URL上是暴露的,相对于POST来说不安全(POST将参数放到body里),传输数据量小,受限于URL最大长度URL最大长度问题 表单默认请求方式数据集必须为ASCII,执行效率比POST高
POST
安全,参数不可见(放到body中),数据类型没有限制, 可传输的数据容量大(GET和POST可传递的值到底有多大?)
TCP/UDP
tcp是长连接,能够保证传输数据的完整性
udp是发出去就不管的,容易丢包
Socket/WebSocket
socket: 应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口,提供一套调用TCP/IP协议的API
websocket: 一种双向通信协议。在建立连接后,WebSocket服务器端和客户端都能主动向对方发送或接收数据,就像Socket一样;(而http服务端不能主动联系客户端,只能有客户端发起,太被动啦) ,只要建立一次HTTP请求,就可以连续不断的得到服务器推送的消息,节省带宽和服务器端的压力~
WebServer模拟本地接口请求数据
三次握手🤝/四次挥手👋
三次握手(通道的建立):
(1)在建立通道时,客户端首先要向服务端发送一个SYN同步信号。
(2)服务端在接收到这个信号之后会向客户端发出SYN同步信号和ACK确认信号。
(3)当服务端的ACK和SYN到达客户端后,客户端与服务端之间的这个“通道”就会被建立起来。
PS. 以上整个过程有点像叫人开门的感觉, 大声呼喊听得见吗? 听得见的话就把门打开, 然后对方清晰听到后把门开开,即成功建立连接,要是没听见(没连接上)或者没有听清楚(ack丢失了),那就得加大分贝多喊几次(反复发送ack直到确认) …
四次挥手(通道的关闭):
(1)在数据传输完毕之后,客户端会向服务端发出一个FIN终止信号。
(2)服务端在收到这个信号之后会向客户端发出一个ACK确认信号。
(3)如果服务端此后也没有数据发给客户端时服务端会向客户端发送一个FIN终止信号。
(4)客户端在收到这个信号之后会回复一个Ack确认信号,在服务端接收到这个信号之后,服务端与客户端的通道也就关闭了。
PS. 以上过程有点像黑帮交易验货~ 到达交易约定地点之后,由甲方提出要验货,乙方听到后点了点头,然后让小弟把大箱子搬了出来,甲方打开之后验货没有问题,然后叫小弟把装钱的箱子提了上来交给乙方, 乙方清点之后也没有问题,银货两讫,各自退散~
多线程
基本概念
- 手机里每一个App就一个独立的进程,负责运行的程序内存分配,每个进程有独立的虚拟的内存空间, 线程是CPU执行任务的基本单元,是进程执行任务的路径
- 队列: 串行和并行, 串行是一个任务接着一个任务执行,并行是指在不同队列即多个CPU同时执行不同的任务 (多核才有可能实现真正的并行)
- 并发: 单核情况下,单位时间内多个任务快速切换执行,造成同时执行的假象(异步执行任务才有并发一说)
- 同步和异步: 分配了一堆任务,同步是指阻塞式的一个任务执行完再执行下一个任务, 异步是指分不同优先级调度任务顺序来执行任务
- 主线程和子线程: 每个进程必有一条线程即主线程,异步才会开子线程
GCD
- 创建队列
1 | // 队列类型 |
常用的几种:
dispatch_once_t
一次性代码块dispatch_after和dispatch_time_t
设置延时或者倒计时dispatch_barrier_async
栅栏函数,拦截之前任务执行完才让执行下一个dispatch_group
分组处理多任务统一回调问题dispatch_semaphore_t
信号量,线程同步,防止资源抢占
NSOperation
OC高级-NSOperation 和 NSOperationQueue
设计模式 参考
性能优化 参考
本地持久化方案
本地数据库sqlite3.0+FMDB
归档
查看归档相关代码 show the code
#import <Foundation/Foundation.h>
#import "YYModel.h"
@interface Person : NSObject<NSCoding, NSCopying>
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *age;
@end
#import "Person.h"
@implementation Person
//重写以下几个方法
- (void)encodeWithCoder:(NSCoder*)aCoder {
[self yy_modelEncodeWithCoder:aCoder];
}
- (id)initWithCoder:(NSCoder*)aDecoder
{
self = [super init];
return [self yy_modelInitWithCoder:aDecoder];
}
- (id)copyWithZone:(NSZone*)zone {
return [self yy_modelCopy];
}
- (NSUInteger)hash {
return [self yy_modelHash];
}
- (BOOL)isEqual:(id)object {
return [self yy_modelIsEqual:object];
}
@end
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
#import "ViewController.h"
#import "Person.h"
#define KDocumentPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) lastObject]
#define kPersonInfoPath [KDocumentPath stringByAppendingPathComponent:@"personInfo.archiver"]
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//先存档 然后屏蔽这段代码 看看是否归档到了本地
// NSMutableArray *dataArray =[NSMutableArray array];
//
// for (NSInteger i = 10; i < 20 ; i++) {
// Person * p =[Person new];
// p.name = [NSString stringWithFormat:@"name==-%ld",i];
// p.age = [NSString stringWithFormat:@"+++%ld岁",i];
// [dataArray addObject:p];
// }
//
// BOOL ret = [NSKeyedArchiver archiveRootObject:dataArray toFile:kPersonInfoPath];
//
// if (ret) {
// NSLog(@"归档成功");
// }else{
// NSLog(@"归档失败");
// }
NSArray *arr =[NSKeyedUnarchiver unarchiveObjectWithFile:kPersonInfoPath];
for (Person *p in arr) {
NSLog(@"名字%@,年龄%@", p.name,p.age);
}
}
@end
keychain
这个需要绑定唯一标识,不会因为应用卸载而消除,可以重置系统或者手动删除
写文件到沙盒
- Document目录,一般持久化的数据都存储在这个目录,iCloud会自动备份Document中的所有文件,一般重要的文件存储在这里
1 | NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).firstObject; |
- tmp临时目录,临时文件夹里面的文件,由系统回收, 如磁盘内存不足,重启手机,应用进程杀掉,都会清除临时文件,系统自动管理该目录的文件
1 | NSString *tmpDir = NSTemporaryDirectory(); |
Library 存储默认设置或者一些状态信息
- Library/Caches 缓存目录,iCloud不会备份,系统不会自动清除,需要写代码去清除
1
NSString *cacheDir =[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastobject];
- Library/Preferences 存储偏好设置 NSUserDefaults
Runtime
- 黑魔法方法交换实现
1 | //实例方法交换 |
- category中生成关联对象
1 | @property (nonatomic,assign) NSInteger totalScore; |
- 字典转模型
1 | - (instancetype)initWithDict:(NSDictionary *)dict { |
- 消息动态转发
1 | - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params |
- 实现
NSCoding
自动归档和解档
1 | - (id)initWithCoder:(NSCoder *)aDecoder { |
Runloop
音视频处理
音频
录制
1 | -(void)setupRecorder{ |
播放
- AudioServicesCreateSystemSoundID(url,soundId) 用于播放音效
- AVPlayer 本地/远程的音视频均可播放
- AVAudioPlayer 只支持播放本地音频文件
- AudioToolbox中的Audio Queue Services
转格式
- wav 参考WAV文件格式
- aac 体积小,音质尚可,手机语音音频传输用得多 可直接录制 iOS下解码AAC并播放
- mp3 早期音乐音频格式 wav转mp3
- amr 安卓的比aac体积还小的音频格式 amr和wav互转
视频
录制
- 通过系统相机的录制
- 基于
AVFoundation
自定义相机 - 第三方GPUImage自定义相机
播放
- AVPlayer 需要自定义UI
- AVPlayerViewController 封装好的拿来即用
转格式
录制格式为mov需要转成mp4
1 | + (void)convertAvcompositionToAvasset:(AVURLAsset *)composition presentName:(NSString *)presentName completion:(void (^)(AVAsset * _Nonnull, NSURL * _Nonnull))completion { |
存储到相册
和保存图片到相册类似的C接口
1 |
|