iOS 性能、架构、socket 小结

腾讯 – 酷狗音乐 收集

一、直播相关技术

直播相关点击跳转

二、性能优化

性能优化,我将其分为方向:

  • ① 操作流畅性(用户可感知)
  • ② APP 大小瘦身
  • ③ APP自身稳定健壮性(用户很少基本不感知)

1、操作追求流畅性(用户可感知优化:CPU、GPU入手)

  1. 启动时间优化:pre_main 和 main 后优化

    1. pre_main 优化主要由4部分组成:
      1. dylib loading(动态库的加载):
        这个阶段 dylib 会分析应用依赖的 dylib。由此可知: 应用依赖的 dylib 越少越好。在这一步优化的宗旨是减少 dylib 数量:

        • 移除不必要的 dylib ;
        • 合并多个 dylib 成一个 dylib 。
      2. rebase/binding
        这个阶段主要是注册 Objc 类。所以指针数量越少越好。可做的优化有:

        • 清理项目中无用的类
        • 删减没有被调用到或者已经废弃的方法
        • 删减一些无用的静态变量
        • 可以通过 AppCode 等工具实现项目中未使用的代码扫描
      3. ObjeC setup
        这个阶段基本不用优化。若 rebase/binding 阶段优化很好,本阶段耗时也会很少

      4. initializer

        • 在这个阶段,dylib 开始运行程序的初始化函数,调用每个类和分类的 + load() 方法,调用 C/C++ 中的构造器函数。 initializer 阶段执行结束后, dylib 开始调用 main() 函数。在这一步,检查 + load() 方法,尽量把事情推迟到 + initialize() 方法里执行;并且控制 category 数量,去掉不必要的 category。
        • 在这里我们修改了部分原本代码中直接在 +load 函数初始化逻辑改为在 +initialize 中加载,也就是到使用时才加载。
    2. Main() 后优化
      1. didFinishLaunchingWithOptions 优化
        • 业务分级(三级:启动必须注册、加载首页后注册、懒加载注册)。
        • 目前 App 的 didFinishLaunchingWithOptions 方法里执行了多项项业务,有一大部分业务并不是一定要在这里执行的,如支付配置、客服配置、分享配置等。整理该方法里的业务,能延迟加载的就往后推迟,防止其影响启动时间。
        • 可以新建一个类负责启动事件,新加的业务可以往这边添加。
        • 不必要不开多线程
      2. 首页渲染优化
        • 减少启动期间创建的 UIViewController 数量。
          通过打符号断点-[UIViewController viewDidLoad] 发现,如果App 启动过程中创建了 12 个 UIViewController(包括闪屏),即在启动过程中创建了 12 个视图控制器,导致首页渲染时间较长。
        1. 延迟首页耗时操作。
          如果 App 首页有个侧滑页面及侧滑手势,并且该页面是用 xib 构建的,将该 ViewController 改为代码构建,同时延迟该页面的创建时机,等首页显示后再创建该页面及侧滑手势,这个改动节省了 300-400ms。
        2. 去除启动时没必要及不合理的操作。
          项目中使用了自定义的侧滑返回,在每次 push 的时候都会截图,启动的时候自定义导航栏会截取两张多余首页的图片,并且截图用的 API (renderInContext) 性能较差,耗时 800ms 左右,去掉启动截图的操作。
          闪屏请求回调里写plist文件的操作放在主线程,导致启动时占用主线程,将文件读写移到子线程操作。
  2. 网络优化

    • 减少、压缩网络数据。
    • 如果多次请求的结果是相同的,尽量使用缓存。
    • 使用断点续传,否则网络不稳定时可能多次传输相同的内容。
    • 网络不可用时,不要尝试执行网络请求。
    • 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。
    • 批量传输,比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果下载广告,一次性多下载一些,然后再慢慢展示。如果下载电子邮件,一次下载多封,不要一封一封地下载。
  3. 页面打开优化

    • 跳转优化(跳转动画、跳转时间)
    • 展示状态优化(请求数据中,请求成功、请求失败)
  4. UI 卡顿优化

    1. 列表优化
      • 复用机制
      • 高级计算提前计算 + 高度缓存
      • 善用 hidden,减少 cell 的注册。
      • 按需加载
    2. 耗时操作尽量放入子线程
    3. 控制多线程并发量
    4. 图片异步加载(解码、绘制)
    5. 大图加载:考虑缓存多图合成
    6. 减少视图数量和层次 (subViews 的数量)
    7. 减少透明的视图(alpha<1),不透明的就设置opaque为YES
    8. 避免不需要动画
    9. 如有需要可使用异步进行 layer 渲染(AsyncDisplayKit 又叫 Texttrue)
    10. 避免离屏渲染:需要创建新的缓冲区,需要在当屏和离屏直接切换。如下会导致离屏:
      • 光栅化,layer.shouldRasterize = YES
      • 遮罩,layer.mask
      • 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0
      • 考虑通过CoreGraphics绘制裁剪圆角,或者叫美工提供圆角图片
      • 阴影,layer.shadowXXX 、如果设置了layer.shadowPath就不会产生离屏渲染
    11. 友好的操作化提示、引导

