前言
一个项目做大了,项目会很大,大家都去编译同一套代码,编译时间过长;
多人开发容易冲突;
更要命的是,修改一个地方,会牵扯到很多地方的修改,也就是项目耦合性太高。
为了解决这些问题,组件化不失为一种好的办法。
在学习之前,先抛几个问题,等学完后,看看自己能否解答
如何划分组件?
如何组织组件?
组件内部资源的使用?
组件间如何通讯?
如何通信包括:跳转、跳转的带参;事件传递
…
如果你跟我一样,充满好奇,我们一起来学习吧
Git的简单使用
如何将本地一个项目上传到远程仓库?
cd 项目路径下
git init
初始化一个本地仓库;
运行结果:
Initialized empty Git repository in /Users/admin/learnFold/studyGit/.git/
.git文件夹以外的被称为:工作区
.git文件夹内部的被称为:代码仓库
其中,代码仓库里面有两个重要的部分:
暂缓区、分支(默认为master主分支)
我们要做的就是,把工作区的内容,上传到master分支,这,就实现了git管理
要实现工作区->master分支的移动,需要借助暂缓区,即:
工作区->暂缓区->master
工作区->暂缓区,借助命令:git add .
注意:add .中间有空格
暂缓区->master,借助命令:git commit
git status
查看当前状态
如果是红色,则是处于工作区
通过命令git add .
可将工作区的内容添加到暂缓区。此时再使用git status
查看状态,会显示绿色
然后,通过命令git commit -m '注释'
将暂缓区的内容放到master分支上
提交完毕后,再使用git status
,可以看到
On branch master
nothing to commit, working tree clean
该commit只是提交到本地代码仓库里面
想要提交到网络仓库还需要:
git remote add origin 远程仓库地址
git push origin master(分支)
除此之外,可能你还需要了解下如何打标签:
git tag -a '0.0.1' -m '注释'
或者git tag '0.0.2'
git tag
查看
git push --tags
将本地所有标签同步到网络库
git tag -d 0.1.0
删除标签
git push origin :0.1.0
删除远程标签
更多学习可参考:
学习Git小笔记
CocoaPods的简单介绍
CocoaPods官网
首先,需要知道的是:git、svn都是版本管理工具
cocoapods是管理第三方库的一个工具
因此,不要混淆git与cocoapods
pod search AFNetworking
搜索AFNetwork框架
pod init
初始化一个pod。
工程文件下会生成一个Podfile文件:
# platform :ios, '9.0'
target 'studyGit' do
use_frameworks!
pod 'AFNetworking'
end
里面,#是注释
pod install
安装即可
pod install
Analyzing dependencies
Downloading dependencies
Installing AFNetworking (4.0.1)
成功之后,可以看到项目工程里面多了两个文件:
Podfile.lock以及xxx.xcworkspace
好了,项目已经引入了CocoaPods管理了
在git管理的时候,不需要上传Pods文件
有些命令不知怎么使用,请使用pod --help
问:pod install与pod update的区别?
pod install第一次是按照Podfile里面的配置进行安装配置
以后,pod install是按照Podfile.lock里面的配置进行安装配置
而pod update是每次都是按照Podfile里面的配置进行安装配置,然后更新Podfile.lock
问题就在于,如果没有指定版本号,pod update就是按照引入第三方库的最新版本进行更新的。
这样,有可能你更新了最新的库,而你的小伙伴还是使用老的库,造成某些问题
解决的话也简单,你pod update
上传代码后,下次你的小伙伴拉代码,需要执行下pod instal
即可
+install Install project dependencies according to versions from a
Podfile.lock
+update Update outdated project dependencies and create new Podfile.lock
问:Podfile与Podfile.lock的区别?
略
讲解完cocoaPods的使用,我们来了解下cocoaPods是如何工作的,或者说cocoaPods的机制是怎样的,怎么就可以实现第三方库的管理呢?
cocoaPods的机制
废话不多说,直接上小码哥的图:
当我们pod search x
第三方库的时候,是在本地库里面的某一个包含key-value的文件中查找对应的三方库,找到之后,在.spec
描述信息里面,找到第三方库的远程仓库地址,然后进行下载。
也就是,在远程索引库中,存放的是第三方库的.spec
描述信息,根据描述信息,找到真正的第三方库的远程地址
了解了CocoaPods的机制后,想不想知道:
你,如何将一个自己写的三方库也放入到CocoaPods里面进行管理,让后人敬仰呢?
其实,步骤很简单,分为三步:(打开冰箱🚪,🐘进去,关🚪)
第一步:自己写好源码,并上传到远程仓库
第二步:写一个.spec
描述文件
第三步:将.spec
描述文件上传到远程索引库
在https://github.com/CocoaPods/Specs/tree/master/Specs
随便打开一个文件,可看到:
该git地址,就是该第三方库的项目下载地址
问:本地索引库在哪里呢?
/Users/admin/.cocoapods/repos/trunk/Specs
第一步:创建代码并上传到Git仓库,就不做叙述了
第二步:写一个.spec
描述文件
使用pod spec create testLib
命令,可以看到,多了一个testLib.podspec
文件
该文件就是.spec描述文件
具体.spec文件里面怎么写,或者说怎么读,可以参考:cocoapods官网下面的podspec
例如:
Pod::Spec.new do |spec|
spec.name = 'Reachability'
spec.version = '3.1.0'
spec.license = { :type => 'BSD' }
spec.homepage = 'https://github.com/tonymillion/Reachability'
spec.authors = { 'Tony Million' => 'tonymillion@gmail.com' }
spec.summary = 'ARC and GCD Compatible Reachability Class for iOS and OS X.'
spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' }
spec.source_files = 'Reachability.{h,m}'
spec.framework = 'SystemConfiguration'
end
最重要的就是,将远程Git代码地址放在homepage后:
spec.homepage = "https://github.com/x/testlib"
将代码clong地址放在source后
spec.source = { :git => "https://github.com/x/testlib.git", :tag => "#{spec.version}" }
tag标签要打,不然不知道你要上传的是哪个版本
其实也很容易理解,你下载的AFN不都有版本么,没有版本不乱套了
.spec里面有一个spec.source_files = "Classes", "Classes/**/*.{h,m}"
这个是干嘛的呢?
首先,你在某一个git项目中,可以看到某个项目中还包含一些example的文件
但,在你pod install使用这个第三方库的时候,你是不需要这些example文件的
所以,你需要把你将提供给别人下载使用的文件放在source_files里面,别人只下载这里面的文件就可以满足功能了
此外
spec.description = "描述是空的不行"
第三步:将.spec
描述文件上传到远程索引库
上传的话,使用的是trunk上传
官网上有一些介绍
首先,要注册trunk
pod trunk register 123@qq.com 'name'
spec.license = "MIT"
使用命令pod trunk push testLib.podspec
上传.spec文件
然后经过审核,就可以了
完成之后,有可能pod search 自己的库名
还是找不到自己的库
这是因为,远方的库更新了,你自己的seach缓存没有更新,因此,你需要把自己的缓存文件删除,重写下载即可。
本地search缓存路径为:/Users/admin/Library/Caches/CocoaPods/search_index.json
组件化
概念
将一个单一工程的项目,分解为各个独立的组件;
然后按照某种方式,任意组织成一个拥有完整业务逻辑的工程
一般来说,可以分为基础组件、功能组件、业务组件
基础组件
包括:
基本配置:常量、宏
分类
网络:AFN、SDWebImage
工具:日期时间处理、文件处理、设备信息等
功能组件
包括:控件、功能
比如:拍摄视频、照片,就可以封装成一个拍摄视频、照片的组件。在组件内部实现拍摄、添加滤镜、选择照片库等等一系列操作
又比如:下载功能也可以封装成一个单独的下载功能组件
业务组件
业务组件可以是首页、个人中心、或者相亲项目、购买、打车等等某一个模块做为一个业务组件
相亲与打车、或者买东西解耦,不做关联,这样,就可以改动相亲里面的代码,不至于影响打车项目
三者的关系大致为:
业务组件在最上层
业务组件依赖于基础组件、功能组件
业务组件之间,以及功能组件之间,不能产生相互依赖
组件间通信
组件化之间通信大致有三种方法:
- URL 路由
- Target - Action
- Protocol - Class
1. URL路由
注册模块
register url 注册模块
通过 url 绑定 一个 操作绑定Block
url格式 = 协议头://模块/参数列表
打开模块
open url 打开模块
其代表是MGJRoute
控制器不作为中间件,使用MGJRoute作为中间件。每个组件里面都需要注册MGJRoute。MGJRoute里面存的是key-url,通过注册的URL做跳转
优点:
- 技术比较成熟
- 解决了跨端上的差异性,同一个路由iOS和Android都可以使用
缺点:
- 每个组件都需要初始化,内存里需要维护url-block映射表,组件多了会有内存问题(注册方法是在+load方法被调用)
- 需要去注册和维护路由表
2. Target - Action
目标-行为
基于Runtime+OC反射机制+分类
在Object-C中Target就是消息接收者对象,Action就是消息
比如我们要调用Person对象的play方法我们会说向Person对象发送了一个play消息,此时Target就是person对象,Action就是play这个方法
之前我们在AViewController中push到BViewController,需要在AViewController类文件中import进BViewController,这样二者就会产生耦合,现在利用Target-Action机制,我们不再直接import进BViewController,而是利用NSClassFromString(<#NSString * _Nonnull aClassName#>)
这个api将BViewController这个字符串反射成BViewController这个类,这样我们就可以根据反射后的类进行实例化,再调用实例化对象的各种方法。
利用Target-Action机制,我们可以实现各种灵活的解耦,将任意类的实例化过程封装到任意一个Target类中,同时,相比于URL Router,Target-Action也不需要注册和内存占用,但缺点是:编译阶段无法发现潜在的BUG,而且,开发者所创建的类和定义的方法必须要遵守Target-Action的命名规则,调用者可能会因为硬编码问题导致调用失败。
这种方案对应的开源框架是:CTMediator
和阿里BeeHive
中的Router,二者都是通过反射机制拿到最终的目标类和所需要调用的方法(对应的api是NSSelectorFromString(<#NSString * _Nonnull aSelectorName#>)
),最终通过runtime或performSelector:执行target的action,在action中进行类的实例化操作,根据具体的使用场景来决定是否将实例对象作为当前action的返回值。
通过NSClassFromString获取类并创建实例
通过performSelector + NSInvocation动态调用方法
3. Protocol - Class
和Targer Action有点像, 只不过具体功能的声明是写到一个协议里, 开发者A只需要从Mediator获取到实现了某个Protocol的类, 执行其中的方法即可
就是通过protocol定义服务接口
组件通过protocol接口,来访问实现接口的模块定义的服务:
具体实现就是把protocol和class做一个映射
同时在内存中保存一张映射表
使用的时候,就通过protocol找到对应的class来获取需要的服务。
通过protocol获取真实的class