iOS应用架构谈 view层的组织和调用方案

Date   Fri 24 April 2015  Tags  iOS /  architect /  thoughts




iOS应用架构谈 开篇 
iOS应用架构谈 view层的组织和调用方案 
iOS应用架构谈 网络层设计方案 
iOS应用架构谈 动态部署方案 
iOS应用架构谈 本地持久化方案




前言


iOS应用架构谈 开篇》出来之后,很多人来催我赶紧出第二篇。这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分。

现在好啦,第二篇出来了。


当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构。

一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们在这时候必须清楚认识到:View层的架构一旦实现或定型,在App发版后可修改的余地就已经非常之小了。因为它跟业务关联最为紧密,所以哪怕稍微动一点点,它所引发的蝴蝶效应都不见得是业务方能够hold住的。这样的情况,就要求我们在实现这个架构时,代码必须得改得勤快,不能偷懒。也必须抱着充分的自我怀疑态度,做决策时要拿捏好尺度。


View层的架构非常之重要,在我看来,这部分架构是这系列文章涉及4个方面最重要的一部分,没有之一。为什么这么说?



View层架构是影响业务方迭代周期的因素之一


产品经理产生需求的速度会非常快,尤其是公司此时仍处于创业初期,在规模稍大的公司里面,产品经理也喜欢挖大坑来在leader面前刷存在感,比如阿里。这就导致业务工程师任务非常繁重。正常情况下让产品经理砍需求是不太可能的,因此作为架构师,在架构里有一些可做可不做的事情,最好还是能做就做掉,不要偷懒。这可以帮业务方减负,编写代码的时候也能更加关注业务。

我跟一些朋友交流的时候,他们都会或多或少地抱怨自己的团队迭代速度不够快,或者说,迭代速度不合理地慢。我认为迭代速度不是想提就能提的,迭代速度的影响因素有很多,一期PRD里的任务量和任务复杂度都会影响迭代周期能达到什么样的程度。抛开这些外在的不谈,从内在可能导致迭代周期达不到合理的速度的原因来看,其中有一个原因很有可能就是View层架构没有做好,让业务工程师完成一个不算复杂的需求时,需要处理太多额外的事情。当然,开会多,工程师水平烂也属于迭代速度提不上去的内部原因,但这个不属于本文讨论范围。还有,加班不是优化迭代周期的正确方式,嗯。

一般来说,一个不够好的View层架构,主要原因有以下五种:

  1. 代码混乱不规范
  2. 过多继承导致的复杂依赖关系
  3. 模块化程度不够高,组件粒度不够细
  4. 横向依赖
  5. 架构设计失去传承


这五个地方会影响业务工程师实现需求的效率,进而拖慢迭代周期。View架构的其他缺陷也会或多或少地产生影响,但在我看来这里五个是比较重要的影响因素。如果大家觉得还有什么因素比这四个更高的,可以在评论区提出来我补上去。

对于第五点我想做一下强调:架构的设计是一定需要有传承的,有传承的架构从整体上看会非常协调。但实际情况有可能是一个人走了,另一个顶上,即便任务交接得再完整,都不可避免不同的人有不同的架构思路,从而导致整个架构的流畅程度受到影响。要解决这个问题,一方面要尽量避免单点问题,让架构师做架构的时候再带一个人。另一方面,架构要设计得尽量简单,平缓接手人的学习曲线。我离开安居客的时候,做过保证:凡是从我手里出来的代码,终身保修。所以不要想着离职了就什么事儿都不管了,这不光是职业素养问题,还有一个是你对你的代码是否足够自信的问题。传承性对于View层架构非常重要,因为它距离业务最近,改动余地最小。

所以当各位CTO、技术总监、TeamLeader们觉得迭代周期不够快时,你可以先不忙着急吼吼地去招新人,《人月神话》早就说过加人不能完全解决问题。这时候如果你可以回过头来看一下是不是View层架构不合理,把这个弄好也是优化迭代周期的手段之一。

嗯,至于本系列其他三项的架构方案对于迭代周期的影响程度,我认为都不如View层架构方案对迭代周期的影响高,所以这是我认为View层架构是最重要的其中一个理由。



View层架构是最贴近业务的底层架构