2、App 瘦身优化(大小)

  1. 资源优化

    • 删除无用图片
      使用 LSUnusedResources 查找无用图片。注意 [UIImage imageNamed:[NSString stringWithFormat:“icon_%d.png”,index]]; 这种使用图片的方式,可能会被误删。

    • 删除不使用重复资源:xib、Json、Plist、Extension 等

    • 压缩图片资源

      • 使用 ImageOptim 无损压缩图片。
      • 使用 TinyPNG 有损压缩图片。使用的时候直接执行 tinypng *.png -k token 脚本即可。
    • 其他技巧:

      • 用 LaunchScreen.storyboard 替换启动图片。
      • 本地大图片都使用 webp。
      • 资源按需加载(苹果提供),非必要资源都等到使用时再从服务端拉取。
  2. 编译选项优化:

    • 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。
  3. 可执行文件优化:
    使用 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
    1. 苹果官方的策略:
      • 使用 xcasset 管理图片
      • 开启 BitCode

3. APP自身稳定健壮性(用户很少基本不感知)

  1. 内存优化
  2. 耗电优化
    • InstrumentsEnergy Log 查看电量使用情况
    • 耗电分析:
      • 地图定位:是否需要实时?定位精确度需求?定位完毕就关掉定位服务?后台定位设置?
      • 网络传输:没有网络状态下不进行请求!
      • 后台运行:是否需要后台运行?运行方案?保活多久?
      • CPU 调度频率:多线程超量?离屏渲染操作?音视频编码使用了软编码?
      • 定时器的使用
      • 优化I/O操作
        • 尽量不要频繁的写入小数据,最好批量一次写入。
        • 读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的的API。用dispatch_io系统会优化磁盘访问。
        • 数据量比较大时,建议使用数据库(比如SQLite、CoreData)
  3. 奔溃相关预防处理
    看第四点:如下

