王巍
博客地址:OneV's Den
git地址:onevcat (Wei Wang) · GitHub
江湖人称喵神,目前就职于line。喵神的博客涉及方面比较广, 有Obejctive-C, Swift, SwiftUI, Unity等等。博客内容很有深度,非常值得关注。
戴铭
博客地址:戴铭的博客
git地址:ming1016 (戴铭) · GitHub
《iOS开发高手课》和《跟戴铭学iOS编程》作者,前滴滴出行技术专家。
郭曜源
git地址:ibireme · GitHub
著名的YYKit作者,博客内容更多的是Obejctive-C相关, 其中有不少性能优化相关的,目前更新较少。
念茜
博客地址:念茜_【Android 系统与应用】,【iOS 应用程序开发】,【Qt 应用程序开发】-CSDN博客
安全大佬,博客内容包含iOS和安卓,主要是安全攻防方面,也包含一些其他技术内容。博客似乎搬家了,但没找到新的。
bang
博客地址:bang's blog git地址:bang590 (bang) · GitHub
JSPatch作者。博客技术内容主要是Obejctive-C,目前更新也比较少。
Kenshincui
博客地址:https://www.cnblogs.com/kenshincui/
NSHipster
博客地址:NSHipster
博客内容内容较广,有Obejctive-C, Swift, CoCoa, Xcode等等。
唐巧
博客地址:唐巧的博客
git地址:tangqiaoboy (Tang Qiao) · GitHub
就职于猿辅导,首届国内Swift开发者大会的组织者。博客更新率较高,但目前比较少涉及具体的技术问题。
黄文臣
博客地址:黄文臣_iOS开发详解,iOS进阶,Swift入门教程(1.0)-CSDN博客
抖音iOS架构师, 内容涉及iOS音视频,汇编,动画等方面。
李忠
博客地址:Limboy
来自蘑菇街,RAC实践的国内先驱者。博客更新率较高,但已经比较少涉及一些具体的技术问题。
萧宸宇
博客地址:Ewan's Blog
博客中内容涉及较广,其中包含了许多iOS开发的文章。
ina
博客地址:IAN博客 | 苹果技术分享网
iOS & macOS 开发者|独立博客博主
sunny
博客地址:sunnyxx的技术博客
git地址:sunnyxx (Yuan Sun) · GitHub
博客目前已经很久不更新了,但有很多文章放到现在也值得一看。
阿毛
博客地址:https://xiangwangfeng.com/
git地址:xiangwangfeng (阿毛) · GitHub
Tualatrix Chou
博客地址:I'm TualatriX
王令天下的Blog
博客地址:码农人生
学习Flutter的同学可以关注一下。
码农人生
博客地址:码农人生
已经很久不更新,iOS音视频相关。
iOS 整体架构
002-知识体系| iOS系统整体框架及类继承框架图-综述 - 掘金
Cocoa Touch Layer :
触摸层提供应用基础的关键技术支持和应用的外观。如NotificationCenter的本地通知和远程推送服务,iAd广告框架,GameKit游戏工具框架,消息UI框架,图片UI框架,地图框架,连接手表框架,UIKit框架、自动适配等等
Media Layer :
媒体层提供应用中视听方面的技术,如图形图像相关的CoreGraphics,CoreImage,GLKit,OpenGL ES,CoreText,ImageIO、QuartzCore等等。声音技术相关的CoreAudio,OpenAL,AVFoundation,视频相关的CoreMedia,Media Player框架,音视频传输的AirPlay框架等等
Core Services Layer :
系统服务层提供给应用所需要的基础的系统服务。如Accounts账户框架,广告框架,数据存储框架,网络连接框架,地理位置框架,运动框架等等。这些服务中的最核心的是CoreFoundation和Foundation框架,定义了所有应用使用的数据类型。CoreFoundation是基于C的一组接口,Foundation是对CoreFoundation的OC封装
Core OS Layer :
系统核心层包含大多数低级别接近硬件的功能,它所包含的框架常常被其它框架所使用。Accelerate框架包含数字信号,线性代数,图像处理的接口。针对所有的iOS设备硬件之间的差异做优化,保证写一次代码在所有iOS设备上高效运行。CoreBluetooth框架利用蓝牙和外设交互,包括扫描连接蓝牙设备,保存连接状态,断开连接,获取外设的数据或者给外设传输数据等等。Security框架提供管理证书,公钥和私钥信任策略,keychain,hash认证数字签名等等与安全相关的解决方案。
iOS UIView 和 CALayer
UIView 和 CALayer 的关系
iOS 中 UIView 和 CALayer 的关系 - 掘金
- UIView 是UIKit 中的类,继承于UIResponse,可以响应事件。
- CALayer 是 QuartzCore 中的类,继承于NSObject,负责绘制内容、动画,不能响应事件。
CALayer anchorPoint 和 Position
了解CALayer隐式动画
响应者链机制及其应用
**事件的传递:**Hittest 和 PointInside
当一个事件发生后,事件会从父控件传给子控件,也就是说由UIApplication -> UIWindow -> UIView -> subview, **从 SubView数组最后一个开始hittest,**以上就是事件的传递,也就是寻找最合适的view的过程。
拦截事件处理
在父控件的hitTest:withEvent:中返回子控件作为最合适的view! 因为hittest 是从 SubView数组最后一个开始,有可能最合适的view 比想要指定的view 后添加
事件的响应: touchsBegin
接下来是事件的响应。首先看SubView能否处理这个事件,如果不能则会将事件传递给其上级视图(SubView的superView);如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller,首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传 递;(对于第二个图视图控制器本身还在另一个视图控制器中,则继续交给父视图控制器的根视图,如果根视图不能处理则交给父视图控制器处理);一直到 window,如果window还是不能处理此事件则继续交给application处理,如果最后application还是不能处理此事件则将其丢弃
在事件的响应中,如果某个控件实现了touches...方法,则这个事件将由该控件来接受,如果调用了[supertouches....];就会将事件顺着响应者链条往上传递,传递给上一个响应者;接着就会调用上一个响应者的touches....方法
Swift
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if isHidden == true || alpha < 0.05 || isUserInteractionEnabled == false {
return super.hitTest(point, with: event)
}else{
if self.point(inside: point, with: event) {
return super.hitTest(point, with: event)
}else{
//1.有subView时交给subView 去响应
for subview in self.subviews{
let coverPoint = self.convert(point, to: subview)
return subview.hitTest(coverPoint, with: event)
}
//2.没有subView时交给自己来响应,也就是说你无论在哪儿点击都会响应(扩大点击区域)
//当然这里如果你想扩大到一定的返回,可以在此处加限制
let isResponse:Bool = false
if isResponse {
return self
}else {
return nil
}
//3.如果你不想当没有subView时就随便响应,j就返回nil
return nil
}
}
}
了解UITouch 和 UIEvent
应用
- hittest 扩大按钮点击范围: iOS 扩大UIButton的点击范围 - 简书
Swift
@implementation CustomButton
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// 扩大点击区域的宽度和高度
CGFloat expandWidth = 20.0;
CGFloat expandHeight = 20.0;
// 负值会让响应区域扩大
CGRect largerArea = CGRectInset(self.bounds, -expandWidth, -expandHeight);
// 返回点是否在扩展后的区域内
return CGRectContainsPoint(largerArea, point);
}
@end
UIResponder 的 nextResponder属性 :响应链之nextResponder - 简书
动画
iOS开发系列--让你的应用"动"起来 - KenshinCui - 博客园
动画类型
- CALayer(bounds,position,anchorPoint,transform等)
- Core Animation(基础动画,关键帧动画,动画组,转场动画,逐帧动画)
- UIView动画封装(基础动画,关键帧动画,转场动画)
UIView封装动画存在的问题:
1:不能控制动画的暂停
2:不能进行动画的组合
。。。
这里就需要了解iOS的核心动画Core Animation(包含在Quartz Core框架中)。在iOS中核心动画分为几类:基础动画、关键帧动画、动画组、转场动画。
应用
按钮缩放、抖动动画(使用CAKeyframe实现)
https://www.cnblogs.com/sundaysgarden/articles/9252348.html
Lottie 和 PAG
配置文件
Lottie: Json
PAG: 自研二进制文件
了解Lottie Json 文件大概内容,如何修改
性能优化
卡顿优化、内存优化、启动优化、WKWebview 启动优化、包体积优化、
CPU 占用率
、使用时崩溃率
、耗电量监控
、流量监控
、网络状况监控
、等等
内存优化
内存管理-内存分区
- 堆区:是由程序员分配和释放,用于存放运行中被动态分配的内存段。大小不定,可增加和缩减。
- 栈区:栈是由编译器分配和释放,用于存放程序临时创建的变量、函数的参数、局部变量等。
- 全局(静态)区:是编译时分配的内存空间,在程序运行过程中,此内存中的数据一直存在,程序结束后由系统释放。
static
修饰的变量(全局变量)始终保存到常量区。 - 常量区:是编译时分配的内存空间,在程序结束后由系统释放。存放的是常量,是一块特殊的区域。
- 代码区:用来存放函数的二进制代码,它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,只允许读操作,不允许写操作。
内存问题
- 内存泄漏:是指申请的内存空间使用完毕之后未回收,主要由循环引用引起,例如block 里引用self,delegate 没有使用weak 修饰,Timer 定时器没有 invalidate, 置为 nil
- 野指针:是指引用一个已经释放的内存空间,例如assign 修饰对象,对象创建即释放,只剩一个指针。
- 内存溢出:是指程序在申请内存时,没有足够的内存空间供其使用。
内存问题检测
iOS 内存泄露、野指针调试技巧_weixin_30515513的博客-CSDN博客
- **野指针:**选中Edit Scheme,并点击 Run -> Diagnostics -> Enable Zombie Objects 设置
完 之 后,重新运行。
- **内存泄漏:**Analyzer(静态分析);MLeaksFinder (第三方工具);Instruments Leaks (动态检测);排除法
引用计数(ARC, MRC)
+1:alloc, retain, copy,mutableCopy,new
-1:release
引用计数为0,对象释放; 谁引用,谁释放
属性修饰符
IOS中(assign,retain,copy,weak,strong)的区别以及nonatomic的含义
MRC: assign,retain,copy
ARC: strong,weak
readwrite, nonatomic
Weak 和 Unowned
weak
- 用于避免强引用循环。
- 当引用的对象被释放后,weak引用会自动被设置为nil,避免了悬挂指针(dangling pointers)。
- 可以在声明时使用可选类型(optional)来表示weak引用,因为它可能为nil。
unowned
- 也用于避免强引用循环。
- 不会被自动设置为nil,所以在对象被释放后,如果你访问了一个unowned引用,会导致野指针(野引用)错误。
- 通常用于你确切知道引用的对象在引用生命周期内不会被释放的情况,否则会导致崩溃。
卡顿优化
卡顿原因
iOS 保持界面流畅的技巧 | Garan no dou
iOS 底层 - 性能优化之CPU、GPU - 简书
计算机系统中 CPU、GPU、显示器是协同工作的。CPU 计算好显示内容(如视图的创建、布局计算、图片解码、文本绘制等)提交到 GPU,由 GPU 进行变换、合成、渲染,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。
优化方案
CPU
- **耗时操作放入子线程;**如图片解码、尺寸计算、文本绘制等;
- 控制线程的最大并发数量;
- 提前计算好布局,在有需要的时候一次性调整对应属性,不要多次修改;
- 尽量用轻量级的对象 如:不用处理事件的 UI 控件可以考虑使用 CALayer;
- 不要频繁地调用
UIView
的相关属性 如:frame、bounds、transform 等;
GPU
- 避免离屏渲染;
- 减少视图层级;
- 使用合适的图片格式和大小
离屏渲染(Off-Screen Rendering)
指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
设置了以下属性时,都会触发离屏绘制:
- 遮罩:
layer.mask
- 圆角:同时设置
layer.masksToBounds = YES
,layer.cornerRadius > 0
,可以用 CoreGraphics 绘制裁剪圆角 - 光栅化:
layer.shouldRasterize = YES
- 阴影:如果设置了
layer.shadowPath
不会产生离屏渲染
切圆角优化
iOS性能谈设置圆角的卡顿解决_weixin_30376509的博客-CSDN博客
[iOS] 图像处理 - 一种高效裁剪图片圆角的算法 - 简书
列表优化
CPU
-
预加载
-
多线程
-
缓存
-
drawRect优化
-
图片优化
GPU
-
减少视图数量和层次,可把多个视图预先渲染为一张图片
-
不要让图片和视图超过GPU可渲染的最大尺寸
-
视图不透明
-
防止离屏渲染 OpenGL 中,GPU 屏幕渲染有以下两种方式:
图片优化
图片内存优化
原理
图片占用的内存大小与图像的尺寸有关,与它的文件大小无关。
在iOS SRGB显示格式中(4byte空间显示一个像素),如果解析所有的像素,需要
2048 px * 1536 px * 4 byte/px = 10MB的空间,此时的 ImageBuffer的大小为
10MB。
- 分页加载
- 后台异步加载
- 缩小图片尺寸,列表可以使用缩略图
- 缓存
- 减少同时加载的图片数量
优化
- 选择合适的图片渲染格式
- iOS 默认创建的图片格式是 SRGB,每个像素点通常包括红、绿、蓝和 alpha 数据4个字节。而实际使用时,图像可能不需要这么多通道。
- 使用 UIGraphicsBeginImageContextWithOptions 创建的格式固定是 SRGB,可以使用 UIGraphicsImageRenderer (iOS10之后)替代,会自动选择最合适的图像格式。
- 图片读取
对于仅使用一次或是使用频率很低的大图片资源,使用了[UIImage imageNamed:]方法进行加载
图片的加载,有两种方式,一种是[UIImage imageNamed:]
,加载后系统会进行缓存,且没有API能够进行清理;另一种是[UIImage imageWithContentsOfFile:]
或[[UIImage alloc] initWithContentsOfFile:]
,系统不会进行缓存处理,当图片没有再被引用时,其占用的内存会被彻底释放掉
-
图片压缩,列表使用缩略图
-
后台优化
- 当应用切入后台时,图像默认还在内存中 ,可以在退到后台或view消失时从内存中移除图片,进入前台或view出现时再加载图片 (通过监听系统通知) 。
- 向下采样
WKWebview加载优化
iOS WebView提高加载速度的几种优化方案 - 西门桃桃 ~ 个人博客
启动速度优化: 直播地球项目,H5、CSS、JS 文件模版放本地
WKWebview白屏
解决方法是监听到 URL 为 nil 或者接收到 WKNavigationDelegate的 webViewWebContentProcessDidTerminate
时,reload 页面
启动优化
main函数前优化
main函数后优化
业务流程优化
交互优化
启动器开发
启动动态弹缩
启动时间防腐化
包体积优化
编译项配置
删除无用的资源
尽量减少从库中引入的资源
压缩 PNG 和 JPEG 文件
耗电优化
网络优化
多线程
进程与线程的关系
iOS 线程和进程的简单理解_清秋锁梦的博客-CSDN博客_ios线程
**进程:**进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,系统为每个进程分配独立的内存空间。
**线程:**线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行
进程要想执行任务,必须得有线程,进程至少有一条线程
程序启动会默认开始一条线程,这条线程被称为主线程或Ui线程
进程与线程的区别:
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间.
- 资源拥有:同一进程内的线程共享本进程的资源如内存,I/O,CPU等,但是进程之间的资源是独立的
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉.所以多进程要比多线程健壮.
- 进程切换时,消耗的资源大,效率低.所以设计到频繁的切换时,使用线程要高于进程.同样如果要求同时进行又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程:每个独立的进程有一个程序运行的入口,顺序执行序列.但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制.
- 线程是处理器调度的基本单元,但是进程不是.
iOS开发系列--并行开发其实很容易 - KenshinCui - 博客园
iOS中多线程使用并不复杂,关键是:
- 如何控制好各个线程的执行顺序
- 处理好资源竞争问题
NSOperation, GCD的队列:一种先进先出的数据结构,线程的创建和回收不需要程序员操作,由队列负责。
NSTread
NSThread 是轻量级的多线程开发,但是需要自己管理线程生命周期。selector 方法只能传递一个参数,需要传递多个参数只能自定义对象。
执行顺序
可以设置优先级来影响执行顺序。线程优先级范围为0~1,值越大优先级越高,每个线程的优先级默认为0.5。但是优先级高的未必第一个加载。因为第一,每个线程的实际执行顺序并不一定按顺序执行(虽然是按顺序启动);第二,每个线程执行时实际网络状况很可能不一致。
NSOperation
NSOperation 只要将一个NSOperation放到NSOperationQueue这个队列中线程就会依次启动。NSOperationQueue负责管理、执行所有的NSOperation,在这个过程中可以更加容易的管理线程总数和控制线程之间的依赖关系。
对比之前NSThread加载张图片很发现核心代码简化了不少,这里着重强调两点:
- 使用NSBlockOperation方法,所有的操作不必单独定义方法,同时解决了只能传递一个参数的问题。
- 调用主线程队列的**addOperationWithBlock:**方法进行UI更新,不用再定义一个参数实体(之前必须定义一个KCImageData解决只能传递一个参数的问题)。
- 使用NSOperation进行多线程开发可以设置最大并发线程,有效的对线程进行了控制(上面的代码运行起来你会发现打印当前进程时只有有限的线程被创建,如上面的代码设置最大线程数为5,则图片基本上是五个一次加载的)。
顺序控制
每个NSOperation可以设置依赖线程。但是千万不要设置为循环依赖关系(例如A依赖于B,B依赖于C,C又依赖于A),否则是不会被执行的。
GCD
iOS(Swift) 多线程GCD_LeeCSDN77的博客-CSDN博客
IOS多线程知识总结/队列概念/GCD/串行/并行/同步/异步 - 08号风子 - 博客园
GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法。前面也说过三种开发中GCD抽象层次最高,当然是用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。
GCD队列类型
GCD中也有一个类似于NSOperationQueue的队列,GCD统一管理整个队列中的任务。但是GCD中的队列分为并行队列和串行队列两类:
- 串行队列:只有一个线程,加入到队列中的操作按添加顺序依次执行。
- 并发队列:有多个线程,操作进来之后它会将这些队列安排在可用的处理器上,同时保证先进来的任务优先处理。
其实在GCD中还有一个特殊队列就是主队列,用来执行主线程上的操作任务(从前面的演示中可以看到其实在NSOperation中也有一个主队列)。
GCD任务执行方式
同步异步 :就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:『同步执行』 和 『异步执行』。
- 同步执行(sync) :
- 必须等到上个任务执行完毕后,才会开始执行。
- 只能在当前线程中执行任务,不具备开启新线程的能力。
- 异步执行(async) :
- 添加到线程启动后即可执行,不必等其它任务执行完成。
- 可以在新的线程中执行任务,具备开启新线程的能力。
两者的主要区别:
- 是否等待队列的任务执行结束
- 是否具备开启新线程的能力
GCD 控制线程并发数量
使用信号量dispatch_semaphore_t机制
资源竞用(锁机制):
NSLock,synchronized,GCD 中信号量dispatch_semaphore_t机制
"抢占资源" 使用 atomic 修饰
Runloop
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,一般来说,一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制。Runloop是事件接收和分发机制的一个实现。
RunLoop实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI刷新事件、定时器事件、Selector事件),从而保持程序的持续运行;而且在没有事件处理的时候,会进入睡眠模式,从而节省CPU资源,提高程序性能。
Runloop 相关概念
- **mode:**source,timer, observer
- Runloop 和线程的关系
Runloop 实现的功能
- 定时器
NSTimer、CADisplaylink 都是基于runloop 的,RunLoop为了节省资源,并不会在非常准确的时间点回调这个Timer。Timer 有个属性叫做 Tolerance (宽容度),标示了当时间点到后,容许有多少最大误差。
如果某个时间点被错过了,例如执行了一个很长的任务,则那个时间点的回调也会跳过去,等到下一个循环执行。
- 界面更新
- PerformSelecter
当调用 NSObject 的 performSelecter:afterDelay: 后,实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop,则这个方法会失效。
当调用 performSelector:onThread: 时,实际上其会创建一个 Timer 加到对应的线程去,同样的,如果对应线程没有 RunLoop 该方法也会失效。
- AutoreleasePool
App启动后,苹果在主线程RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。
- 手势响应
- GCD
应用举例
- 使用Runloop检测卡顿: iOS实时卡顿检测-RunLoop(附实例) - 简书
- 线程保活: iOS 线程保活 - 掘金
AFNetworking: AFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop
线程生命周期:iOS 从源码解析Run Loop (一):Run Loop 基本概念理解与 NSRunLoop 文档 - 掘金
ios-设置线程满足某个条件销毁_rv0p111的博客-CSDN博客
定时器
三种常见的定时器:内存问题,准确度问题
iOS常见三种定时器-NSTimer、CADisplayLink、GCD定时器 - 知乎
Runtime
Runtime 是用 C
和汇编
写的,核心是消息传递 (Messaging),是 Objective-C 面向对象和动态性的基础。
基本概念
- 类对象(objc_class)
- 实例(objc_object)
- 元类(Meta Class)
- Method(objc_method)
- isa指针
- SEL(objc_selector)
- IMP
消息传递
一个对象的方法像这样[obj foo]
,编译器转成消息发送objc_msgSend(obj, foo)
,Runtime
时执行的流程是这样的:
- 首先,通过
obj
的isa
指针找到它的class
; - 在
class
的method list
找foo
; - 如果
class
中没到foo
,继续往它的superclass
中找 ; - 一旦找到
foo
这个函数,就去执行它的实现IMP
。
消息转发
前文介绍了进行一次发送消息会在相关的类对象中搜索方法列表,如果找不到则会沿着继承树向上一直搜索知道继承树根部(通常为NSObject
),如果还是找不到并且消息转发都失败了就回执行doesNotRecognizeSelector:
方法报unrecognized selector
错。那么消息转发到底是什么呢?接下来将会逐一介绍最后的三次机会。
- 动态方法解析
- 备用接收者
- 完整消息转发
Runtime应用
- 关联对象(Objective-C Associated Objects):分类增加属性
- 方法魔法(Method Swizzling) :方法添加、方法替换、KVO实现。
swizzling
应该只在+load
中完成。swizzling
应该只在dispatch_once
中完成,由于swizzling
改变了全局的状态,所以我们需要确保每个预防措施在运行时都是可用的。原子操作就是这样一个用于确保代码只会被执行一次的预防措施,就算是在不同的线程中也能确保代码只执行一次。 - 消息转发(热更新)解决Bug(JSPatch)
- 实现NSCoding的自动归档和自动解档
- 实现字典和模型的自动转换(MJExtension)
国际化多语言
应用内切换语言
在iOS应用内动态切换语言:无需重启的解决方案-CSDN博客
方法一
苹果允许我们通过修改NSUserDefaults
中的AppleLanguages
键来设置应用程序特定的语言。然而,这种方法需要应用重新启动才能生效:
Swift
UserDefaults.standard.set("fr", forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
方法二: runtime
- 创建Bundle的子类BundleEx,
- 在 BundleEx 重写 localizedStringForKey:value:table:方法
Swift
class BundleEx: Bundle {
override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String {
if let bundle = Bundle.getLanguageBundle() {
return bundle.localizedString(forKey: key, value: value, table: tableName)
} else {
return super.localizedString(forKey: key, value: value, table: tableName)
}
}
}
extension Bundle {
class func getLanguageBundle(language: Language? = UserDefaults.currentLanguage) -> Bundle? {
guard let languageBundlePath = Bundle.main.path(forResource: language?.rawValue.replacingOccurrences(of: "_", with: "-"), ofType: "lproj") else {
return nil
}
return Bundle.init(path: languageBundlePath)
}
}
Swift
UserDefaults.standard.set([language], forKey: "AppleLanguages")
object_setClass(Bundle.main, BundleEx.self)
UserDefaults.standard.synchronize()
对于已经显示的界面,在AppDelegate 中使用 window.rootViewController = newVC 刷新根视图控制器,确保界面元素按新语言更新。
OC 与JS交互
混合 APP 开发(Hybrid App)_LeeCSDN77的博客-CSDN博客
Native 调用 JS
- WebView 直接注入 JS 并执行
Swift
self.wkWebView.evaluateJavaScript("jsFuncName()") { (result, error) in
print(result, error)
}
JS 调用 Native
- 拦截 URL 请求
- Webkit 的 WKUIDelegate协议(web 调用alert 或 confirm)
- 模型注入(Webkit 的 WKScriptMessageHandler协议)
沙盒机制
应用程序沙盒目录下有三个文件夹Documents、Library(下面有Caches和Preferences目录)、tmp。
- Documents:保存应用运行时生成的需要持久化的数据iTunes会自动备份该目录。苹果建议将在应用程序中浏览到的文件数据保存在该目录下。
- Library/Caches:一般存储的是缓存文件,例如图片视频等,此目录下的文件不会再应用程序退出时删除,在手机备份的时候,iTunes不会备份该目录。
- Library/Preferences:保存应用程序的所有偏好设置iOS的Settings(设置),我们不应该直接在这里创建文件,而是需要通过NSUserDefault这个类来访问应用程序的偏好设置。iTunes会自动备份该文件目录下的内容。
- tmp:临时文件目录,在程序重新运行的时候,和开机的时候,会清空tmp文件夹。
本地存储
NSUserDefaults(偏好设置 ):
偏好设置是专门保存应用的配置信息的,如保存用户名、密码、字体大小、是否登陆等设置,一般不要在偏好设置保存其他数据。
NSUserDefaults
适合存储轻量级的本地数据,支持的数据类型有:NSNumbe (NSInteger、float、double
),NSString
,NSDate
,NSArray
,NSDictionary
,BOOL
,NSData。
NSUserDefaults****保存的是基本数据类型, 不能保存自定义数据对象。
优点:
- 基本类型直接存储,使用方便。
缺点:
- 不能存储自定义类型。
NSUserDefaults
保存的数据都是不可变的,取出来的数据也是不可变类型
keyChain(钥匙串):
- 存储敏感信息
- 应用被卸载,数据也不会被删除
- keychain适合存储
较小的数据量
(不超过上千字节或上兆字节
)的内容
plist
轻量级,只能存储数组,字典
NSKeyArchiver(归档)
【iOS】数据持久化:使用NSKeyedArchiver进行数据归档 - 简书
- 可以存储自定义对象
- 需要服从 NSCoding 协议
SQLite
存储大量数据,可以存储对象
- 创建数据库 DB
- 创建table, 设置存储字段
- 插入model,将 model 各个属性赋值给table 中相应字段
Realm 数据库和WCDB的区别
Realm:key-value数据库,跨平台
-
Model 必须继承子 Object 类,对代码侵入性很强
-
必须在同一线程中操作realm,否则会崩溃
-
数据迁移简单,dbVersion 加 1 即可,Realm 会自行检测新增和需要移除的属性,然后自动更新硬盘上的数据库架构,移除属性的数据将会被删除。
-
事务需要自己封装
-
支持数据变化通知
-
没有自增属性
WCDB:基于 SQLite
-
model 继承自 Codable 协议
-
支持多个线程并发操作,不需要担心线程安全问题
-
自带的插入更新接口都使用了嵌入事务,不需要自己封装
Swift 和 OC
SWift
Swift 和 OC
语言类型
- Swift 是面向协议POP,静态类型语言。
- OC 是面向对象OOP,动态类型语言。
类型安全与类型推断
- Swift 是强类型和类型安全的语言,意味着你必须明确地处理不同的数据类型。同时,它支持类型推断(无需显式声明类型,编译器会自动推断)。
- OC 允许动态类型,许多类型信息是在运行时决定的,而不是编译时。
可选类型 (Optionals)
- Swift 引入了 Optionals ,用于处理
nil
值,以避免常见的空值错误。你需要明确地解包可选类型,避免直接使用nil
。- OC 没有可选类型的概念,任何对象都可以是
nil
,这可能导致一些意外错误。错误处理
- Swift 引入了强大的 错误处理 机制,使用
do-try-catch
结构处理错误。它的错误处理是编译时检查的,确保开发者正确处理可能的错误。- OC 的错误处理依赖
NSError
对象,使用的是传统的返回值和指针传递方式,错误处理较为繁琐。函数式编程与闭包
- Swift 支持函数式编程范式,提供了强大的 闭包(Closures)机制。闭包在 Swift 中是第一等公民,简洁且易用。
- OC 也有类似的 Block 概念,但相比 Swift 的闭包,语法更为复杂。
协议扩展与泛型
- Swift 支持 协议扩展 和 泛型,使代码更加灵活和可复用。开发者可以为任何类型扩展方法,而无需修改现有代码。
- OC 也支持协议和泛型,但 Swift 的泛型功能更强大和类型安全。
动态 vs 静态派发
- 默认情况下,Swift 使用 静态派发,意味着在编译时决定函数调用的实现,这提升了性能。不过,如果你使用
@objc
关键字,可以启用动态派发,类似于 Objective-C 的运行时。- OC 大部分调用是通过 动态派发 完成的,意味着在运行时决定方法的实现。虽然这提供了灵活性,但性能相对较差。
Struct 和 Class 区别
值类型 vs 引用类型
struct
是值类型 :在 Swift 中,结构体是值类型,这意味着每次你将结构体实例赋值给一个新的变量或常量,都会复制一份新的副本。也就是说,两个变量指向的是完全不同的结构体实例,它们之间互不影响。class
是引用类型 :类是引用类型,赋值一个类实例时,新的变量只是指向原始实例的引用。两个变量指向的是同一个实例,修改其中一个变量会影响另一个。继承
struct
不支持继承 :结构体不支持类继承(Inheritance
)。也就是说,不能创建一个结构体的子类型。class
支持继承:类支持继承。可以定义一个类继承自另一个类,从而获取父类的属性和方法。这是类相较于结构体的一个重要特性。可变性
struct
的实例是不可变的(默认情况下) :如果你用let
声明一个结构体实例,即使该结构体中的属性是var
,你也不能修改它的属性。结构体的可变性由它的变量声明决定。class
的实例总是可变的 :即使类的实例是用let
声明的,只要类的属性是var
,你仍然可以修改它的属性。内存管理
struct
存储在栈中:结构体实例通常存储在栈中,栈是一个高效的内存区域,分配和释放的开销较小。因此,结构体更适合用于小型且短期的数据处理。class
存储在堆中:类的实例存储在堆中,堆的内存管理相对复杂,涉及到引用计数(ARC)。类实例的分配和释放需要更多的性能开销。引用计数
struct
不涉及引用计数:由于结构体是值类型,直接存储在栈中,因此不需要像类那样使用 ARC 管理其生命周期。class
使用 ARC (Automatic Reference Counting):类是引用类型,Swift 使用 ARC 来自动管理类实例的内存。当类的引用计数为 0 时,ARC 会自动释放类实例。相等性比较
struct
自动符合值比较 :结构体之间可以直接进行值比较(如果它们的所有属性都遵循Equatable
协议,Swift 会自动为结构体生成等价比较的方法)。class
进行引用比较 :类默认比较的是引用,即两个类实例是否引用的是同一个对象。如果要比较类的属性是否相同,需要手动实现Equatable
协议线程安全
struct
更易于实现线程安全:由于结构体是值类型,赋值会产生副本,多个线程可以安全地操作自己的结构体副本,而不需要同步。class
需要注意线程安全:由于类是引用类型,多个线程可能同时访问同一个实例,这可能导致数据竞争。因此,在多线程环境下,需要特别注意类的线程安全问题。
选择 struct
和 class
-
使用
struct
的场景:- 当你希望类型是不可变的(或只希望某些属性可变)。
- 当你希望每次赋值时创建副本,而不是共享实例。
- 当类型是简单的数据模型,不需要继承或复杂的生命周期管理。
-
使用
class
的场景:- 当你需要继承特性或需要共享对象实例。
- 当你需要对对象的引用进行管理。
- 当你需要对对象的生命周期进行细粒度控制,例如引用计数、弱引用等。
为什么需要 mutating
?
Swift 设计结构体的初衷是值语义------每次修改值类型实例时,都会创建一个副本。而 mutating
关键字打破了这个规则,允许你在方法内部修改实例的属性,而不是创建一个新的副本。
Swift 高阶函数
令你极度舒适的Swift集合类高阶函数_DCSnail-蜗牛-CSDN博客_swift 高阶函数
map(映射), flatmap(映射,降维,过滤nil), compactmap(映射,过滤nil), filter(过滤), forEach(遍历), reduce(拼接)
Swift 面向协议
举例:UICollectionViewCell的某几个子类要使用同一个方法。
OC 面向对象的思想:
- 继承:能解决问题,但缺点也很明显,继承很容易带来耦合。所有的子类都依赖它,代码难抽离;父类越来越笨重;想要UITableViewCell 也实现相关功能时,无法继承,又要重新在UITableViewCell 里写。
- 类别:可以实现,缺点是给一个类加上一个功能会污染所有的对象,即使他们根本不需要这个功能。
Swift面向协议的思想:
针对某个要实现的功能,我们可以使用协议定义出接口,然后利用协议扩展提供默认的实现。
所有需要该功能的类都可以直接服从协议实现。
这样就最大程度减少了耦合。
Swift的标准库大多都是使用的Protocol 实现的。
OC
- OC 是面向对象OOP 的动态类型语言
面向对象
- 封装:隐藏对象属性和实现细节,仅对外公开接口,控制属性的访问级别;
- 继承:子类继承父类的属性和方法;
- 多态:子类对继承自父类的方法可以有不同实现;
动态性
iOS OC动态运行时(runtime)~动态特性表现 - 简书
动态:将编译时要做的事,推迟到了运行时。
OC动态性主要体现在三个方面:动态类型(Dynamic typing)、动态绑定(Dynamic binding)和动态加载(Dynamic loading)。动态------必须到运行时(run time)才去执行。
- 动态类型:即运行时再决定对象的类型,简单来说就是id类型。动态类型是跟静态类型相对的。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型 在编译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。
- 动态绑定:到运行的时候才动态地添加函数调用,在运行时才决定要调 用什么方法,需要传什么参数进去。基于动态类型,在某个实例对象类型被确定后,该对象对应的属性和响应消息也被完全确定。
- 动态加载:运行时根据需求加载所需要的资源,最基本就是不同机型的适配,例如,在Retina设备上加载@2x的图片,而在老一些的普通苹设备上加载原图,让程序在运行时添加代码模块以及其他资源,用户可根据需要加载一些可执行代码和资源,而不是在启动时就加载所有组件,可执行代码可以含有和程序运行时整合的新类。
组件通信
函数式响应编程
函数式编程
函数式编程 (Functional Programming,简称 FP)是一种编程范式,核心思想是将计算视为函数的组合 ,并避免使用可变状态和副作用。它强调使用纯函数、不可变数据和表达式式编程,旨在编写简洁、可复用和易于测试的代码。
核心特点
-
纯函数(Pure Functions):
- 函数的输出只依赖于输入参数,且不会修改外部状态或变量(无副作用)。
- 纯函数在相同输入下永远返回相同的输出,便于推理和测试。
- 例子:
func add(a: Int, b: Int) -> Int { return a + b }
-
不可变性(Immutability):
- 数据一旦创建就不能修改(不可变),每次需要改变时都会返回新的数据副本。
- 不可变数据能避免许多并发问题。
-
高阶函数(Higher-Order Functions):
- 函数可以作为参数传递给其他函数,或者作为返回值返回。
- iOS 中常见的高阶函数:
map
、filter
、reduce
等。 - 例子:
let squares = [1, 2, 3].map { $0 * 2 }
-
函数作为一等公民:
- 函数可以像变量一样被赋值、传递和操作。
-
链式调用(Chaining):
- 函数式编程鼓励将多个函数组合在一起,通过链式调用实现复杂的操作。
- 例子:
let result = array.filter { $0 > 0 }.map { $0 * 2 }.reduce(0, +)
-
无副作用(No Side Effects):
- 副作用指修改全局变量、类的属性或与外部系统交互等操作。函数式编程尽量避免这些副作用,使函数调用变得更安全。
应用
- Swift 提供了许多函数式编程特性,如
map
、filter
、reduce
等高阶函数,以及对闭包和不可变性的支持。 - 常用于处理数组、数据流等场景,也常见于 SwiftUI 和 Combine 等框架的使用中。
总结:函数式编程追求简洁、可复用、无副作用的代码风格,通过使用纯函数和不可变数据来提高代码的可预测性和可测试性。
响应式编程
响应式编程是一种以异步数据流为核心的编程范式,它使得处理复杂的异步任务和动态 UI 更新变得更加直观和简洁。在 iOS 开发中,使用 RxSwift 或 Combine 等响应式框架,可以帮助开发者更轻松地管理异步事件、处理数据流并保持应用程序的响应性。
- 核心概念:数据流、观察者模式、可观察序列、操作符、订阅、异步事件处理。
- 框架:RxSwift 和 Combine 是常用的响应式编程框架。
- 优势:响应式编程简化了异步任务处理,提供了一种优雅的方式来处理事件流。
响应式编程的优势
-
代码简洁:响应式编程可以通过链式调用减少嵌套的回调和状态管理逻辑,使代码更加简洁和易读。
-
异步任务处理更简单:通过流的方式统一处理异步操作,如网络请求、UI 事件响应,减少回调地狱和复杂的状态管理。
-
组合复杂数据流:使用操作符可以轻松地将多个异步流进行组合、过滤、合并,从而简化复杂的异步逻辑。
-
响应性:通过响应式编程,应用程序可以对数据变化和用户输入做出实时反应,确保界面始终与数据保持同步。
RxSwift
iOS底层原理(七):系统架构之MVVM+RxSwift - 简书
iOS防抖和节流
RXSwift 中的节流(Throttle) 与 防抖(Debounce) - 简书
iOS中的Throttle(函数节流)与Debounce(函数防抖) - 简书
为什么要防抖节流?
如 scroll、click、reload等高频率的触发事件,会过度损耗页面性能,导致页面卡顿,页面抖动。有时我们不希望在事件持续触发的过程中那么频繁地去执,。此时防抖和节流是比较好的解决方案。
它们用于控制事件的频率,避免过于频繁的操作,尤其在处理用户输入、滚动事件等场景中非常有用。
1. 防抖(Debounce)
Debounce 的工作原理是:在一个指定的时间间隔内,如果没有新的事件发出,则发出最后一个事件。如果在时间间隔内有新的事件到来,那么时间间隔重新计时。
防抖的常见场景是搜索框,只有用户停止输入一段时间后才会触发搜索请求,避免频繁请求。
Swift
let searchTextObservable = searchTextField.rx.text.orEmpty
searchTextObservable
.debounce(.milliseconds(300), scheduler: MainScheduler.instance) // 300毫秒内没有新的输入,则发出最新的输入
.distinctUntilChanged() // 防止重复搜索相同内容
.subscribe(onNext: { query in
performSearch(query)
})
.disposed(by: disposeBag)
2. 节流(Throttle)
Throttle 的工作原理是:在指定的时间间隔内,最多只发出一个事件。可以选择 latest
模式(只发出最新的事件)或者 first
模式(只发出第一个事件)。
节流常用于限制某些高频操作,如按钮点击或滚动事件,避免性能问题。
Swift
let buttonTapObservable = button.rx.tap
buttonTapObservable
.throttle(.seconds(2), scheduler: MainScheduler.instance) // 每2秒内最多只触发一次
.subscribe(onNext: {
handleButtonTap()
})
.disposed(by: disposeBag)
区别
- Debounce 适用于需要等待用户停止操作后才触发事件的场景。
- Throttle 适用于限制事件触发频率,在频繁的操作中限制调用次数。
这两个操作符在处理用户输入、滚动、点击等场景中非常实用,能有效提升应用的性能和用户体验。
RxSwift核心概念
-
Observable - 可监听序列
-
Observer - 观察者(subscribe,bind)
-
Observable & Observer 既是可监听序列也是观察者(PublishSubject**,**ReplaySubject,BehaviorSubject)
-
Operator - 操作符(skip, debug, merge, zip, debounce, filter)
-
Disposable - 可被清除的资源
-
Schedulers - 调度器
-
Error Handling - 错误处理
Swift
为值类型,在传值与方法回调上有影响,RxSwift
一定程度上弥补Swift
的灵活性
RxSwift
使得代码复用性较强,减少代码量RxSwift
因为声明都是不可变更,增加代码可读性RxSwift
使得更易于理解业务代码,抽象异步编程,统一代码风格RxSwift
使得代码更易于编写集成单元测试,增加代码稳定性
注意点:
- 事件回调中使用 weak ;
- tableview cell 中使用,要在 prepare 方法中充值 disposed 属性。
MVVM
MVVM由MVC 演变而来,通常会把大量原来放在 Controller
里的视图逻辑和数据逻辑 移到 ViewModel
里,从而有效的减轻了 Controller
的负担。
MVVM的组成 :MVVM由Model、View、ViewModel、Controller
四部分组成:
-
View
是视图显示层,只展示UI,不做任何逻辑处理,与绑定ViewModel; -
Model
是数据模型,一般通过网络层进行一次封装; -
ViewModel
是视图模型,负责处理逻辑,包括网络请求、业务逻辑、UI逻辑等,可以把ViewModel想象成一个黑盒,传进去输入源后,经过ViewModel进行处理,得到输出源 -
Controller
是胶水层,负责拼接UI,装配ViewModel,并且将ViewModel与View进行绑定
MVVM的优点:
- MVVM 将逻辑交给了ViewModel层 ,控制器 只需要负责
数据绑定
,如此一来控制器的压力就减轻了很多; - ViewModel与控制器及其页面相互独立,可以很方便给ViewModel写单元测试,适合业务逻辑比较复杂的大型项目。
MVVM的缺点:
- MVVM学习成本比较高;
- 需要对ViewModel和View进行双向绑定,当出现Bug时,会迅速被传递,比较难以调试;
iOS 内购
怎么处理订阅失败
应用间跳转
- URLScheme
- niversal Link iOS 唤起APP之Universal Link(通用链接) - 简书
图形绘制
- iOS提供了两套绘图框架,分别是UIBezierPath和Core Graphics。UIBezierPath属于UIKit。UIBezierPath是对Core Graphics框架的进一步封装。
- OpenGL和Core Graphics都是绘图专用的API类族,调用图形处理器(GPU)进行图形的绘制和渲染。在架构上是平级的,相比UIkit更接近底层。
UIBezierPath用于创建基于矢量的路径,如圆形、椭圆形和矩形,或者由多个直线和曲线组成的形状。优势是方便,能用最少的代码得要想要的图形,并且不需要管理图形上下文、缓冲区等容易出问题的地方,只需要关注图形本身就行了。最主要的缺点是支持的效果有限,当需要实现一些复杂图形、复杂渐变效果的时候就无能为力了。
Core Graphics是基于Quartz框架的高保真输出2D图形的渲染引擎。可处理基于路径的绘图、抗锯齿渲染、多个图形叠加、多种颜色渐变、图像、颜色管理、PDF文档等。 Core Graphics提供了一套2D绘图功能的C语言API,使用C结构体和C的函数模拟了一套面向对象的编程机制。Core Graphics中没有OC的对象和方法。
无论图片、PDF还是视图的图层,都是由CoreGraphics框架完成绘制的。UIImage、UIBezierPath和NSString都提供了至少一种用于在drawRect:中绘图的方法,实现原理是将Core Graphics代码封装在其中,降低绘图难度。
OpenGL ES是直接操作GPU绘图,而Core Graphics框架是先把命令输入到CPU再调用GPU绘图。很明显OpenGL ES绘图的效率更高,使用OpenGL ES绘图也可以实现跨平台,而Core Graphics只能在iOS系统中使用。相比Core Graphics只支持2D绘图,OpenGL ES对2D\3D都有很好的支持。
可见OpenGL ES是全方面的碾压Core Graphics,但是除了特别大的图片运算需求外,我们很少使用OpenGL ES。其实上面的示例代码已经很好的说明了,绘制同一个图形UIBezierPath需要5行代码,Core Graphics需要10行代码,虽然Core Graphics明显比UIBezierPath工作量大,但还是同一个数量级的。而使用OpenGL ES绘制,算上Vertex Shader和Fragment Shader绘制一个图形要300~400行代码。整整增加了两个数量级。再加上调试、debug的工作量,就算有跨平台的加成也无法抵消增加的时间成本。想使用OpenGL进行绘图,基本上都需要二次封装,这也是为什么我们在绘图的时候一般首选Core Graphics。
CoreText:CoreText实战讲解,手把手教你实现图文、点击高亮、自定义截断功能 - 掘金
iOS图形处理概论:OpenGL ES,Metal,Core Graphics,Core Image,GPUImage,OpenCV等 - 掘金
iOS 图形绘制框架 ------UIBezierPath 、Core Graphics 和OpenGL - 简书
访问控制关键字
https://www.cnblogs.com/lxlx1798/articles/14745082.html
public, open, internal, file-private, private
-
open
: 可以在定义的模块中使用,也可在其他的模块中使用,(模块相当项目的target)其他模块也可继承、重写。
open
只能用在类、类成员上。打包静态库给其他项目使用时就需使用open
修饰 -
public
:可以在定义的模块中使用,也可在其他的模块中使用,但是其他模块不能继承、重写。 -
internal
: 只允许在定义的模块中访问, 不允许在其他模块中方法。通常用来隐藏文件内部实现细节。 -
fileprivate
: 只允许在定义的源文件中访问(只能在.swift文件中使用) -
private
: 只允许在定义的封闭声明中访问(例如:类中)
Cocopods私有库
本地私有库
iOS组件化(一):创建本地私有库 - WinJayQ - 博客园
远程私有库
iOS组件化----- 创建私有组件库 - 简书
远程索引库不能为空(至少有一个master 分支), 否则索引库和代码库关联时会报错:
Your configuration specifies to merge with the ref 'refs/master'
from the remote, but no such ref was fetched.
私有库更新
iOS 自定义私有库及更新_LeeCSDN77的博客-CSDN博客
- 修改源码
- 修改.podspec文件中的版本
- 新增tag
- 推送标签
- 推送版本
- 验证pod和spec, 推送repo
- 测试版本更新是否成功
APP 启动流程
UIViewController 生命周期
KVC, KVO
iOS KVC和KVO详解 - 掘金
Swift 4新知:KVC和KVO新姿势_51CTO博客_swift kvo
Framework
Framework 分类: 静态库和动态库
iOS使用Swift制作Framework(开发、调试、打包) - 简书
制作静态库
Swift+第三方库的framework制作流程详解 - 掘金
https://juejin.cn/post/6959103632314728478
崩溃调试
使用Umeng,bugly
断点调试
音视频, 直播
音频
视频
直播
基本概念
推流、拉流卡顿优化
HTTP及网络编程相关
OSI七层模型和TCP/IP五层模型详解
每层对应的设备**TCP/IP 是互联网相关的各类协议族的总称。**比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议。
TCP/IP 协议是一个协议簇 。里面包括很多协议的。UDP 只是其中的一个。之所以命名为 TCP/IP 协议,因为 TCP,IP 协议是两个很重要的协议,就用他两命名了。
。这些协议可以划分为四层,分别为链路层、网络层、传输层和应用层。
- 链路层:负责封装和解封装IP报文,发送和接受ARP/RARP报文等。
- 网络层:负责路由以及把分组报文发送给目标网络或主机。
- 传输层:负责对报文进行分组和重组,并以TCP或UDP协议格式封装报文。
- 应用层:负责向用户提供应用程序,比如HTTP、FTP、Telnet、DNS、SMTP等。
每层对应的协议
HTTP
HTTP(Hyper Text Transfer Protocol超文本传输协议),是从WEB服务器传输超文本标记语言(HTML)到本地浏览器的传送协议。
设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。
- 支持客户/服务器模式:也是一种请求/响应模式的协议。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
- 灵活:HTTP允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。
- 无连接:限制每次连接只处理一个请求。服务器处理完请求,并收到客户的应答后,即断开连接,但是却不利于客户端与服务器保持会话连接,为了弥补这种不足,产生了两项记录http状态的技术,一个叫做Cookie,一个叫做Session。
- 无状态:无状态是指协议对于事务处理没有记忆,后续处理需要前面的信息,则必须重传。
Session、Cookie: https://www.cnblogs.com/xuxinstyle/p/9813654.html
HTTP 的 KeepAlive 和 TCP 的 KeepAlive 的关系
TCP和HTTP中的KeepAlive机制总结_nginx_陈德伟_InfoQ写作社区
Socket
iOS WebSocket 使用 (SocketRocket) - 简书
Socket 是TCP/IP网络的API,Socket接口定义了许多函数或例程,用以开发TCP/IP网络上的应用程序。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层 ,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面。对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
HTTP、TCP、UDP、SocketSocket 编程过程
建立socket连接
建立Socket连接至少需要一对套接字,其中一个运行于客户端,
称为ClientSocket,另一个运行于服务器端,称为ServerSocket。
套接字之间的连接过程分为三个步骤:
1.服务器监听
2.客户端请求
3.连接确认
TCP和UDP
详解TCP协议与UDP协议的区别_网络协议_Linux服务器开发_InfoQ写作社区
TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。
TCP
TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的字节流服务。在收发数据前,必须和对方建立可靠的连接。
特点
面向连接,是指发送数据之前必须在两端建立连接。建立连接的方法是"三次握手",这样能建立可靠的连接。建立连接,是为数据的可靠传输打下了基础。
仅支持单播传输
每条 TCP 传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。
- 面向字节流
TCP 不像 UDP 一样那样一个个报文独立地传输,而是在不保留报文边界的情况下以字节流方式进行传输。
可靠传输,判断丢包,误码靠的是 TCP 的段编号以及确认号。TCP 为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。
提供拥塞控制
当网络出现拥塞的时候,TCP 能够减小向网络注入数据的速率和数量,缓解拥塞
- TCP 提供全双工通信
TCP 允许通信双方的应用程序在任何时候都能发送数据,因为 TCP 连接的两端都设有缓存,用来临时存放双向通信的数据。当然,TCP 可以立即发送一个数据段,也可以缓存一段时间以便一次发送更多的数据段(最大的数据段大小取决于 MSS)
UDP
UDP (User Datagram Protocol,用户数据报协议),是 OSI(Open System Interconnection,开放式系统互联) 参考模型中一种非连接 的传输层 协议,提供面向事务 的简单不可靠信息传送服务,传输数据之前源端和终端不建立连接 ,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上,故也不安全。
它有以下几个特点:
- 面向无连接。
- UDP是面向报文的。
- 不可靠性。
- 头部开销小,传输数据报文时是很高效的。
- 有单播,多播,广播的功能。UDP 传输方式支持一对多,多对多,多对一。
TCP 建立链接三次握手和断开连接四次挥手
详解TCP协议与UDP协议的区别_网络协议_Linux服务器开发_InfoQ写作社区
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
这里可能大家会有个疑惑:为什么 TCP 建立连接需要三次握手,而不是两次?这是因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
WebSocket
IOS Websocket (一) Starscream实现Websocket通讯 - 简书
iOS WebSocket 使用 (SocketRocket) - 简书
WebSocket 是 HTML5 一种新的协议 。它实现了浏览器与服务器全双工通信 ,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:WebSocket 是一种双向通信协议。
在
WebSocket API
,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。WebSocket 通常应用在某些数据经常性或频繁改变的场景。例如实时消息通知、实时聊天、交易系统中的变化的股票价格等。
现在很多第三方比如:融云,环信,网易云,腾讯云等等,都可以满足即时通讯的功能。而且技术成熟。但是当由于特殊要求不能使用第三方时,就可以使用WebSocket建立长链接 ,实现即时通讯功能。
WebSocket 特点:
1、建立在 TCP 协议之上,服务器端的实现比较容易。
2、与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3、数据格式比较轻量,性能开销小,通信高效。
4、可以发送文本,也可以发送二进制数据。
5、没有同源限制,客户端可以与任意服务器通信。
6、协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
ws://example.com:80/some/path
Starsream
IOS Websocket (一) Starscream实现Websocket通讯 - 简书
WebSocket 双端实践(iOS/ Golang) - 掘金
Swift版本 WebSocket最好用的框架就应该是daltoniam的Starscream了。
Starscream
库下载地址:StarscreamStarscream会自动响应传入的ping 控制帧,这样你就不需要手动发送 pong。
但是,如果出于某些原因需要控制这个 prosses,你可以通过禁用 respondToPingWithPong 来关闭自动 ping 响应。
socket.respondToPingWithPong=false//Do not automaticaly respond to incoming pings with pongs.
SocketRocket
iOS WebSocket之SocketRocket - 掘金
心跳机制
心跳就是用来查看TCP连接双方是否可用,而TCP的KeepAlive则是保证连接的存在,并不能保证双方的可用性。
心跳ping 由客户端发起,假如在指定时间内未收到回调,那此时就判断此时连接不可用,我们应该主动断开连接,当然服务端也维护一份scoket心跳,在未收到客户端的心跳之后,服务端任务连接失效,也会主动断开。
OC 最好用的算是Facebook的SocketRocket,使用说明可以参考我的另一篇文章iOS WebSocket长链接
- 创建WebSocketManager类(一般使用单例)
- connect创建连接
- 发送心跳(使用定时器)
- 代理收到消息,回调代理方法
WebSocket与Socket、TCP、HTTP的关系及区别
理解WebSocket与Socket、TCP、HTTP的关系及区别_Apifox的技术博客_51CTO博客
WebSocket 和HTTP的区别
iOS WebSocket之SocketRocket - 掘金
区别总结
- 连接方式不同: HTTP 是一种单向请求-响应协议,每次请求需要重新建立连接,而 WebSocket 是一种双向通信协议,使用长连接实现数据实时推送。
- 数据传输方式不同: HTTP 协议中的数据传输是文本格式的,而 WebSocket 可以传输文本和二进制数据。
- 通信类型不同: HTTP 主要用于客户端和服务器之间的请求和响应,如浏览器请求网页和服务器返回网页的 HTML 文件。WebSocket 可以实现双向通信,常常用于实时通信场景。
- 性能方面不同 : 由于 HTTP 的每次请求都需要建立连接和断开连接,而 WebSocket 可以在一次连接上进行多次通信,WebSocket 在性能上比 HTTP 有优势。
WebSocket与Socket的区别
相同点:都能实现双向通信
不同点 :
(1)WebSocket是一种协议,而Socket是一组接口;
(2)WebSocket是应用层协议,而Socket是位于传输层与应用层之间的抽象层;
(3)WebSocket在传输数据之前需要进行握手操作,而Socket不需要。
WebSocket和Socket 实现实时通信的区别:
协议:
Socket :Socket 是一个通用的、低级的网络通信协议,通常使用 TCP 或 UDP 协议。在实时聊天中,通常使用 TCP 协议,因为它提供可靠的、面向连接的通信,确保消息不会丢失和按顺序传递。Socket 通信需要开发者自己定义和实现通信协议,包括消息的分割、序列化和解析等。
WebSocket :WebSocket 是一种高级的、基于 HTTP 的协议 ,专门设计用于实时双向通信。WebSocket 基于标准的握手协议,在客户端和服务器之间建立持久性的双向通信通道,使消息的传递更加简单和高效。WebSocket 协议已经定义了消息的帧格式,因此开发者不需要自己实现消息的分割和解析。
实现复杂性:
Socket:使用原始的 Socket 协议通常需要更多的开发工作和处理复杂性。你需要管理连接、处理网络中断、实现消息队列、处理并发连接等。这需要更多的代码和测试来确保稳定性和可靠性。
WebSocket :WebSocket 提供了更高级别的抽象,隐藏了许多底层细节,使实时聊天应用程序的开发更加简单。大多数现代编程语言和框架都提供了 WebSocket 客户端和服务器库,因此你可以更容易地构建实时聊天应用。
适用场景:
Socket :Socket 适用于需要细粒度控制和自定义通信协议的应用,以及对于性能和资源利用率 有更高要求的场景。它可以用于游戏服务器、传感器数据传输等。
WebSocket:WebSocket 适用于大多数实时聊天应用,包括在线聊天、即时消息传递、在线游戏聊天等。它提供了一种简单、可靠且高效的方式来实现实时通信,而不需要过多的低级编程。
综上所述,WebSocket 通常是实时聊天应用的更好选择,因为它提供了更高级别的抽象,使开发更容易,同时提供了可靠的双向通信。然而,Socket 可以在特定情况下提供更多的控制和性能优势。选择使用哪种方法取决于你的应用需求和技术栈。在现代开发中,WebSocket 是实现实时聊天的常见选择之一。
WebSocket 和 TCP 的区别
WebSocket 和 HTTP 都是基于 TCP 协议的应用层协议。
层次结构: WebSocket 是应用层协议,而 TCP 是传输层协议。
协议特点: TCP 是一种面向连接的协议,使用三次握手建立连接,提供可靠的数据传输。而 WebSocket 是一种无状态的协议,使用 HTTP 协议建立连接,可以进行双向通信,WebSocket 的数据传输比 TCP 更加轻量级。
数据格式: TCP 传输的数据需要自定义数据格式,而 WebSocket 可以支持多种数据格式,如 JSON、XML、二进制等。WebSocket 数据格式化可以更好的支持 Web 应用开发。
连接方式: TCP 连接的是物理地址和端口号,而 WebSocket 连接的是 URL 地址和端口号。
硬件开发相关
App 与外设间的通信方式: iOS - App 与外设间的通信方式-阿里云开发者社区
App 与外设间的通信方式:
- 通过网络端口通信:Socket开发使用CocoaAsyncSocket (iOS | Socket & CocoaAsyncSocket介绍与使用 - 简书)、Wi-Fi 连接、USB 热点共享连接、NCM 连接
- **通过 EAP 方式通信,**EAP 全拼是 External Accessory Protocol,外部设备协议。这个是苹果推荐使用的外设连接方式。需要外设集成 MFi 芯片进行 MFi 认证。
- 通过 BLE 方式通信
蓝牙开发
应用场景:门禁、智能手环、智能家居
可以先用LightBlue App 搜索查看设备服务
基本概念
- 中心设备(central):是发起连接的主设备,如:App。
- 外设(peripheral):被连接的设备称为外部设备,即外设, 如:体脂称,健康手环等;
- 服务(CBService): 外设可以包含一个或多个服务,它是用于实现装置的功能或特征数据相关联的行为集合。
- 特征(CBCharacteristic):每个服务可以对应多个特征,它是提供外设服务进一步的细节。
中心者模式
需要使用到CoreBluetooth.framework中的CBCentralManager、CBPeripheral两个类,分别用来连接蓝牙和读写蓝牙传递的数据。
**步骤:**开启蓝牙->搜索固定标示的蓝牙设备->连接蓝牙->向蓝牙模块写入加密指令->蓝牙模块将指令信息输出到硬件主板->执行具体操作
注:为了用户有更好的使用体验,这里的连接蓝牙是自动连接即连接时无需授权(蓝牙模块开发人员处理),且为了保证蓝牙连接的稳定性我们使用的蓝牙最大连接数为一个。
外设模式
需要用到CoreBluetooth.framework中的CBPeripheralManager类,把手机作为蓝牙广播包的发送者。
**步骤:**开启蓝牙->将带有固定标示的加密数据写入广播包的CBAdvertisementDataLocalNameKey中->硬件中的蓝牙模块识别到该数据包并将相关数据输出到主板->解密数据并执行相应操作
注:这个方式需要和蓝牙模块开发人员、硬件开发人员共同制定数据传输协议
蓝牙获取电量
低功耗蓝牙
物联网
WLAN: A wireless local-area network无线局域网。
WLAN物联网,以Wi-Fi、蓝牙、Zigbee、Z-wave等技术为代表,是一种覆盖范围较小的物联网络。
LPWAN: Low Power Wide Area Network低功耗广域网, 名字里就有它的两个最重要的特点:低功耗、广覆盖。
LPWAN物联网,也是包括很多技术标准。目前比较主流的有:NB-IoT、LoRa、Sigfox、eMTC。
NB-IoT:
Narrow Band Internet of Things, NB-IoT窄带物联网,成为万物互联网络的一个重要分支。NB-IoT构建于蜂窝网络,只消耗大约180kHz的带宽,可直接部署于GSM网络、UMTS网络或LTE网络,以降低部署成本、实现平滑升级。
NB-IoT是IoT领域一个新兴的技术,支持低功耗设备在广域网的蜂窝数据连接,也被叫作低功耗广域网(LPWAN)。NB-IoT支持待机时间长、对网络连接要求较高设备的高效连接。据说NB-IoT设备电池寿命可以提高至少10年,同时还能提供非常全面的室内蜂窝数据连接覆盖。
**应用场景:**智慧锁、智慧城市、智慧水表、智慧气表、智慧跟踪器、智慧仓储、智慧路灯...它们都是把原始的数据诉求给平台方,云平台通过NB模块实现数据的整合,传递给人类进行有效数据的分析使用。
OneNET :中国移动通信集团推出的第一个也是唯一一个专业的物联网开放云平台