View层架构虽然也算底层,但还没那么底层,它跟业务的对接面最广,影响业务层代码的程度也最深。在所有的底层都牵一发的时候,在View架构上牵一发导致业务层动全身的面积最大。

所以View架构在所有架构中一旦定型,可修改的空间就最小,我们在一开始考虑View相关架构时,不光要实现功能,还要考虑更多规范上的东西。制定规范的目的一方面是防止业务工程师的代码腐蚀View架构,另一方面也是为了能够有所传承。按照规范来,总还是不那么容易出差池的。

还有就是,架构师一开始考虑的东西也会有很多,不可能在第一版就把它们全部实现,对于一个尚未发版的App来说,第一版架构往往是最小完整功能集,那么在第二版第三版的发展过程中,架构的迭代任务就很有可能不只是你一个人的事情了,相信你一个人也不见得能搞定全部。所以你要跟你的合作者们有所约定。另外,第一版出去之后,业务工程师在使用过程中也会产生很多修改意见,哪些意见是合理的,哪些意见是不合理的,也要通过事先约定的规范来进行筛选,最终决定如何采纳。

规范也不是一成不变的,什么时候枪毙意见,什么时候改规范,这就要靠各位的技术和经验了。


以上就是前言。




这篇文章讲什么?


  • View代码结构的规定

  • 关于view的布局

  • 何时使用storyboard,何时使用nib,何时使用代码写View

  • 是否有必要让业务方统一派生ViewController?

  • 方便View布局的小工具

  • MVC、MVVM、MVCS、VIPER

  • 本门心法

  • 跨业务时View的处理

  • 留给评论区各种补

  • 总结




View代码结构的规定


架构师不是写SDK出来交付业务方使用就没事儿了的,每家公司一定都有一套代码规范,架构师的职责也包括定义代码规范。按照道理来讲,定代码规范应该是属于通识,放在这里讲的原因只是因为我这边需要为View添加一个规范。

制定代码规范严格来讲不属于View层架构的事情,但它对View层架构未来的影响会比较大,也是属于架构师在设计View层架构时需要考虑的事情。制定View层规范的重要性在于:

  1. 提高业务方View层的可读性可维护性
  2. 防止业务代码对架构产生腐蚀
  3. 确保传承
  4. 保持架构发展的方向不轻易被不合理的意见所左右


在这一节里面我不打算从头开始定义一套规范,苹果有一套Coding Guidelines,当我们定代码结构或规范的时候,首先一定要符合这个规范。

然后,相信大家各自公司里面也都有一套自己的规范,具体怎么个规范法其实也是根据各位架构师的经验而定,我这边只是建议各位在各自规范的基础上再加上下面这一点。


viewController的代码应该差不多是这样:

pic1



要点如下:



所有的属性都使用getter和setter


不要在viewDidLoad里面初始化你的view然后再add,这样代码就很难看。在viewDidload里面只做addSubview的事情,然后在viewWillAppear里面做布局的事情(勘误1),最后在viewDidAppear里面做Notification的监听之类的事情。至于属性的初始化,则交给getter去做。

比如这样:

#pragma mark - life cycle
- (void)viewDidLoad
{
   
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.firstTableView];
    [self.view addSubview:self.secondTableView];
    [self.view addSubview:self.firstFilterLabel];
    [self.view addSubview:self.secondFilterLabel];
    [self.view addSubview:self.cleanButton];
    [self.view addSubview:self.originImageView];
    [self.view addSubview:self.processedImageView];
    [self.view addSubview:self.activityIndicator];
    [self.view addSubview:self.takeImageButton];
}

- (void)viewWillAppear:(BOOL)animated
{
   
    [super viewWillAppear:animated];

    CGFloat width = (self.view.width - 30) / 2.0f;

    self.originImageView.size = CGSizeMake(width, width);
    [self.originImageView topInContainer:70 shouldResize:NO];
    [self.originImageView leftInContainer:10 shouldResize:NO];

    self.processedImageView.size = CGSizeMake(width, width);
    [self.processedImageView right:10 FromView:self.originImageView];
    [self.processedImageView topEqualToView:self.originImageView];

    CGFloat labelWidth = self.view.width - 100;
    self.firstFilterLabel.size = CGSizeMake(labelWidth, 20);
    [self.firstFilterLabel leftInContainer:10 shouldResize:NO];
    [self.firstFilterLabel top:10 FromView:self.originImageView];

    ... ...
}


