澳门新葡新京 > 关于我们 > 程序员怎样鉴定强悍的小团队,少见却神奇的类

程序员怎样鉴定强悍的小团队,少见却神奇的类
2020-05-05 19:38

CocoaPods是用ruby实现的,因此Podfile文件的语法就是ruby的语法。podfile是一个说明文件,用以描述管理一个或者多个Xcode project的target的依赖库。这个文件应该且必须被命名为Podfile。Podfile可以非常简单,下面的例子增加了Alamofire依赖库到单个target:

抱歉用了这条鸡汤风格的标题。

众所周知,NSObject类是Objective-C中大部分类的基类。但不是很多人知道除了NSObject之外的另一个基类——NSProxy

以前一直做持续发布使用xcbuild和xctool这些工作进行打包,但是经常遇到证书相关的问题,各种Provising Profile文件匹配不上,然后又证书不匹配等等。于是开始寻求新的解决方案,找到了新的神器fastlane。

target 'MyApp' do use_frameworks! pod 'Alamofire', '~> 3.0'end

什么叫强悍的小团队?我带领的蝉小队在过去4年里,一共做了7个APP(其中2个的难度中上),以及5个难度中等的网站,研发组长期保持1后端 / 1 iOS +前端 / 1 Android 的配置,也就是3位程序员。

NS_ROOT_CLASS@interface NSProxy <NSObject>

相关信息

fastlane是一整套的工具帮助开发者主要是iOS开发者,打包App上传App到App store或着TestFlight。fastlane的官方repo地址:https://github.com/fastlane/fastlanefastlane的快速上手文档:https://docs.fastlane.tools/fastlane支持的action文档https://docs.fastlane.tools/actions/

下面是一个更复杂的例子,Podfile链接了app和它的测试bundle:

根据我对同行的观察,同样的业务,正常情况下会用到8-10位程序员,然而这3倍的效率提升并不是靠加班换来的。如果不赶市场档期,我们从不安排加班,并且厌恶常态的加班。

这个奇怪的类是干嘛的?请允许我做一个黑人问号脸马上查了一下Apple的官方文档:NSProxy

安装fastlane

1.确保xcode命令行工具已经安装```xcode-select --install```2.参考官方文档有三种安装方式任选一种进行安装

图片 1Paste_Image.png

source 'https://github.com/CocoaPods/Specs.git'source 'https://github.com/Artsy/Specs.git'platform :ios, '9.0'inhibit_all_warnings!target 'MyApp' do pod 'GoogleAnalytics', '~> 3.1' # Has its own copy of OCMock # and has access to GoogleAnalytics via the app # that hosts the test target target 'MyAppTests' do inherit! :search_paths pod 'OCMock', '~> 2.0.1' endendpost_install do |installer| installer.pods_project.targets.each do |target| puts target.name endend

本文会告诉你,怎样的组队方案才能实现超高的研发效率。

NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.

使用fastlane进行打包

1.初始化项目 fastlane init*使用fastlane必须要先对项目进行初始化fastlane init*a.使用命令行工具进入项目目录执行fastlane init命令

图片 2Paste_Image.png

初始化的过程中会需要填写一些项目信息包括scheme name和你的Apple id等等。同时会在项目中生成一个fastlane的文件夹。

2.查看fastlane文件夹中的内容上面已经说到初始化之后,在项目根目录会生成一个fastlane的文件夹,接着打开文件夹看一看

图片 3Paste_Image.png

文件夹里一共有4个文件

  1. Appfile里面存放了App的基本信息包括app_identifier、apple_id、team_id等等。如果在第一步init的时候你输入了正确的appId账号和密码会在这里生成正确的team_id信息。
  2. Fastfile是最重要的一个文件,可以编写和定制我们打包脚本的一个文件,我们自定义的一些功能就写在这里。
  3. README.md里面是一些帮助说明信息
  4. report.xml就不用管了

Fastfile

下面贴一下Fastfile中的内容

图片 4Paste_Image.png前面几行记录了fastlane的版本信息从platform :ios do开始下面的就是fastlane可以执行的任务第一个

before_all do cocoapodsend

