制定代码规范,iOS架构师之路

2020-03-24 11:11 来源:未知

先吹个牛,我打心眼自认为自己是喜欢对团队项目的代码质量负责的人,对于思考如何写出高质量可读性的代码我是乐此不彼。之前我写过两篇关于代码命名规范和代码编写规范的文章,《iOS架构师之路:iOS开发中的命名规范》、《iOS架构师之路:IOS项目中的编码规范》,您要是心情很好,就去看看吧,如果低于很好,那不建议您看,怕您心里骂娘,因为现在看,感觉自己写的不太认真,有不少方面可以写的更细致,恩,我决定给自己帖贴金,不能这么说自己:其实这半年小哥我在代码规范方面的知识又见涨不少,所以看以前定制的规范不爽,作为架构师保持谦卑,通过不断学习,不断自我修正,对代码有一点洁癖是该有的气质(潜台词其实我想说我有)。制定项目的代码规范对架构师的重要性,就像要你生个娃一样,责任重大,万一生出来缺胳膊少腿,娶不到姑娘,你以后即使伺候他一辈子,给他当牛做马,他也不一定会念你的好。

  • 1.架构师要为整个项目技术方向的发展负责,所以制定一个良好的代码规范,让开发工程师遵守,有利于项目朝着您预知的方向发展。比如当您向使用AOP技术实现日志功能时,就需要规定一些方法命名。
  • 2.一致的代码规范,有利于代码reveiw工作。如果每个工程师写的代码风格不一样,review代码的同事,阅读起来肯定不顺畅。
  • 3.要求工程师按照代码规范写出一致的代码,就不怕他跳槽。这行本来就浮躁,流动性大,要是工程师写的代码风格只有他自己能看懂,那玩意他跳槽,新人是很难继续维护这部分代码的,得不偿失。

架构师不是写SDK出来交付业务方使用就没事儿了的,每家公司一定都有一套代码规范,架构师的职责也包括定义代码规范。​

1.iOS切图文件的命名规范

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

给大家推荐一本关于代码规范的杰作,第一本:《禅与 Objective-C 编程艺术(Zen and the Art of the Objective-C Craftsmanship 中文翻译)》,这本书开源社区的大牛,无偿奉献出来的,该书给我们介绍许多写代码的正确姿势,并解释为什么使用这个姿势体验更好。看完这本书应该知道如何写出优雅、高可读性并且可靠的代码了。

首先是为人编写程序,其次才是计算机。说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发、测试、生产、用户使用、版本升级和后期维护等长期过程,只有易读、易维护的软件代码才具有生命力。保持代码的简明清晰,避免过分的编程技巧。说明:简单是最美。保持代码的简单化是软件工程化的基本要求。不要过分追求技巧,否则会降低程序的可读性。编程时首先达到正确性,其次考虑效率。说明:编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。编写代码时要考虑到代码的可测试性。说明:不可以测试的代码是无法保障质量的,开发人员要牢记这一点来设计、编码。实现设计功能的同时,要提供可以测试、验证的方法。函数是为一特定功能而编写,不是万能工具箱。说明:方法是一个处理单元,是有特定功能的,所以应该很好地规划方法,不能是所有东西都放在一个方法里实现

命名规则的基本思想是把文件名分成三部分,第一部分是图片的逻辑归属分类,第二部分是图片的表现内容,第三部分是图片的内容的类型,有些图片还会有第四部分,表示图片表现的状态。首先有3个规则是:

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

《The Objective-C Style Guide used by The New York Times》(简称:New York,该规范也有中文版),《New York》是我比较喜欢的编码规范风格,它是《Zen》的编码思想一个很好的实践。

程序布局的目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。

(1)用英文命名,不用拼音

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

1.iOS切图文件的命名规范

这部分规范可能是很有经验的设计提供,也有可能是我们开发人员提供,掌握总是没有坏处的。

我们的命名规则的基本思想是把文件名分成三部分,第一部分是图片的逻辑归属分类,第二部分是图片的表现内容,第三部分是图片的内容的类型,有些图片还会有第四部分,表示图片表现的状态。首先有几个规则是:

  • 用英文命名,不用拼音
  • 每一部分用下划线分隔
  • 图片名中两倍图在名字最后要加@2x,三倍图在名字最后要加@3x

万能公式

图片 1image_naming_guideline.png

2.1.文件布局

遵循统一的布局顺序来书写头文件。说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。** **头文件布局:

文件头#import (依次为标准库头文件、非标准库头文件)全局宏常量定义全局数据类型类定义

正例:

/*************************************************************************** * 文件引用 ***************************************************************************/ /*************************************************************************** * 类引用 ***************************************************************************/ /*************************************************************************** * 宏定义 ***************************************************************************//*************************************************************************** * 常量 ***************************************************************************/ /*************************************************************************** * 类型定义 ***************************************************************************/ / *************************************************************************** * 类定义 ***************************************************************************/

遵循统一的布局顺序来书写实现文件。说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。实现文件布局:

文件头#import (依次为标准库头文件、非标准库头文件)文件内部使用的宏常量定义文件内部使用的数据类型全局变量本地变量类的实现

正例:

/*************************************************************************** * 文件引用 ***************************************************************************/ /*************************************************************************** * 宏定义 ***************************************************************************//*************************************************************************** * 常量 ***************************************************************************/ /*************************************************************************** * 类型定义 ***************************************************************************//*************************************************************************** * 全局变量 ***************************************************************************//*************************************************************************** * 原型 ***************************************************************************// *************************************************************************** * 类特性 ***************************************************************************// *************************************************************************** * 类的实现 ***************************************************************************/

(2)每一部分用下划线分隔

提高业务方View层的可读性可维护性

2.类的布局

程序布局的目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。

2.2类结构布局

使用#pragma mark –来分类方法

#pragma mark – Life Cycle#pragma mark - Events#pragma mark – Private Methods#pragma mark - UITextFieldDelegate#pragma mark - UITableViewDataSource#pragma mark - UITableViewDelegate#pragma mark - Custom Delegates#pragma mark – Getters and Setters

每个方法或者功能块之间为了结构清晰,应当有且只有一行空格。

@interface SomeClass:NSObject@property (noatomic, strong) UIView *aView-someMethod;@end@implementation SomeClass- setAView:(NSInteger )aview { }-someMethod { }

@end

不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。对的,正常情况下ViewController里面一般是不会存在private methods的,这个private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。

我看到很多APP,甚至我公司的项目,很多开发工程师,初始化属性的位置比较随意,有单独添加一个初始化方法类似setupView的,有在init初始化的,各种情况都有,我其实挺崩溃的,首先初始化方式不一致,其次也这样做非常有可能破坏了每个方法功能的单一性(每个方法只做一件事)。我比较习惯一个对象的"私有"属性写在extension里面,然后这些属性的初始化全部放在getter里面做,在init和dealloc之外,是不会出现任何类似_property这样的写法的。就是这样:

@interface CustomObject()@property (nonatomic, strong) UILabel *label;@end@implementation#pragma mark - getters and setters- (UILabel *)label { if (_label == nil) { _label = [[UILabel alloc] init]; _label.text = @"1234"; _label.font = [UIFont systemFontOfSize:12]; ... ... } return _label;}@end#pragma mark - life cycle- viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.label];}- viewWillAppear:animated { [super viewWillAppear:animated]; self.label.frame = CGRectMake(1, 2, 3, 4);}

唐巧说他喜欢的做法是用_property这种,然后关于_property的初始化通过[self setupProperty]这种做法去做。从刚才上面的代码来看,就是要在viewDidLoad里面多调用一个setup方法而已,然后我推荐的方法就是不用多调一个setup方法,直接走getter。

嗯,怎么说呢,其实两种做法都能完成需求。但是从另一个角度看,苹果之所以选择让[self getProperty]和self.property可以互相通用,这种做法已经很明显地表达了苹果的倾向:希望每个property都是通过getter方法来获得。

早在2003年,Allen Holub就发了篇文章《Why getter and setter methods are evil》,自此之后,业界就对此产生了各种争议,虽然是从Java开始说的,但是发展到后面各种语言也参与了进来。然后虽然现在关于这个问题讨论得少了,但是依旧属于没有定论的状态。setter的情况比较复杂,也不是我这一节的重点,我这边还是主要说getter。我们从objc的设计来看,苹果的设计者更加倾向于getter is not evil。认为getter is evil的原因有非常之多,或大或小,随着争论的进行,大家慢慢就聚焦到这样的一个原因:Getter和Setter提供了一个能让外部修改对象内部数据的方式,这是evil的,正常情况下,一个对象自己私有的变量应该是只有自己关心。

然后我们回到iOS领域来,objc也同样面临了这样的问题,甚至更加严重:objc并没有像Java那么严格的私有概念。但在实际工作中,我们不太会去操作头文件里面没有的变量,这是从规范上就被禁止的。