这样即便在属性非常多的情况下,还是能够保持代码整齐,view的初始化都交给getter去做了。总之就是尽量不要出现以下的情况:


- (void)viewDidLoad
{
   
    [super viewDidLoad];

    self.textLabel = [[UILabel alloc] init];
    self.textLabel.textColor = [UIColor blackColor];
    self.textLabel ... ...
    self.textLabel ... ...
    self.textLabel ... ...
    [self.view addSubview:self.textLabel];
}

这种做法就不够干净,都扔到getter里面去就好了。关于这个做法,在唐巧的技术博客里面有一篇文章和我所提倡的做法不同,这个我会放在后面详细论述。



getter和setter全部都放在最后


因为一个ViewController很有可能会有非常多的view,就像上面给出的代码样例一样,如果getter和setter写在前面,就会把主要逻辑扯到后面去,其他人看的时候就要先划过一长串getter和setter,这样不太好。然后要求业务工程师写代码的时候按照顺序来分配代码块的位置,先是life cycle,然后是Delegate方法实现,然后是event response,然后才是getters and setters。这样后来者阅读代码时就能省力很多。



每一个delegate都把对应的protocol名字带上,delegate方法不要到处乱写,写到一块区域里面去


比如UITableViewDelegate的方法集就老老实实写上#pragma mark - UITableViewDelegate。这样有个好处就是,当其他人阅读一个他并不熟悉的Delegate实现方法时,他只要按住command然后去点这个protocol名字,Xcode就能够立刻跳转到对应这个Delegate的protocol定义的那部分代码去,就省得他到处找了。



event response专门开一个代码区域


所有button、gestureRecognizer的响应事件都放在这个区域里面,不要到处乱放。



关于private methods,正常情况下ViewController里面不应该写


不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。对的,正常情况下ViewController里面一般是不会存在private methods的,这个private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。

ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。



为什么要这样要求?


我见过无数ViewController,代码布局乱得一塌糊涂,这里一个delegate那里一个getter,然后ViewController的代码一般都死长死长的,看了就让人头疼。

定义好这个规范,就能使得ViewController条理清晰,业务方程序员很能够区分哪些放在ViewController里面比较合适,哪些不合适。另外,也可以提高代码的可维护性和可读性。




关于View的布局


业务工程师在写View的时候一定逃不掉的就是这个命题。用Frame也好用Autolayout也好,如果没有精心设计过,布局部分一定惨不忍睹。

直接使用CGRectMake的话可读性很差,光看那几个数字,也无法知道view和view之间的位置关系。用Autolayout可读性稍微好点儿,但生成Constraint的长度实在太长,代码观感不太好。

Autolayout这边可以考虑使用Masonry,代码的可读性就能好很多。如果还有使用Frame的,可以考虑一下使用这个项目

这个项目里面提供了Frame相关的方便

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS 架构一般分为以下几: 1. 应用(Application Layer):这是用户直接与应用程序进行交互的界面,包括用户界面(UI)和应用的业务逻辑。在这一开发者使用UIKit框架构应用的用户界面和处理用户输入。 2. 应用服务(Application Services Layer):该提供了一系列服务和功能,以支持应用的业务逻辑。例如,数据持久化、网络通信、地理位置服务、通知等。开发者可以使用Cocoa Touch框架中的各种服务类来实现这些功能。 3. 系统(System Layer):这一包括了iOS操作系统本身和一些核心框架,提供了许多底功能和服务。例如,文件系统、多任务处理、内存管理、进程间通信等。开发者在这一使用的主要是Objective-C或Swift语言中的系统框架。 4. 核心服务(Core Services Layer):该提供了一些基本服务和工具,用于处理应用程序所需的共享资源和系统级任务。例如,数据存储、网络通信、日期和时间处理、文件管理等。在这一开发者可以使用Core Data、Core Location、Core Animation等核心服务框架。 5. 外部设备支持(Media Layer):这一提供了对各种外部设备和多媒体功能的支持。例如,相机、音频、视频、蓝牙等。开发者可以使用AVFoundation、Core Audio、Core Video等框架来处理相关功能。 这些次相互关联,构成了iOS应用的整体架构开发者在不同的次上进行开发,可以根据需要选择所需的框架和服务来实现应用的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值