这个点意思是在执行每一个lane之前都先执行这个功能,它里面其实就跑了一个fastlane的action这个action叫cocoapods3.1查看fastlane的action以及action的用法上面提到before_all中其实就是执行了一个action那么让我们来看看fastlane一共有哪些action,以及action怎么用在命令行中使用fastlane actions可以列出所有的action,使用fastlane action [action的名字]可以查看该action的相关信息,最重要的是在帮助文档中的这里可以查看具体action的使用方法和示列https://docs.fastlane.tools/actions/.列出所有action

图片 5Paste_Image.png

查看某个action的说明

图片 6Paste_Image.png

网页文档中关于cocoapods这个action的说明:

图片 7屏幕快照 2016-12-05 上午1.11.49.png

3.编写自己的lane

知道了action和lane之后,我们回到Fastfile文件中,看看其实在生成的文件中已经帮我们写了好几个lane了

 desc "Runs all the tests" lane :test do scan end desc "Submit a new Beta Build to Apple TestFlight" desc "This will also make sure the profile is up to date" lane :beta do # match(type: "appstore") # more information: https://codesigning.guide gym # Build your app - more options available pilot # sh "your_script.sh" # You can also use other beta testing services here (run `fastlane actions`) end

上面有2个lane,一个叫test 一个叫beta,test中执行了scan这个action,这就是用来运行单元测试的一个action,具体信息可以使用fastlane action scan查看,然后beta这个lane执行了gym和pilot,gym是打包,pilot是把应用发到TestFlight,具体使用方法一定要去查看文档哦。但是生成的这些lane可能不满足我们的需求,下面以一个企业版应用打包为例我们自己新建一个lane:

 desc "打包成企业版ipa" lane :inhouse do |options| #更新info plist文件 ENV["key"]表示从环境变量中读取值,也可以直接写死,如果要在打包时改变某些参数,可以把它们设置成环境变量 update_info_plist( plist_path: "GoodFolks/info.plist", display_name: "新App名字", ) #更新urlType 具体用法查询action文件update_info_plist的说明 update_info_plist( xcodeproj: "GoodFolks.xcodeproj", plist_path: "GoodFolks/info.plist", block: lambda { |plist| #这里我在更新微信的urlType 这个action还可以更新app的bundleid 具体可以查阅文档 urlScheme = plist["CFBundleURLTypes"].find{|scheme| scheme["CFBundleURLName"] == "weixin"} urlScheme[:CFBundleURLSchemes] = ENV["app_urlschems_weixin"] } ) #设置版本号 increment_version_number( version_number: ENV["app_versionName"] ) #设置build号,这些都可以写死,也可以不要这些action,也可以从环境变量中获取值 increment_build_number( build_number: ENV["app_versionCode"] ) #这个action很重要cert就是下载和安装匹配的Provision Profile文件,不用你去管那些证书不匹配的事情啦,下载的文件会存在项目根目录的build文件夹下 cert(output_path:"build") #这一步就是签名了 sigh( app_identifier: ENV["app_applicationId"], team_id:"9M8CTWAV8P", output_path: "build" ) #最后就是打包,企业版打包,打包完成后会在项目根目录的build文件夹下生成ipa文件 gym( scheme: "GoodFolks", export_method: "enterprise", output_directory: "build", include_bitcode: false ) # do some other stuff hereend

4.起飞上面说了这么多,但是怎么运行呢?很简单在你的项目根目录执行fastlane [lane的名字]比如我们上面自己写的一个fastlane inhouse,fastlane就会执行指定的任务了,然后你就可以去喝咖啡啦。

Tipsfastlane在打包的时候可能需要交互比如输入你的Apple id的密码,但是很明显在做持续发布的时候都是自动化的,不是交互式的shell,没办法人工去输入密码,这个只需要把你的Appleid的密码设置成一个环境变量即可,环境变量的key为FASTLANE_PASSWORD。

如果你希望多个target共享同一个pods,那么可以用关键字abstract_target

令人遗憾的是,实现超高的研发效率,关键并不在程序员身上,而是在老板和产品经理身上。

总的来说,NSProxy是一个虚类,你可以通过继承它,并重写这两个方法以实现消息转发到另一个实例