4. APP奔溃相关(属于第三点内容)

  • 奔溃收集步骤:

    1. 捕获崩溃
    2. 获取堆栈信息
    3. 符号表还原
    4. 服务器上传
  • 奔溃收集原理
    iOS中引发崩溃的代码本质上就类,信号可捕捉的崩溃和信号不可捕捉崩溃。

    1. 信号可捕捉的崩溃:

      • 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和 callstacksignal的堆栈的处理就比较麻烦些;另外,还就是需要分析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。

    2. 信号不可捕捉崩溃:

      1. 后台任务超时
      2. App超过系统限制的内存大小被杀死
      3. 主线程卡顿被杀死
    3. 符号表还原
      符号表还原包括系统符号表App自己的符号表这两类。

      • 系统符号表:比较坑爹,需要有不同系统版本的手机,分别拿到他们的系统符号表,解析出来,没有找到一个Apple提供的统一系统symbol文件下载地址;
      • App的符号表:Build的时候都会生成。通过分析dwarf文件架构,解析出对应的符号表,然后和第二步的结果比照还原。
  • 崩溃率范围
    奔溃率评判标准

  • 奔溃处理预防:

    1. unrecognized selector crash :(没找到对应的函数)
      利用runtime 转发机制在快读转发方法 forwardingTargetForSelector: 中做处理。
      第一步:为类动态的创建一个桩类(NSObject);
      第二步:为类动态为桩类添加对应的Selector,用一个通用的返回0的函数来实现该SEL的IMP;
      第三步:将消息直接转发到这个桩类对象上;
      第四步:在AppDelegate调用替换的方法类 [NSObject xxx]

    2. KVO crash
      现在当被观察者dealloc的时候还被监听着,并不会产生Crash。但是,重复移除观察者或者KVO注册观察者与移除观察者不匹配还是会产生Crash的,不过这两种情景在开发过程中很容易就被发现了,所以,没有必要再做防护了。
      我们可以使用借鉴 KVOController 。
      通过runtime特性对addObserver:forKeyPath:options:context:removeObserver:forKeyPath:方法做替换,避免下面几种情况:
      添加观察者时:通过关系哈希表判断是否重复添加,只添加一次。
      移除观察者时:通过关系哈希表是否已经进行过移除操作,避免多次移除。
      观察键值改变时:同样通过关系哈希表判断,将改变操作分发到原有的观察者上。

    3. 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函数了。

    4. 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。
    5. Container类型crash : 数组,字典,常见的越界,插入,nil
      这个问题,就是运用方法替换进行,目前对以下类进行防范,类中可能导致 crash 的方法,逐步进行增量扩充。
      进行 category 防护。

    6. 野指针类型的crash :
      原因:访问的对象已经被释放,变成了野指针。比如用assign修饰代理属性;
      解决:Debug阶段开启僵尸模式,Release时关闭僵尸模式

    7. 非主线程刷UI类型:(UIKit Called on Non-Main Thread)
      通过runtime的方法替换,替换UIView 的 setNeedsLayoutlayoutIfNeededlayoutSubviewssetNeedsUpdateConstraints。方法,判断当前线程是否为主线程,如果不是,在主线程执行。

      - (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、项目大局观:组件化选择

  1. Url-Scheme 注册:MGJRouter
  2. Target-Action : CTMediator 推荐使用,中间件模式,使用 targetcategory 实现解耦
  3. Protocol-Class 注册 :

2、项目内部:MVC、MVP、MVVP ?

3、什么是设计模式?设计模式解决了什么问题?

在编写程序中,面临着耦合性、内聚性、可维护性、可扩展性、重用性、灵活性等挑战。设计模式就是为了让程序拥有更好的:

  1. 代码重用性(相同功能,抽象剥离)
  2. 可读性(编程规范性,好的代码即使不注释都可以很好的阅读)
  3. 可扩展性(增加新功能的时候方便)
  4. 可靠性(增加新功能,不会对元功能造成影响)
  5. 实现高内聚,低耦合特性(public 暴露尽量的少)

4、设计模式遵循的 7 大原则?

  • 单一职责原则:一个类只负责一个职责,一个方法函数只解决一个问题。
  • 接口隔离原则:大接口改小接口。(外部不需要大接口那么多方法,更易控制)。
  • 依赖反转原则:面向接口编程。尽量不要声明具体类,而是使用接口,实现解耦。
  • 里氏置换原则:能出现父类的地方就一定可以用子类代替。即不要重写父类中已实现的方法。
  • 开闭原则:面向扩展开放,面向修改关闭。不要修改一个已实现的,更不要修改内的方法,应当创建新类和新方法来解决。
  • 迪米特原则:最少知道原则。即对外暴露的public 方法尽量少,实现高内聚。
  • 合成复用原则:不要重复自己,不要copy 代码。应当选择抽象需要copy 的代码,实现多类复用。

5、常见的设计模式有哪些?使用场景?

6、项目稳定性?

  1. 开发流程规范化
    • 代码规范

      • 自测习惯(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协议的网络通信的基本操作单元,包含进行网络通信的必须的种信息:

  1. 连接使用的协议
  2. 本地主机的IP地址
  3. 本地进程的协议端口
  4. 远程主机的IP地址
  5. 远程进程的协议端口
特性:
  • Socket可以支持不同的传输协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接;同理,当使用UDP协议进行连接时,该Socket连接就是一个UDP连接。
  • 多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,计算机操作系统为应用程序TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
连接

建立Socket连接至少需要一对套接字,分别运行于服务端(ServerSocket)和客户端(ClientSocket)。套接字直接的连接过程氛围三个步骤:

  1. 服务器监听:
    服务端Socket始终处于等待连接状态,实时监听是否有客户端请求连接。
  2. 客户端请求
    客户端Socket提出连接请求,指定服务端Socket的地址和端口号,这时就可以向对应的服务端提出Socket连接请求。
  3. 连接确认
    当服务端Socket监听到客户端Socket提出的连接请求时作出响应,建立一个新的进程,把服务端Socket的描述发送给客户端,该描述得到客户端确认后就可建立起Socket连接。而服务端Socket则继续处于监听状态,继续接收其他客户端Socket的请求。
1. 数据粘包、半包问题解决方案?
  1. 特殊字符串比如/r、/n作为数据的结尾,这样就可以区分数据包了
  2. 发送请求包的时候只发送固定长度的数据包,这样在服务端接收数据也只接收固定长度的数据,这种方法效率太低,不太合适频繁的数据包请求
  3. tcp协议的基础上封装一层数据请求协议既数据包 = 数据包长度 + 数据包内容,这样在服务端就可以知道每个数据包的长度,也就可以解决半包、粘包问题。
2. TCP协议如何来保证传输的数据的顺序性?
  • 主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认,如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。

  • 接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等。接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。

  • 具体步骤如下:

    1. 为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区
    2. 并为每个已发送的数据包启动一个超时定时器
    3. 如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区;
    4. 否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。
    5. 接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。

2、多线程 包含多线程模块

3、跨平台

  1. Flutter
  2. RN
  3. Weex
  4. H5 框架

4、OC/Swift 包含了大部分

5、数据库

因为是 iOS, 所以数据库方面没有储备,需要后续学习。

5、CocoaPods

线路清华线路、git 线路、国外线路、淘宝线路
自己生成 pod 库
  1. 创建库名称
     # 创建库名称
    pod lib create xxx
    
  2. 执行完上述命令之后,它会问你几个问题,按需求填写即可
    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
    
  3. 添加库文件:功能撰写 修改 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
    
  4. 项目发布: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上查看是否已经推送上去了。
    
  5. 尝试使用: 我们新建一个项目并引用我们的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 => true
      pod 'JCNetworking',:git =>"https://github.com/huqigu/JCNetworking.git"
      # Pods for TestSDK
      target 'TestSDKTests' do
        inherit! :search_paths
        # Pods for testing
      end
      target 'TestSDKUITests' do
        inherit! :search_paths
        # Pods for testing
      end
    end
    
  6. 更新:将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
    // 出现如下信息表示上传成功
    🎉  Congrats
     🚀  JCNetworking (0.1.0) successfully published
     📅  February 27th, 21:23
     🌎  https://cocoapods.org/pods/JCNetworking
     👍  Tell your friends!
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值