认为getter is not evil的原因也可以聚焦到一个:高度的封装性。getter事实上是工厂方法,有了getter之后,业务逻辑可以更加专注于调用,而不必担心当前变量是否可用。我们可以想一下,假设一个ViewController有20个subview要加入view中,这20个subview的初始化代码是肯定逃不掉的,放在哪里比较好?放在哪里都比放在addsubview的地方好,我个人认为最好的地方还是放在getter里面,结合单例模式之后,代码会非常整齐,生产的地方和使用的地方得到了很好的区分。所以放到iOS来说,我还是觉得使用getter会比较好,因为evil的地方在iOS这边基本都避免了,not evil的地方都能享受到,还是不错的。

我之前写代码一直把Getters and Setters 放在implementation的最前面,昨天看大神casatwy说最好放在最后面,我觉得更有道理。控制器可能会有非常多的view属性和其他属性,如果所有的getters and setters放在前面,就会导致在implementation代码顶部有大量的初始化代码,这就导致主要的逻辑代码挪到后面去了,其他人阅读代码是不太方便的。

(3)图片名中两倍图在名字最后要加@2x,三倍图在名字最后要加@3x

防止业务代码对架构产生腐蚀

2.1.文件布局

遵循统一的布局顺序来书写头文件。

说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。** **头文件布局:

文件头#import (依次为标准库头文件、非标准库头文件)全局宏常量定义全局数据类型类定义

正例:

/*************************************************************************** * 文件引用 ***************************************************************************/ /*************************************************************************** * 类引用 ***************************************************************************/ /*************************************************************************** * 宏定义 ***************************************************************************//*************************************************************************** * 常量 ***************************************************************************/ /*************************************************************************** * 类型定义 ***************************************************************************/ / *************************************************************************** * 类定义 ***************************************************************************/

遵循统一的布局顺序来书写实现文件。说明:以下内容如果某些节不需要,可以忽略。但是其它节要保持该次序。实现文件布局:

文件头#import (依次为标准库头文件、非标准库头文件)文件内部使用的宏常量定义文件内部使用的数据类型全局变量本地变量类的实现

正例:

/*************************************************************************** * 文件引用 ***************************************************************************/ /*************************************************************************** * 宏定义 ***************************************************************************//*************************************************************************** * 常量 ***************************************************************************/ /*************************************************************************** * 类型定义 ***************************************************************************//*************************************************************************** * 全局变量 ***************************************************************************//*************************************************************************** * 原型 ***************************************************************************// *************************************************************************** * 类特性 ***************************************************************************// *************************************************************************** * 类的实现 ***************************************************************************/ 

3.1 IF语句

表达式大括号和其他大括号(if/else/switch/while 等.)总是在同一行语句打开但在新行中关闭。如果没有else 并且括号内只有一行语句,可以和if语句同行,并且不需要括号。