# There are no targets called "Shows" in any Xcode projectsabstract_target 'Shows' do pod 'ShowsKit' pod 'Fabric' # Has its own copy of ShowsKit + ShowWebAuth target 'ShowsiOS' do pod 'ShowWebAuth' end # Has its own copy of ShowsKit + ShowTVAuth target 'ShowsTV' do pod 'ShowTVAuth' endend

先从老板说起。

- forwardInvocation:(NSInvocation *)anInvocation;- (NSMethodSignature *)methodSignatureForSelector:sel;

Podfile中自带一个隐藏的、默认的abstract target,所以你也可以用如下的方式达到上面例子的同样效果:

首先,老板要制定正确的阶段性目标,不要搞大跃进,也不要异想天开。如果阶段目标弄错了,整个团队必然疲于奔命。随便举个例子,每个老板内心深处都有一个社区梦,管他妈什么产品形态,最后都会跑去做社区,花偌大的力气在盐碱地上开垦。

现在NSProxy的真面目终于浮出水面:负责将消息转发到真正的target的代理类。举个例子,你想要卖一件二手物品,但是你并不想直接跟卖家接触(直接向target发消息),这时你去找了一个第三方,你告诉这个第三方你要买什么、出多少钱买、什么时候要等,第三方再去跟卖家接触并把这些信息转告卖家(转发消息给真实的target),最后通过第三方去完成这个交易。

pod 'ShowsKit'pod 'Fabric'# Has its own copy of ShowsKit + ShowWebAuthtarget 'ShowsiOS' do pod 'ShowWebAuth'end# Has its own copy of ShowsKit + ShowTVAuthtarget 'ShowsTV' do pod 'ShowTVAuth'end

其次,老板一定不要插手细节。

了解完NSProxy是是什么以后,那么它究竟能帮我们干些什么呢?

当开始一个项目,你可能会想要使用最新版本的pod依赖库。 如果是这种情况,只需忽略版本要求。

我跟你们讲一个真理:“我知道我的广告费有一半浪费了,但遗憾的是,我不知道是哪一半被浪费了。”哦不对,应该是这句:“任何产品都有一半的需求对成败毫无影响,但遗憾的是,每个人都坚信自己提出的需求奠定了成功的基础。”

多继承在编程中可以说是比较有用的特性。举个例子,原本有两个相互独立的类A和类B,它们各自继承各自的父类,项目进行地好好的,突然有一天产品经理过来告诉你,我要在下个版本加一个xxxxx的特性,非常紧急。一脸懵逼的你发现如果要实现这个特性,你需要对类A以及其父类作很大的修改,代价非常之高。突然你意识到原来类B的父类已经有类似的功能,你只需要让类A继承于类B的父类并重写其某些方法就能实现,这样做高效且低风险,于是你屁颠屁颠地撸起了代码。

pod 'SSZipArchive'

这句话翻译成大白话,就是“对产品设计有决策权的人越少越好。”因为过度设计是不可避免的,可抠的细节又是无穷无尽的,有话语权的人越多,则过度设计越发严重,最应该闭嘴的人就是话语权最大的老板。要知道,抠细节与产品成败没有一毛钱关系,而加功能——尤其是“比着竞品加功能”这么简单的事,如果老板和产品经理的意见相左,99%的情况下是产品经理正确,连这个都判断不准还做个屁的PM。

可是,Objective-C却不支持这样一个强大的特性。不过NSProxy可以帮我们在某种程度上(这只是一个模拟的多继承,并不是完全的多继承)解决这个问题:

稍后在项目您可能想要使用特定版本的pod依赖库,在这种情况下,您可以指定版本号。

精简需求,做好产品的减法,从老板学会闭嘴开始。

现在假设我们想要去买书,但是我懒癌犯了,不想直接去书店买,如果有一个跑腿的人帮我去书店买完,我再跟他买。同时,我买完书又想买件衣服,我又可以很轻松地在他那里买到一件衣服。

pod 'Objection', '0.9'

继续上面的话题,产品经理越少越好。最好是两位,一主一次,既有得讨论,也能控制住过度设计带来的浪费。

