iOS 性能、架构、socket 小结
腾讯 – 酷狗音乐 收集
一、直播相关技术
直播相关点击跳转
二、性能优化
性能优化,我将其分为三
方向:
- ① 操作流畅性(用户可感知)
- ② APP 大小瘦身
- ③ APP自身稳定健壮性(用户很少基本不感知)
1、操作追求流畅性(用户可感知优化:CPU、GPU入手)
启动时间优化:
pre_main 和 main 后优化- pre_main 优化主要由4部分组成:
dylib loading(动态库的加载)
:
这个阶段 dylib 会分析应用依赖的 dylib。由此可知: 应用依赖的 dylib 越少越好。在这一步优化的宗旨是减少 dylib 数量:- 移除不必要的 dylib ;
- 合并多个 dylib 成一个 dylib 。
rebase/binding
:
这个阶段主要是注册 Objc 类。所以指针数量越少越好。可做的优化有:- 清理项目中无用的类
- 删减没有被调用到或者已经废弃的方法
- 删减一些无用的静态变量
- 可以通过 AppCode 等工具实现项目中未使用的代码扫描
ObjeC setup
:
这个阶段基本不用优化。若 rebase/binding 阶段优化很好,本阶段耗时也会很少initializer
:- 在这个阶段,dylib 开始运行程序的初始化函数,调用每个
类和分类的 + load() 方法
,调用C/C++ 中的构造器函数
。 initializer 阶段执行结束后, dylib 开始调用 main() 函数。在这一步,检查 + load() 方法,尽量把事情推迟到 + initialize() 方法里执行
;并且控制 category 数量,去掉不必要的 category。 - 在这里我们修改了部分原本代码中直接在 +load 函数初始化逻辑改为在 +initialize 中加载,也就是到使用时才加载。
- 在这个阶段,dylib 开始运行程序的初始化函数,调用每个
Main() 后优化
didFinishLaunchingWithOptions
优化- 业务分级(三级:启动必须注册、加载首页后注册、懒加载注册)。
- 目前 App 的 didFinishLaunchingWithOptions 方法里执行了多项项业务,有一大部分业务并不是一定要在这里执行的,如支付配置、客服配置、分享配置等。整理该方法里的业务,能延迟加载的就往后推迟,防止其影响启动时间。
- 可以新建一个类负责启动事件,新加的业务可以往这边添加。
- 不必要不开多线程
- 首页渲染优化 :
- 减少启动期间创建的 UIViewController 数量。
通过打符号断点-[UIViewController viewDidLoad] 发现,如果App 启动过程中创建了 12 个 UIViewController(包括闪屏),即在启动过程中创建了 12 个视图控制器,导致首页渲染时间较长。
- 延迟首页耗时操作。
如果 App 首页有个侧滑页面及侧滑手势,并且该页面是用 xib 构建的,将该 ViewController 改为代码构建,同时延迟该页面的创建时机,等首页显示后再创建该页面及侧滑手势,这个改动节省了 300-400ms。 - 去除启动时没必要及不合理的操作。
项目中使用了自定义的侧滑返回,在每次 push 的时候都会截图,启动的时候自定义导航栏会截取两张多余首页的图片,并且截图用的 API (renderInContext) 性能较差,耗时 800ms 左右,去掉启动截图的操作。
闪屏请求回调里写plist文件的操作放在主线程,导致启动时占用主线程,将文件读写移到子线程操作。
- 减少启动期间创建的 UIViewController 数量。
- pre_main 优化主要由4部分组成:
网络优化
- 减少、压缩网络数据。
- 如果多次请求的结果是相同的,尽量使用缓存。
- 使用断点续传,否则网络不稳定时可能多次传输相同的内容。
- 网络不可用时,不要尝试执行网络请求。
- 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。
- 批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载。
页面打开优化
- 跳转优化(跳转动画、跳转时间)
- 展示状态优化(请求数据中,请求成功、请求失败)
UI 卡顿优化
- 列表优化
- 复用机制
- 高级计算提前计算 + 高度缓存
- 善用 hidden,减少 cell 的注册。
- 按需加载
- 耗时操作尽量放入子线程
- 控制多线程并发量
- 图片异步加载(解码、绘制)
- 大图加载:考虑缓存多图合成
- 减少视图数量和层次 (subViews 的数量)
- 减少透明的视图(alpha<1),不透明的就设置opaque为YES
- 避免不需要动画
- 如有需要可使用异步进行 layer 渲染(AsyncDisplayKit 又叫 Texttrue)
避免离屏渲染
:需要创建新的缓冲区,需要在当屏和离屏直接切换。如下会导致离屏:- 光栅化,layer.shouldRasterize = YES
- 遮罩,layer.mask
- 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0
- 考虑通过CoreGraphics绘制裁剪圆角,或者叫美工提供圆角图片
- 阴影,layer.shadowXXX 、如果设置了layer.shadowPath就不会产生离屏渲染
友好的操作化提示、引导
- 列表优化
2、App 瘦身优化(大小)
资源优化
:删除无用图片
使用LSUnusedResources
查找无用图片。注意 [UIImage imageNamed:[NSString stringWithFormat:“icon_%d.png”,index]]; 这种使用图片的方式,可能会被误删。删除不使用重复资源:xib、Json、Plist、Extension 等
压缩图片资源
- 使用 ImageOptim 无损压缩图片。
- 使用 TinyPNG 有损压缩图片。使用的时候直接执行 tinypng *.png -k token 脚本即可。
其他技巧:
- 用 LaunchScreen.storyboard 替换启动图片。
- 本地大图片都使用 webp。
- 资源按需加载(苹果提供),非必要资源都等到使用时再从服务端拉取。
编译选项优化:
- Optimization Level 在 release 状态设置为 Fastest/Smallest。
- Strip Debug Symbols During Copy 在 release 状态设置为 YES。
- Strip Linked Product 在 release 状态设为 YES。
- Make String Read-Only 在 release 状态设为 YES。
- Dead Code Stripping 在 release 状态设为 YES。
- Deployment PostProcessing 在 release 状态设为 YES。
- Symbols hidden by default 在 release 状态设为 YES。
可执行文件优化:
使用LinkMap
分析库的使用情况三方库优化
- 删除不使用的三方库。
- 功能用的少但是体积大的三方库可以考虑自己重写。
- 合并功能重复的三方库。
代码分析:用 AppCode 进行代码扫描
- 去掉无用的类及文件
- 清理 import
- 去掉空方法
- 去掉无用的 log
- 去掉无用的变量
其他技巧(选用):
将业务打包成动态库。
如果动态库的加载时机不控制好,会影响 App 的启动速度,权衡使用。动态化
。将一部分 Native 界面用 RN/Weex 重写。去除 Swift 代码
,Swift 的标准库是打包在安装包里的,一般都有 10M+。然后苹果官方说等到 Swift Runtime 稳定之后会合并到 iOS 系统里,那时候使用 Swift 就不会显著增加包大小了。- 在 target -> Build Settings -> Other Link Flags 里添加如下指令,会把 TEXT 字段的部分内容转移到 RODATA 字段,避免苹果对 TEXT 字段的审核限制。当然其实跟安装包瘦身好像没有什么关系,所以除非快不行了否则不建议操作。
-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring -Wl,- rename_section,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab -Wl,-rename_section,__TEXT,__const,__RODATA,__const -Wl,-rename_section,__TEXT,__objc_methname,__RODATA,__objc_methname -Wl,-rename_section,__TEXT,__objc_classname,__RODATA,__objc_classname -Wl,-rename_section,__TEXT,__objc_methtype,__RODATA,__objc_methtype
苹果官方的策略:
- 使用 xcasset 管理图片
- 开启 BitCode
3. APP自身稳定健壮性(用户很少基本不感知)
- 内存优化
- 耗电优化
Instruments
的Energy Log
查看电量使用情况- 耗电分析:
- 地图定位:是否需要实时?定位精确度需求?定位完毕就关掉定位服务?后台定位设置?
- 网络传输:没有网络状态下不进行请求!
- 后台运行:是否需要后台运行?运行方案?保活多久?
- CPU 调度频率:多线程超量?离屏渲染操作?音视频编码使用了软编码?
- 定时器的使用
- 优化I/O操作:
- 尽量不要频繁的写入小数据,最好批量一次写入。
- 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的的API。用dispatch_io系统会优化磁盘访问。
- 数据量比较大时,建议使用数据库(比如SQLite、CoreData)
- 奔溃相关预防处理
看第四点:如下
4. APP奔溃相关(属于第三点内容)
奔溃收集步骤:
- 捕获崩溃
- 获取堆栈信息
- 符号表还原
- 服务器上传
奔溃收集原理
:
iOS中引发崩溃的代码本质上就两
类,信号可捕捉
的崩溃和信号不可捕捉
崩溃。信号可捕捉的崩溃:
unrecognized selector crash
(没找到对应的函数)KVO crash
:(KVO的被观察者dealloc时仍然注册着KVO导致的crash,添加KVO重复添加观察者或重复移除观察者 )NSNotification crash
:(当一个对象添加了notification之后,如果dealloc的时候,仍然持有notification)NSTimer类型crash
:(需要在合适的时机invalidate 定时器,否则就会由于定时器timer强引用target的关系导致 target不能被释放,造成内存泄露,甚至在定时任务触发时导致crash)
Container类型crash:(数组,字典,常见的越界,插入,nil)野指针类型的crash
非主线程刷UI类型
:(在非主线程刷UI将会导致app运行crash)
NSException
的异常比较简单,直接获取崩溃name,reason和 callstack;signal的堆栈
的处理就比较麻烦些;另外,还就是需要分析APP的当前线程信息
以及所有的线程信息
或者更加深一步的寄存器信息
。不过这个时候拿到的堆栈是地址的形式,还需要第三步的符号表
还原功能才能定位到代码行号。
iOS中引发崩溃的代码本质上就两
类。- 一个是
c++语言层面
的错误,属于比较底层的错误。比如:野指针,除零,内存访问异常等等,这一类的错误可以通过信号机制来捕获
(signal或者是sigaction),即任何系统错误都会抛出一个错误信号,我们可以通过设定一个回调函数,然后在回调函数里面进行自己的处理; 另一类是未捕获异常(Uncaught Exception)
,iOS下面最常见的就是 objective-c 的 NSException
。比如:数组访问元素越界。这些异常如果没有在最上层try住,那么程序就崩溃了。
结果:
针对NSException
的捕获,通过调用NSSetUncaughtExceptionHandler
来捕获,系统错误通过注册signal来捕获,一般产生一个NSException的异常的时候,同时也会抛出一个signal的信号。
如果要做得全面,除了处理Object-C项目,还需要处理Swift语言编写的项目,另外还有就是U3D的工程,移动游戏开发很多都是使用这个。
注意:
在分析堆栈信息时需要区分32位和64位,以及分别处理真机和模拟器的堆栈,因为一个是arm架构,一个是x86_ 64。信号
不可捕捉
崩溃:- 后台任务超时
- App超过系统限制的内存大小被杀死
- 主线程卡顿被杀死
符号表还原
符号表还原包括系统符号表
和App自己的符号表
这两类。- 系统符号表:比较坑爹,需要有不同系统版本的手机,分别拿到他们的
系统符号表
,解析出来,没有找到一个Apple提供的统一系统symbol文件下载地址; - App的符号表:
Build
的时候都会生成。通过分析dwarf
文件架构,解析出对应的符号表,然后和第二步的结果比照还原。
- 系统符号表:比较坑爹,需要有不同系统版本的手机,分别拿到他们的
崩溃率范围
:
奔溃处理预防:
unrecognized selector crash
:(没找到对应的函数)
利用runtime
转发机制在快读转发方法forwardingTargetForSelector:
中做处理。
第一步
:为类动态的创建一个桩类(NSObject);
第二步
:为类动态为桩类添加对应的Selector,用一个通用的返回0的函数来实现该SEL的IMP;
第三步
:将消息直接转发到这个桩类对象上;
第四步
:在AppDelegate
调用替换的方法类[NSObject xxx]
。KVO crash
:
现在当被观察者dealloc
的时候还被监听着,并不会产生Crash
。但是,重复移除观察者或者KVO注册观察者与移除观察者不匹配还是会产生Crash
的,不过这两种情景在开发过程中很容易就被发现了,所以,没有必要再做防护了。
我们可以使用借鉴 KVOController 。
通过runtime
特性对addObserver:forKeyPath:options:context:
、removeObserver:forKeyPath:
方法做替换,避免下面几种情况:
添加观察者时:通过关系哈希表判断是否重复添加,只添加一次。
移除观察者时:通过关系哈希表是否已经进行过移除操作,避免多次移除。
观察键值改变时:同样通过关系哈希表判断,将改变操作分发到原有的观察者上。NSNotification crash
:
主要针对iOS9系统之前不移除通知。 苹果在iOS9之后专门针对于这种情况做了处理,所以在iOS9之后,即使开发者没有移除observer,Notification crash也不会再产生了。
hook NSObject的dealloc
函数,在对象真正dealloc之前先调用一下
[[NSNotificationCenter defaultCenter] removeObserver:self]
即可。
注意
:并不是所有的对象都需要做以上的操作,如果一个对象从来没有被NSNotificationCenter 添加为observer的话,在其dealloc
之前调用removeObserver完全是多此一举。 所以我们hook了NSNotificationCenter的addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject函数,在其添加observer的时候,对observer动态添加标记flag。这样在observer dealloc的时候,就可以通过flag标记来判断其是否有必要调用removeObserver函数了。NSTimer 类型 crash
:
使用NSTimer的scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:接口做重复性的定时任务时存在一个问题:NSTimer会强引用target实例,所以需要在合适的时机invalidate定时器,否则就会由于定时器timer强引用target的关系导致target不能被释放,造成内存泄露,甚至在定时任务触发时导致crash。 crash的展现形式和具体的target执行的selector有关。与此同时,如果NSTimer是无限重复的执行一个任务的话,也有可能导致target的selector一直被重复调用且处于无效状态,对app的CPU,内存等性能方面均是没有必要的浪费。- swizzle NSTimer的
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
相关的方法。
在新方法中动态创建stubTarget对象,stubTarget对象弱引用持有原有的target,selector,timer,targetClass等properties。然后将原target分发stubTarget上,selector回调函数为stubTarget的fireProxyTimer。 - 通过stubTarget的fireProxyTimer:来具体处理回调函数selector的处理和分发。
当NSTimer的回调函数fireProxyTimer:被执行的时候,会自动判断原target是否已经被释放,如果释放了,意味着NSTimer已经无效,此时如果还继续调用原有target的selector很有可能会导致crash,而且是没有必要的。所以此时需要将NSTimer invalidate,然后统计上报错误数据。如此一来就做到了NSTimer在合适的时机自动invalidate。
- swizzle NSTimer的
Container类型crash
: 数组,字典,常见的越界,插入,nil
这个问题,就是运用方法替换进行,目前对以下类进行防范,类中可能导致crash
的方法,逐步进行增量扩充。
进行 category 防护。野指针类型的crash :
原因:访问的对象已经被释放,变成了野指针。比如用assign修饰代理属性;
解决:Debug
阶段开启僵尸模式,Release
时关闭僵尸模式非主线程刷UI类型
:(UIKit Called on Non-Main Thread)
通过runtime
的方法替换,替换UIView 的setNeedsLayout
,layoutIfNeeded
,layoutSubviews
,setNeedsUpdateConstraints
。方法,判断当前线程是否为主线程,如果不是,在主线程执行。- (void)wt_safe_setNeedsLayout {if (![NSThread isMainThread]) {dispatch_async(dispatch_get_main_queue(), ^{NSAssert(false, @"wt_safe_setNeedsLayout failed");[self wt_safe_setNeedsLayout];});} else {[self wt_safe_setNeedsLayout];} }
多线程死锁:
原因:死锁(同步线程放到串行队列中)、子线程中更新UI、多个线程同时释放一个对象
三、架构设计
点击跳转包含了架构设计
1、项目大局观:组件化选择
- Url-Scheme 注册:MGJRouter
- Target-Action :
CTMediator
推荐使用,中间件模式,使用target
、category
实现解耦 - Protocol-Class 注册 :
2、项目内部:MVC、MVP、MVVP ?
3、什么是设计模式?设计模式解决了什么问题?
在编写程序中,面临着耦合性、内聚性、可维护性、可扩展性、重用性、灵活性
等挑战。设计模式就是为了让程序拥有更好的:
- 代码重用性(相同功能,抽象剥离)
- 可读性(编程规范性,好的代码即使不注释都可以很好的阅读)
- 可扩展性(增加新功能的时候方便)
- 可靠性(增加新功能,不会对元功能造成影响)
- 实现高内聚,低耦合特性(public 暴露尽量的少)
4、设计模式遵循的 7 大原则?
- 单一职责原则:一个类只负责一个职责,一个方法函数只解决一个问题。
- 接口隔离原则:大接口改小接口。(外部不需要大接口那么多方法,更易控制)。
- 依赖反转原则:面向接口编程。尽量不要声明具体类,而是使用接口,实现解耦。
- 里氏置换原则:能出现父类的地方就一定可以用子类代替。即不要重写父类中已实现的方法。
- 开闭原则:面向扩展开放,面向修改关闭。不要修改一个已实现的,更不要修改内的方法,应当创建新类和新方法来解决。
- 迪米特原则:最少知道原则。即对外暴露的public 方法尽量少,实现高内聚。
- 合成复用原则:不要重复自己,不要copy 代码。应当选择抽象需要copy 的代码,实现多类复用。
5、常见的设计模式有哪些?使用场景?
6、项目稳定性?
- 开发流程规范化
代码规范
自测习惯
(review、sonar 代码检测)- XMind、PDMan、PostMan、Jenkins、Sonar 等工具使用。养成先计划,再动手习惯。
- Git 、Svn 、禅道、TAPD等使用规范。保证项目流程的流畅性。
- 性能检测:FPS(CADisplayLink)
- CPU 使用率:
Instruments 中好几个 Time Profiler、Activity Monitor(监视CPU、内存、磁盘、网络使用情况)、Core Animation
音视频使用了软编码?
离屏渲染?
内存管理:
- Instruments 的 Leaks 监控内存状态;
- LinkMap 查看项目库、文件等使用情况
启动规划:
耗电规划:
测试(风险把控)
- 单元测试
- UI 测试
- 功能测试
- 异常测试
线上 bug 处理
- **监控:**异常监控、bug 流程跟踪、bug 保存、日志上传
- **修复:**JSPatch、RN、WEEX、Flutter、H5 跨平台
四、Socket、多线程、跨平台、OC/Swift、Flutter、数据库、Cocopods
1、Socket
定义:
网络上两个程序通过一个双向通信连接实现数据交互,这种双向通信的连接叫做Socket
(套接字)。
本质:
网络模型中应用层与TCP/IP协议族通信
的中间软件抽象层
,是它的一组编程接口(API)
,也即对TCP/IP的封装
。
TCP/IP
也要提供可供程序员做网络开发所用的接口,即Socket
编程接口。
要素:
Socket
是网络通信的基石
,是支持TCP/IP协议
的网络通信的基本操作单元
,包含进行网络通信的必须的五
种信息:
- 连接使用的协议
- 本地主机的IP地址
- 本地进程的协议端口
- 远程主机的IP地址
- 远程进程的协议端口
特性:
- Socket可以支持不同的传输协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接;同理,当使用UDP协议进行连接时,该Socket连接就是一个UDP连接。
- 多个TCP连接或多个应用程序进程可能需要通过
同一个TCP协议端口
传输数据。为了区别不同的应用程序进程和连接,计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
连接
建立Socket连接至少需要一对套接字
,分别运行于服务端(ServerSocket)和客户端(ClientSocket)。套接字直接的连接过程氛围三个
步骤:
- 服务器监听:
服务端Socket始终处于等待连接状态,实时监听是否有客户端请求连接。 - 客户端请求
客户端Socket提出连接请求,指定服务端Socket的地址和端口号,这时就可以向对应的服务端提出Socket连接请求。 - 连接确认
当服务端Socket监听到客户端Socket提出的连接请求时作出响应,建立一个新的进程,把服务端Socket的描述发送给客户端,该描述得到客户端确认后就可建立起Socket连接。而服务端Socket则继续处于监听状态,继续接收其他客户端Socket的请求。
1. 数据粘包、半包问题解决方案?
- 以特殊字符串比如/r、/n作为数据的结尾,这样就可以区分数据包了。
- 发送请求包的时候只发送固定长度的数据包,这样在服务端接收数据也只接收固定长度的数据,这种方法效率太低,不太合适频繁的数据包请求。
- 在tcp协议的基础上
封装一层数据请求协议
,既数据包 = 数据包长度 + 数据包内容
,这样在服务端就可以知道每个数据包的长度,也就可以解决半包、粘包问题。
2. TCP协议如何来保证传输的数据的顺序性?
主机每次发送数据时,TCP就给每个数据包分配一个
序列号
并且在一个特定的时间内
等待接收主机对分配的这个序列号进行确认
,如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。接收主机
利用序列号对接收的数据进行确认
,以便检测对方发送的数据是否有丢失或者乱序等。接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。具体步骤如下:
- 为了保证数据包的可靠传递,
发送方必须
把已发送的数据包保留在缓冲区
; - 并为每个已发送的数据包启动一个超时定时器;
- 如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
- 否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
- 接收方收到数据包后,先进行
CRC校验
,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
- 为了保证数据包的可靠传递,
2、多线程 包含多线程模块
3、跨平台
- Flutter
- RN
- Weex
- H5 框架
4、OC/Swift 包含了大部分
5、数据库
因为是 iOS, 所以数据库方面没有储备,需要后续学习。
5、CocoaPods
线路清华线路、git 线路、国外线路、淘宝线路
自己生成 pod 库
创建库名称
# 创建库名称 pod lib create xxx
- 执行完上述命令之后,它会问你几个问题,按需求填写即可
What platform do you want to use?? [ iOS / macOS ]> iOS What language do you want to use?? [ Swift / ObjC ]> ObjC Would you like to include a demo application with your library? [ Yes / No ]> Yes Which testing frameworks will you use? [ Specta / Kiwi / None ]> None Would you like to do view based testing? [ Yes / No ]> Yes What is your class prefix?> JC
添加库文件:功能撰写
修改xxx.podspec
文件Pod::Spec.new do |s| # 名称s.name = 'xxx' # 默认版本号s.version = '0.1.0' # 简短描述s.summary = 'A short description of JCNetworking.' # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? # * Try to keep it short, snappy and to the point. # * Write the description between the DESC delimiters below. # * Finally, don't worry about the indent, CocoaPods strips it!s.description = <<-DESC TODO: Add long description of the pod here.DESC # 主页s.homepage = 'https://github.com/huqigu/JCNetworking'# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2's.license = { :type => 'MIT', :file => 'LICENSE' }s.author = { 'huqigu' => 'huqigu@163.com' } # source路径s.source = { :git => 'https://github.com/huqigu/JCNetworking.git', :tag => s.version.to_s }# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' # 最低支持 iOS 版本 s.ios.deployment_target = '8.0' # 要封起来的文件路径s.source_files = 'JCNetworking/Classes/**/*'# s.resource_bundles = {# 'JCNetworking' => ['JCNetworking/Assets/*.png']# }# s.public_header_files = 'Pod/Classes/**/*.h'# s.frameworks = 'UIKit', 'MapKit' #尝试引入第三方依赖库s.dependency 'AFNetworking', '~> 2.3' end
项目发布
:cd到项目路径下执行git命令,将项目发布到上一步创建的github里。yellow@jiangchongdeMacBook-Pro ~ cd /Users/yellow/Desktop/JCNetworking // 添加github项目路径 yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master ● git remote add origin https://github.com/huqigu/JCNetworking.git // 添加文件 yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master ● git add . yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master ✚ git commit -m "first commit" # --allow-unrelated-histories # git pull origin maste会失败 ,提示:fatal: refusing to merge unrelated histories # 原因是远程仓库origin上的分支master和本地分支master被Git认为是不同的仓库,所以不能直接合并,需要添加 --allow-unrelated-histories yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master git pull origin master --allow-unrelated-histories // 推送到master分支 ✘ yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master git push origin master // 提交版本号并push #注意这里的版本号要与.podspec中的版本号保持一致yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master git tag 0.1.0 yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master git push origin 0.1.0 # 执行完上述命令之后可以去github上查看是否已经推送上去了。
尝试使用:
我们新建一个项目并引用我们的pod库 看看是否能成功pod下了。target 'TestSDK' do# Uncomment the next line if you're using Swift or would like to use dynamic frameworks# use_frameworks! 'jc',:inhibit_warnings => truepod 'JCNetworking',:git =>"https://github.com/huqigu/JCNetworking.git"# Pods for TestSDKtarget 'TestSDKTests' doinherit! :search_paths# Pods for testingendtarget 'TestSDKUITests' doinherit! :search_paths# Pods for testingend end
更新:将podspec推送到cocoapods仓库
// 邮箱为github绑定的邮箱,会发送一封带有链接的邮件,打开链接即完成注册 yellow@jiangchongdeMacBook-Pro ~ pod trunk register huqigu@163.com 'mxObject-c' --description='regist trunk' // 然后将仓库推送到cocoapods上 yellow@jiangchongdeMacBook-Pro ~/Desktop/JCNetworking master ● pod trunk push JCNetworking.podspec --allow-warnings // 出现如下信息表示上传成功
iOS 性能、架构、socket 小结相关推荐
- 淘宝拍立淘iOS相册架构设计小结
推荐语:这篇文章从系统权限.API 调用.架构设计等角度,生动演示了一个设计友好.模块独立.易拓展以及用户体验优秀的相册是如何开发出来的.除此之外,作者针对各种小细节也做了优化和解析,使得功能实现更加 ...
- 一.了解 iOS 操作系统架构
首先看一下Mac OS 和 iOS 系统架构的对比图: 从图上可以看出Mac OS和iOS的系统架构层次只有最上面一层不同,Mac是Cocoa框架,而iOS是Cocoa Touch框架,因此Mac O ...
- iOS应用架构谈 网络层设计方案--RTNetworking
iOS应用架构谈 开篇 iOS应用架构谈 view层的组织和调用方案 iOS应用架构谈 网络层设计方案 iOS应用架构谈 本地持久化方案及动态部署 iOS应用架构谈 组件化方案 https:/ ...
- iOS 性能监控(一)—— CPU功耗监控
前言 最近,在看戴铭老师关于 "性能监控" 相关的技术分享,感觉收获很多.基于最近的学习,总结了一些性能监控相关的实践,并计划落地一系列 "性能监控" 相关的文 ...
- 使用纯 python 实现 Instruments 协议,跨平台 (win,mac,linux) 获取 iOS 性能数据
原文由YueChen发表于TesterHome社区网站,点击原文链接可与YueChen交流. 前言 获取 iOS 性能数据,一直都是比较麻烦的事情,之前在构建测试框架&平台的时候,获取 iOS ...
- (转)iOS应用架构谈 本地持久化方案及动态部署
原文链接:http://casatwy.com/iosying-yong-jia-gou-tan-ben-di-chi-jiu-hua-fang-an-ji-dong-tai-bu-shu.html ...
- iOS应用架构谈 网络层设计方案
iOS应用架构谈 开篇 iOS应用架构谈 view层的组织和调用方案 iOS应用架构谈 网络层设计方案 iOS应用架构谈 本地持久化方案及动态部署 iOS应用架构谈 组件化方案 前言 网络层在 ...
- Nice是如何做iOS客户端架构的?
一个创业产品的iOS客户端架构到底怎么做呢?现下最有活力的图片社交软件Nice的技术负责人刘诗彬将为我们解答创业产品如何实现iOS客户端架构. 分享人:刘诗彬,毕业于北京邮电大学电子信息科学与技术专业 ...
- 新浪微博iOS客户端架构与优化之路
随着Facebook.Twitter.微博的崛起,向UGC.PGC.OGC,自媒体提供平台的内 容消费型App逐渐形成了独特的客户端架构模式.与电商和通讯工具类App不同,微博客户端具有多信息流.内容 ...
- 滴滴android架构演进,滴滴出行iOS客户端架构演进之路
自从蘑菇街的李忠老师在移动前线群里做了一次关于iOS组件化的分享之后,大家对于iOS客户端的架构非常感兴趣,展开了热烈的讨论.我很认同一句话,架构都是演变出来的,没有最好的架构,只有最合适的架构,刚好 ...
最新文章
- Go进阶(8): map嵌套的两轮初始化
- dqn在训练过程中loss越来越大_[动手学强化学习] 2.DQN解决CartPole-v0问题
- 直播丨抢鲜体验-openGauss入门
- SQL优化之一则MySQL中的DELETE、UPDATE 子查询的锁机制失效案例
- 【华为云技术分享】Sketch插件技术 — 让研发和设计高效协同
- 11.25晚C语言答疑
- hello word!------为什么开通博客以及自我介绍
- Unsafe in Java
- FOR XML PATH 应用及其反向分解
- ffmpeg推流到流媒体服务器
- 解析数论引论 第2章 数论函数和狄利克雷乘积
- 常用十六进制颜色对照表
- html 图片整体缩小,CSS实现图片等比例缩小不变形的实例代码
- Linux系统进程优先级——计算方式
- 在c语言中把x和y互换怎么编译,已知int x=10,y=12;编程将x和y的值相互交换
- PHP7 Windows10环境搭建
- 某大厂程序员炫耀:来新加坡后,每天最多工作五六个小时,家庭年收入150万人民币,已躺平!...
- 高等数学:第八章 多元函数的微分法及其应用(3)全微分
- java路由总线_网易考拉Android客户端路由总线设计
- x264中码率控制(一)
热门文章
- 【OpenBMC 系列】D-Bus 调试器介绍 - D-Feet
- 错误 C1189 #error MFC does not support WINVER less than 0x0501.
- python[爬虫]爬取百万条新浪新闻 新浪滚动新闻中心(多进程)
- [luoguT30208]太极剑
- Android系统的JNI原理分析(四)- JNI的jni.h头文件
- Grasshopper显示配色
- 怒怼外媒,为中国正名,这个《流浪地球》捧红的犹太小哥太励志了
- Qt 常见错误及坑锦集(更新中....)
- 【ITool】mybatis-generator-annotation
- C++大小写字母转换(齐全)
- 淘宝拍立淘iOS相册架构设计小结