if (user.isHappy) { //Do something} else { //Do something else}if (somethingIsBad) return something;

2.类的布局

确保传承

2.2类结构布局

使用#pragma mark –来分类方法

#pragma mark – Life Cycle#pragma mark - Events#pragma mark – Private Methods#pragma mark - UITextFieldDelegate#pragma mark - UITableViewDataSource#pragma mark - UITableViewDelegate#pragma mark - Custom Delegates#pragma mark – Getters and Setters

3.2SWITCH语句

大括号在case语句中并不是必须的,除非编译器强制要求。当一个case语句包含多行代码时,大括号应该加上。

 switch (condition) { case 1: // ... break; case 2: { // ... // Multi-line example using braces break; } case 3: // ... break; default: // ... break; }

当在switch使用枚举类型时,'default'是不需要的。例如:

 RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain; switch  { case RWTLeftMenuTopItemMain: // ... break; case RWTLeftMenuTopItemShows: // ... break; case RWTLeftMenuTopItemSchedule: // ... break; }

代码中尽量少注释,让代码能自我描述。不过当需要注释的时候,能需要清除的解释某个代码块的含义和作用。注释应当保持最新,如果不必要请删除。

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

#pragma mark - life cycle- 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];}- viewWillAppear: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去做了。总之就是尽量不要出现以下的情况:

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

欢迎大家关注我的微信公众号:丁丁的coding日记

图片 2qrcode_for_gh_a0330831fea6_430 .jpg

程序布局的目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。

保持架构发展的方向不轻易被不合理的意见所左右

2.3布局中的空格

每个方法或者功能块之间为了结构清晰,应当有且只有一行空格。

@interface SomeClass:NSObject@property (noatomic, strong) UIView *aView- someMethod;@end@implementation SomeClass- setAView:(NSInteger )aview { }- someMethod { }@end

(1).文件布局

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

2.4关于布局中的Private Methods块,正常情况下ViewController里面不应该写

不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。对的,正常情况下ViewController里面一般是不会存在private methods的,这个private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。

1).文件头一

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

3.属性初始化放哪最好?建议在Getter中初始化

我看到很多APP,甚至我公司的项目,很多开发工程师,初始化属性的位置比较随意,有单独添加一个初始化方法类似setupView的,有在init初始化的,各种情况都有,我其实挺崩溃的,首先初始化方式不一致,其次也这样做非常有可能破坏了每个方法功能的单一性(每个方法只做一件事)。我比较习惯一个对象的"私有"属性写在extension里面,然后这些属性的初始化全部放在getter里面做,在init和dealloc之外,是不会出现任何类似_property这样的写法的。就是这样:

@interface CustomObject()@property (nonatomic, strong) UILabel *label;@end@implementation#pragma mark - getters and setters- (UILabel *)label { if (_label == nil) { _label = [[UILabel alloc] init]; _label.text = @"1234"; _label.font = [UIFont systemFontOfSize:12]; ... ... } return _label;}@end#pragma mark - life cycle- viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.label];}- viewWillAppear:animated { [super viewWillAppear:animated]; self.label.frame = CGRectMake(1, 2, 3, 4);}

唐巧说他喜欢的做法是用_property这种,然后关于_property的初始化通过[self setupProperty]这种做法去做。从刚才上面的代码来看,就是要在viewDidLoad里面多调用一个setup方法而已,然后我推荐的方法就是不用多调一个setup方法,直接走getter。

嗯,怎么说呢,其实两种做法都能完成需求。但是从另一个角度看,苹果之所以选择让[self getProperty]self.property可以互相通用,这种做法已经很明显地表达了苹果的倾向:希望每个property都是通过getter方法来获得。

早在2003年,Allen Holub就发了篇文章《Why getter and setter methods are evil》,自此之后,业界就对此产生了各种争议,虽然是从Java开始说的,但是发展到后面各种语言也参与了进来。然后虽然现在关于这个问题讨论得少了,但是依旧属于没有定论的状态。setter的情况比较复杂,也不是我这一节的重点,我这边还是主要说getter。我们从objc的设计来看,苹果的设计者更加倾向于getter is not evil。认为getter is evil的原因有非常之多,或大或小,随着争论的进行,大家慢慢就聚焦到这样的一个原因:Getter和Setter提供了一个能让外部修改对象内部数据的方式,这是evil的,正常情况下,一个对象自己私有的变量应该是只有自己关心。

然后我们回到iOS领域来,objc也同样面临了这样的问题,甚至更加严重:objc并没有像Java那么严格的私有概念。但在实际工作中,我们不太会去操作头文件里面没有的变量,这是从规范上就被禁止的。

认为getter is not evil的原因也可以聚焦到一个:高度的封装性。getter事实上是工厂方法,有了getter之后,业务逻辑可以更加专注于调用,而不必担心当前变量是否可用。我们可以想一下,假设一个ViewController有20个subview要加入view中,这20个subview的初始化代码是肯定逃不掉的,放在哪里比较好?放在哪里都比放在addsubview的地方好,我个人认为最好的地方还是放在getter里面,结合单例模式之后,代码会非常整齐,生产的地方和使用的地方得到了很好的区分。所以放到iOS来说,我还是觉得使用getter会比较好,因为evil的地方在iOS这边基本都避免了,not evil的地方都能享受到,还是不错的。

#import (依次为标准库头文件、非标准库头文件)

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

4.Getters and Setters放在最底部

我之前写代码一直把Getters and Setters 放在implementation的最前面,昨天看大神casatwy说最好放在最后面,我觉得更有道理。控制器可能会有非常多的view属性和其他属性,如果所有的getters and setters放在前面,就会导致在implementation代码顶部有大量的初始化代码,这就导致主要的逻辑代码挪到后面去了,其他人阅读代码是不太方便的。

夜深,该睡了。欢迎收藏的 我的博客

全局宏

图片 3

常量定义

要点如下:

全局数据类型

所有的属性都使用getter和setter

类定义

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

2).文件头二

比如这样:

#import (依次为标准库头文件、非标准库头文件)

#pragma mark - life cycle

文件内部使用的宏

- (void)viewDidLoad

常量定义

{

文件内部使用的数据类型

[super viewDidLoad];

全局变量

self.view.backgroundColor = [UIColor whiteColor];

本地变量(即静态全局变量)

[self.view addSubview:self.firstTableView];

类的实现

[self.view addSubview:self.secondTableView];

(2).类结构布局

[self.view addSubview:self.firstFilterLabel];

使用#pragma mark –来分类方法

[self.view addSubview:self.secondFilterLabel];

#pragma mark – Life Cycle

[self.view addSubview:self.cleanButton];

#pragma mark - Events

[self.view addSubview:self.originImageView];

#pragma mark – Private Methods

[self.view addSubview:self.processedImageView];

#pragma mark - UITextFieldDelegate

[self.view addSubview:self.activityIndicator];

#pragma mark - UITableViewDataSource

[self.view addSubview:self.takeImageButton];

#pragma mark - UITableViewDelegate

}

#pragma mark - Custom Delegates

- (void)viewWillAppear:(BOOL)animated

#pragma mark – Getters and Setters

{

(3).布局中的空格

[super viewWillAppear:animated];

每个方法或者功能块之间为了结构清晰,应当有且只有一行空格。

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

@interface SomeClass:NSObject

self.originImageView.size = CGSizeMake(width, width);

@property (noatomic, strong) UIView *aView

[self.originImageView topInContainer:70 shouldResize:NO];

- (void)someMethod;

[self.originImageView leftInContainer:10 shouldResize:NO];

@end

self.processedImageView.size = CGSizeMake(width, width);

@implementation SomeClass

[self.processedImageView right:10 FromView:self.originImageView];

- (void)setAView:(NSInteger )aview {

[self.processedImageView topEqualToView:self.originImageView];

}

CGFloat labelWidth = self.view.width - 100;

- (void)someMethod {

self.firstFilterLabel.size = CGSizeMake(labelWidth, 20);

}

[self.firstFilterLabel leftInContainer:10 shouldResize:NO];

@end

[self.firstFilterLabel top:10 FromView:self.originImageView];

(4).关于布局中的Private Methods块

... ...

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

}

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

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

(5).属性初始化放哪最好?

- (void)viewDidLoad

苹果之所以选择让[self getProperty]和self.property可以互相通用,这种做法已经很明显地表达了苹果的倾向:希望每个property都是通过getter方法来获得。认为最好的地方还是放在getter里面,结合单例模式之后,代码会非常整齐,生产的地方和使用的地方得到了很好的区分。

{

一个对象的"私有"属性写在extension里面,然后这些属性的初始化全部放在getter里面做,在init和dealloc之外,是不会出现任何类似_property这样的写法的。就是这样:

[super viewDidLoad];

@interface CustomObject()

self.textLabel = [[UILabel alloc] init];

@property (nonatomic, strong) UILabel *label;

self.textLabel.textColor = [UIColor blackColor];

@end

self.textLabel ... ...

@implementation

self.textLabel ... ...

//1.懒加载设置基本属性

self.textLabel ... ...

#pragma mark - getters and setters

[self.view addSubview:self.textLabel];

- (UILabel *)label {

}

if (_label == nil) {

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

_label = [[UILabel alloc] init];

getter和setter全部都放在最后

_label.text = @"1234";

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

_label.font = [UIFont systemFontOfSize:12];

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

... ...

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

}

event response专门开一个代码区域

return _label;

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

}

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

@end

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

#pragma mark - life cycle

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

//2.添加控件到父控件上

勘误1:其实在viewWillAppear这里改变UI元素不是很可靠,Autolayout发生在viewWillAppear之后,严格来说这里通常不做视图位置的修改,而用来更新Form数据。改变位置可以放在viewWilllayoutSubview或者didLayoutSubview里,而且在viewDidLayoutSubview确定UI位置关系之后设置autoLayout比较稳妥。另外,viewWillAppear在每次页面即将显示都会调用,viewWillLayoutSubviews虽然在lifeCycle里调用顺序在viewWillAppear之后,但是只有在页面元素需要调整时才会调用,避免了Constraints的重复添加。

- (void)viewDidLoad {

[super viewDidLoad];

[self.view addSubview:self.label];

}

//3.设置frame

- (void)viewWillAppear:(BOOL)animated {

[super viewWillAppear:animated];

self.label.frame = CGRectMake(1, 2, 3, 4);

}

(6).Getters and Setters放在最底部

控制器可能会有非常多的view属性和其他属性,如果所有的getters and setters放在前面,就会导致在implementation代码顶部有大量的初始化代码,这就导致主要的逻辑代码挪到后面去了,其他人阅读代码是不太方便的。

TAG标签:
版权声明:本文由美高梅网投平台发布于美高梅网投网址,转载请注明出处:制定代码规范,iOS架构师之路