首先,我们定义BookProvider类与ClothesProvider类作为基类。

除了没有版本,或特定的一个,也可以使用逻辑运算符:

很多团队是这样的,先招一个PM顶住,发现不够强,再招一个,还是不够强,再招一个……为了弥补缺口,制造更大的缺口。我们都知道产品经理扎堆会带来多么可怕的后果。

// TDBookProvider.h#import <Foundation/Foundation.h>@protocol TDBookProviderProtocol <NSObject>- purchaseBookWithTitle:(NSString *)bookTitle;@end@interface TDBookProvider : NSObject@end

// TDClothesProvider.h#import <Foundation/Foundation.h>typedef NS_ENUM (NSInteger, TDClothesSize){ TDClothesSizeSmall = 0, TDClothesSizeMedium, TDClothesSizeLarge};@protocol TDClothesProviderProtocol <NSObject>- purchaseClothesWithSize:(TDClothesSize )size;@end@interface TDClothesProvider : NSObject@end
  • '> 0.1' 高于0.1的任何版本
  • '>= 0.1' 版本0.1或更高版本
  • '< 0.1' 低于0.1的任何版本
  • '<= 0.1' 版本0.1或更低的版本

然而招到或培养一个“相当靠谱的产品经理”,目前还是小概率事件。

这里要注意:一定要通过protocol来声明接口,而不是直接在类的@interfere中定义。因为通过protocol来声明接口,然后让proxy类遵循此协议,可以骗过编译器防止编译器提示proxy类未声明接口的错误。这个问题下面可以看到。

除了逻辑运算符,还有一种运算符:

好的产品经理能够为提升研发效率做三件事情。

然后是这两个类的实现

  • '~> 0.1.2' 版本0.1.2和0.2版本之间的任意版本,不包括0.2和比0.2更高的版本
  • '~> 0.1' 版本0.1和版本1.0之间的任意版本,不包括1.0和比1.0更高的版本
  • '~> 0' 版本0或比版本0更高的版本,这基本上和不指定版本号的效果是一样的。

第一是头脑清晰,控制过度设计,至少一半以上的需求对“验证成败”这个终极目的是有帮助的。同时能复用就复用,能精简就精简,擅长从研发成本的角度出发去考虑需求,而不是因为“这个功能牛逼”“这个交互创新”,不会为了刷存在感而去做一些花哨的事情。

第二是划分清楚优先级,需求都重要,都重要就是都不重要咯。谁最重要,最优先,最紧急?第一个验证性的版本最小包含哪些需求?如果验证结果不够满意,再修改哪些需求可以快速验证第二轮?把优先级厘清楚,才能真正实现敏捷开发,否则就是瞎鸡巴折腾。

第三是有预见性,提出一个功能,构建一个页面的时候,预见到它在未来可能的几种进化。虽然这特别难,但能够有效减少代码上的推翻重来,算是老司机专用的橙卡。

// TDBookProvider.m#import "TDBookProvider.h"@interface TDBookProvider () <TDBookProviderProtocol>@end@implementation TDBookProvider- purchaseBookWithTitle:(NSString *)bookTitle{ NSLog(@"You've bought "%@"",bookTitle);}@end

// TDClothesProvider.m#import "TDClothesProvider.h"@interface TDClothesProvider () <TDClothesProviderProtocol>@end@implementation TDClothesProvider- purchaseClothesWithSize:(TDClothesSize )size{ NSString *sizeStr; switch  { case TDClothesSizeLarge: sizeStr = @"large size"; break; case TDClothesSizeMedium: sizeStr = @"medium size"; break; case TDClothesSizeSmall: sizeStr = @"small size"; break; default: break; } NSLog(@"You've bought some clothes of %@",sizeStr);}@end

如果你想建立一个本地依赖库和项目之间的关系,即项目依赖本地文件夹的某个依赖库,可以用关键字path

显而易见,能做到以上三点的产品经理,年薪最低60万起谈,期权另计,有价无市。考虑到他为公司节约的研发成本,60万都是白菜价。

现在两个Provider的类写完,我们可以直接向供应商买东西了,但这跟我们的需求还有很大差异,我们需要一个中间的经销商