在日常的开发过程中,如果大家都遵循统一的代码规范,我们就可以避免许多无缘无故的Bug,提高程序的准确性、连续性、可读性、可维护性更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。。以下是我们公司一些常见的代码规范以及个人认为一些比较好的代码风格来和大家分享一下:
1. .h和.m文件布局
1.1 .h文件布局,请遵循以下顺序来撸代码
文件头(该文件信息的描述)
#import(依次为标准库头文件、非标准库头文件)
文件内部使用的宏定义
常量定义 (文件内部使用的数据类型,比如枚举等)
静态全局变量定义
代理协议定义(如果有使用了代理等)
类的实现
成员变量
类方法
实例方法
1.2 .m文件布局
#pragma mark – init
#pragma mark - View lifecycle
#pragma mark – Notification
#pragma mark – Delegate
#pragma mark - 按钮事件
#pragma mark – 自定义方法
#pragma mark – get set方法
2. 项目目录文件分类
2.1 每个功能块放入一个Group,在目录里建立实际文件夹管理,不能虚拟目录。公司采用的是MVVM设计模式,每个功能模块应包括以下几个部分
View
ViewControlle
Model
ViewModel
2.2 建立Library文件夹,所有第三方库放入其中
2.3 建立Common文件夹,所有模块公用的放入其中
2.4 建立Resources文件夹,资源文件放入其中
2.5 建立SRC文件夹,源代码文件放入其中
3. 引入其它类时,若要作为实例变量的在.h中引入。否则在.m中引入。
a:声明实例变量一律以属性声明。
b:其它类要访问的实例变量和方法在.h文件中声明,否则声明于.m文件中。
c:实例变量及方法以功能块放在一起,实现一个功能的连续着放在一起,另一个功能的空一行开始声明。
4. 建议使用“#pragma mark”,方便阅读代码
5. 界面初始化和布局如果用code实现,需要写到单独方法里面。不要把过多逻辑写在viewDidLoad
说明:这样实现比较清晰。
正例
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.viewModel=LoginViewModel.new;
self.title=@"登录";
[self initViews];
[self autolayoutViews];
[self bindViews];
};
-(void)initViews
{
[self.view addSubview:self.usernameTextField];
[self.view addSubview:self.passwordTextField];
[self.view addSubview:self.loginButton];
[self.view addSubview:self.finpwdButton];
[self.view addSubview:self.registerButton];
}
反例
-(void) viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UITextField *usernameTextField = UITextField.new;
usernameTextField.backgroundColor = [UIColor clearColor];
usernameTextField.delegate=self;
usernameTextField.placeholder=@"请输入手机";
usernameTextField.font=[UIFont systemFontOfSize:16];
[self.view addSubview:self.usernameTextField];
}
6. 代码基本格式
6.1 if、else、else if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 { }。
说明:这样可以防止书写失误,也易于阅读。
正例
if (varible1 < varible2)
{
varible1 = varible2;
}
反例:下面的代码执行语句紧跟if的条件之后,而且没有加{},违反规则。
if (varible1 < varible2) varible1 = varible2;
6.2 { 和 }独占一行
说明:这样可以防止书写失误,也易于阅读。
正例:
if (varible1 < varible2)
{
varible1 = varible2;
}
反例:
if (varible1 < varible2) {
varible1 = varible2;
}
6.3 定义指针类型的变量,*应放在变量前。
正例:
float *pfBuffer;
反例:
float* pfBuffer;
6.4 当参数过长时,每个参数占用一行,以冒号对齐。
- (void)writeFisrtNumber:(NSString *)firstStr
withNextNumber:(NSString *)nextStr
withLastNumber:(NSString *)lastStr
{
}
6.5 程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。{ }之内的代码块使用缩进规则对齐。
6.6 注释符与注释内容之间要用一个空格进行分隔。
6.7 方法名与形参不能留空格,返回类型与方法标识符有一个空格。
6.8 关键字之后要留空格,函数名之后不要留空格。
6.9 声明类或方法时,注意空格的使用,参数过多时可换行保持对齐。
6.10 长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首,拆分出的新行要进行适当的缩进,使排版整齐。
说明:条件表达式的续行在第一个条件处对齐。
for循环语句的续行在初始化条件语句处对齐。
函数调用和函数声明的续行在第一个参数处对齐。
赋值语句的续行应在赋值号处对齐。
正例:
if ((iFormat == CH_A_Format_M)
&& (iOfficeType == CH_BSC_M)) // 条件表达式的续行在第一个条件处对齐
{
doSomething();
}
for (long_initialization_statement;
long_condiction_statement; // for循环语句续行在初始化条件语句处对齐
long_update_statement)
{
doSomething();
}
// 函数声明的续行在第一个参数处对齐
BYTE ReportStatusCheckPara(HWND hWnd,
BYTE ucCallNo,
BYTE ucStatusReportNo);
// 赋值语句的续行应在赋值号处对齐
fTotalBill = fTotalBill + faCustomerPurchases[iID]
+ fSalesTax(faCustomerPurchases[iID]);
6.11 函数(方法)声明时,类型与名称不允许分行书写
正例:
extern double FAR CalcArea(double dWidth, double dHeight);
反例:
extern double FAR CalcArea(double dWidth, double dHeight)
;
6.12 建议换行规则
A) .h中的空行
1、文件说明与头文件包含(#import)之间空1行
2、头文件包含(#import)之间,如果需要分类区别,各类别之间空1行。
3、头文件包含(#import)与@class之间空1行。
4、@interface与@class之间空1行。
5、头文件{}里面,空1行开始声明对象成员,如果需要分类区别,各类别之间空1行。
6、头文件{}外,空1行书写属性,如果需要分类区别,各类别之间空1行。
7、属性下面空1行开始写方法,如果需要分类区别,各类别之间空1行。
8、方法完成后,空1行@end。
9、如果需要声明protocol,空2行接着写。通常protocol写在@end后面,但是声明在@interface之前。
B) .m中的空行
1、文件说明与头文件包含(#import)之间空1行
2、头文件包含(#import)之间,如果需要分类区别,各类别之间空1行。
3、@implementation和@synthesize之间空1行, 如果需要分类区别,各类别之间空1行。
4、@synthesize与方法之间空1行。
5、方法与方法之间空1行。
C) 方法里面的空行
1、变量声明后需要空1行,如果需要分类区别,各类别之间空1行。
2、条件、循环,选择语句,整个语句结束,需要空1行。
3、各功能快之间空1行。
4、最后一个括弧之前不空行。
5、注释与代码之间不空行。
6、#pragma mark 与方法之间空1行。
7. 注释
7.1 多行注释采用“/* … */”,单行注释采用“// …”
7.2 一般情况下,源程序有效注释量必须在30%以上。
说明:注释的原则是有助于对程序的阅读理解,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。有效的注释是指在代码的功能、意图层次上进行注释,提供有用、额外的信息。
7.3 注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。
说明:在使用缩写时或之前,应对缩写进行必要的说明。
正例:
如下书写比较结构清晰
/* 获得子系统索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
反例1:
如下例子注释与描述的代码相隔太远。
/* 获得子系统索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
反例2:
如下例子注释不应放在所描述的代码下面。
iSubSysIndex = aData[iIndex].iSysIndex;
/* 获得子系统索引 */
反例3:
如下例子,显得代码与注释过于紧凑。
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
7.4 全局变量要有详细的注释,包括对其功能、取值范围、访问信息及访问时注意事项等的说明。
正例:
/*
* 变量作用说明
* 变量值说明
*/
BYTE g_ucTranErrorCode;
7.5 注释与所描述内容进行同样的缩排。
说明:可使程序排版整齐,并方便注释的阅读与理解。
正例:
如下注释结构比较清晰
- (int)doSomething
{
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
}
反例:
如下例子,排版不整齐,阅读不方便;
int DoSomething(void)
{
/* 代码段1注释 */
[ 代码段1 ]
/* 代码段2注释 */
[ 代码段2 ]
}
7.6 尽量避免在注释中使用缩写,特别是不常用缩写。
说明:在使用缩写时,应对缩写进行必要的说明。
8. 命名
8.1 标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。
说明:标识符应当直观且可以拼读,可望文知义,避免使人产生误解。程序中的英文单词一般不要太复杂,用词应当准确。
8.2 严格禁止使用连续的下划线,下划线也不能出现在标识符头或结尾。
说明:这样做的目的是为了使程序易读。因为 variable_name 和 variable__name 很难区分,下划线符号‘’若出现在标识符头或结尾,容易与不带下划线‘’的标识符混淆。
8.3 预编译开关的定义使用下划线 ‘_’ 开始。
8.4 尽量避免名字中出现数字编号,如Value1、Value2等,除非逻辑上的确需要编号。
8.5 资源命名
App 里的拥有 4 种属性,分別为一般、点击、不能点击、选中。 _selected _highlight _disabled,一般提供普通状态 不带后缀
icon icon_xxx.png
导航栏 nav_xxx.png。
标签栏 tab_xxx.png tab_xxx_selected.png。
按钮
btn_xxx.png
btn_xxx_selected.png
btn_xxx_highlight.png
btn_xxx_disabled.png
背景图
bg_xxx.png bg_xxx_selected.png
8.6 类
8.6.1 类名用大写字母开头的单词组合而成
所有的类名,接口名(Protocol)均以大写字母开头,多单词组合时,后面的单词首字母大写。类,接口名必须是有意义的。
8.6.2 类名必须符合规范。
1) 继承自UIView的类以View结尾。其他类似。
例如:
OperatorUsersInfomationView,LabelView等。
2) 继承自ViewController的类以viewController结尾。
例如:
HomePageViewController,LoginViewController等。其他类推。
3) 所有保存数据的实体以Model结尾。
例如:
UserModel
8.6.3 在编写派生类的赋值时,注意不要忘记对基类的成员变量重新赋值。
说明:除非在派生类中调用基类的赋值函数,否则基类变量不会自动被赋值。
正例:
- (void)viewDidLoad
{
[super viewDidLoad];
}
8.6.4 避免调用new方法
不要调用NSObject 的new方法,也不要在子类中重写它,而是应该使用 alloc 和 init 方法来初始化retained的对象。
Objective-C代码显式调用 alloc 和 init 方法来创建和retain一个对象。new 的方法可能会带来内存上调试的麻烦。
8.7 方法
8.7.1 同时取值方法前不要加前缀“get”
8.7.1 方法名用小写字母开头的单词组合而成。采用驼峰法命名,第一个单词小写,其他大写。
说明:方法名力求清晰、明了,通过方法名就能够判断方法的主要功能。方法名中不同意义字段之间不要用下划线连接,而要把每个字段的首字母大写以示区分。方法命名采用大小写字母结合的形式,但专有名词不受限制。
8.8 变量
- 变量必须起有意义的名字,使其他组员可以很容易读懂变量所代表的意义,变量命名可以采用同义的英文命名,可使用几个英文单词,第一个单词首字母小写,其他单词首字母大写。
- 对于一些特殊类型的变量,命名时要带上类型,如NSArray 的变量命名为xxxArray,其他的如xxxDictionary,xxxSize等。这样就可以从名称上知道是什么类型的变量。
- 对于系统的常用类作实例变量声明时加入后缀:
UIViewController: ViewController
UIImage:image
UIImageView:ImageView
UIView:View
UILabel:Label
UIButton:Button
UINavigationBar: NavigationBar
UIToolBar:ToolBar
UISearchBar:SBar
UITextField:TextField
UITextView:TextView
NSArray:Array
NSMutableArray:MArray
NSDictionary:Dict
NSMutableDictionary:MDict
NSString:Str
NSMutableString:MStr
NSSet:Set
NSMutableSet:MSet
- 实例变量声明时变量名前面加下划线“_”,局部变量不用加。
8.9 常量
变量、常量和数据类型是程序编写的基础,它们的正确使用直接关系到程序设计的成败,变量包括全局变量、局部变量和静态变量,常量包括数据常量和指针常量,类型包括系统的数据类型和自定义数据类型。本章主要说明变量、常量与类型使用时必须遵循的规则和一些需注意的建议,关于它们的命名,参见命名规则。
8.9.1 对于全局变量通过统一的函数访问
说明:可以避免访问全局变量时引起的错误。
8.9.2 常量(预定义,枚举,局部常量等)使用小写k开头的驼峰法。
比如kInvalidHandle,kWritePerm。
8.10 宏
8.10.1 宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。
说明:在宏定义中,对表达式和变量使用括号,可以避免可能发生的计算错误。
正例:
#define HANDLE(A, B) (( A ) / ( B ))
反例:
#define HANDLE(A, B) (A / B)
8.10.2 宏名大写字母。
正例:
#define BUTTON_WIDTH (int)320
反例:
#define kButtonWidth (int)320
8.10.3 宏常量要指定类型。
说明:不同的编译器,默认类型不一样。
正例:
#define BUTTON_WIDTH (int)320
反例:
#define BUTTON_WIDTH 320
8.11 建议书写属性名不要和系统一样,避免发生莫名其妙的问题;特别注意的是label;属性名不要写成textLabel。
8.12 项目中添加plist类型文件,不要命名为info.plist,以防止和系统自带的文件重名,发生莫名其妙的问题。
9. 表达式与语句
9.1 一条语句只完成一个功能。变量定义时,一行只定义一个变量。
说明:复杂的语句阅读起来,难于理解,并容易隐含错误。
正例:
int iHelp;
int iBase;
int iResult;
iHelp = iBase;
iResult = iHelp + GetValue(&iBase);
反例:
int iBase, iResult; // 一行定义多个变量
iResult = iBase + GetValue(&iBase); // 一条语句实现多个功能,iBase有两种用途。
9.2 在表达式中使用括号,使表达式的运算顺序更清晰。
说明:由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。
**正例: **
if (((iYear % 4 == 0) && (iYear % 100 != 0)) || (iYear % 400 == 0))
反例:
if (iYear % 4 == 0 && iYear % 100 != 0 || iYear % 400 == 0)
9.3 不可将布尔变量和逻辑表达式直接与YES、NO或者1、0进行比较。
说明:TURE和FALSE的定义值是和语言环境相关的,且可能会被重定义的。
正例:
设bFlag 是布尔类型的变量
if (bFlag) // 表示flag为真
if (!bFlag) // 表示flag为假
反例:
设bFlag 是布尔类型的变量
if (bFlag == TRUE)
if (bFlag == 1)
if (bFlag == FALSE)
if (bFlag == 0)
9.4 在条件判断语句中,当整型变量与0 比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。
正例:
if (iValue == 0)
if (iValue != 0)
反例:
if (iValue) // 会让人误解 iValue是布尔变量
if (!iValue)
9.5 不可将浮点变量用“==”或“!=”与任何数字比较。
说明:无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该转化成“>=”或“<=”形式。
正例:
if ((fResult >= -EPSINON) && (fResult <= EPSILON))
反例:
if (fResult == 0.0) // 隐含错误的比较
其中EPSINON是允许的误差(即精度)。
9.6 应当将指针变量用“==”或“!=”与nil比较。
说明:指针变量的零值是“空”(记为NULL),即使NULL的值与0相同,但是两者意义不同。
正例:
if (pHead == nil) // pHead与NULL显式比较,强调pHead是指针变量
if (pHead != nil)
反例:
if (pHead == 0) // 容易让人误解pHead是整型变量
if (pHead != 0)
或者
if (pHead) // 容易让人误解pHead是布尔变量
if (!pHead)
9.7 在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。
说明:避免漏掉break语句造成程序错误。同时保持程序简洁。
对于多个分支相同处理的情况可以共用一个break,但是要用注释加以说明。
正例:
switch (iMessage)
{
case SPAN_ON:
{
[处理语句]
break;
}
case SPAN_OFF:
{
[处理语句]
break;
}
default:
{
[处理语句]
break;
}
}
9.8 不可在for 循环体内修改循环变量,防止for 循环失去控制。
9.9 for语句的循环控制变量的取值采用“半开半闭区间”写法。
说明:这样做更能适应c语言数组的特点,c语言的下标属于一个“半开半闭区间”。
正例:
int aiScore[NUM];
…
for (i = 0; i < NUM; i++)
{
printf(“%d\n”,aiScore[i])
}
反例:
int aiScore[NUM];
…
for (i = 0; i <= NUM-1; i++)
{
printf(“%d\n”,aiScore[i]);
}
相比之下,正例的写法更加直观,尽管两者的功能是相同的。
10. 函数、方法、接口
10.1 避免函数有太多的参数,参数个数尽量控制在5个以内。
说明:如果参数太多,在使用时容易将参数类型或顺序搞错,而且调用的时候也不方便。如果参数的确比较多,而且输入的参数相互之间的关系比较紧密,不妨把这些参数定义成一个结构,然后把结构的指针当成参数输入。
10.2 对于有返回值的函数(方法),每一个分支都必须有返回值。
说明:为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值。
10.3 对输入参数的正确性和有效性进行检查。
说明:很多程序错误是由非法参数引起的,我们应该充分理解并正确处理来防止此类错误。
10.4 函数(方法)的功能要单一,不要设计多用途的函数(方法)。
说明:多用途的函数往往通过在输入参数中有一个控制参数,根据不同的控制参数产生不同的功能。这种方式增加了函数之间的控制耦合性,而且在函数调用的时候,调用相同的一个函数却产生不同的效果,降低了代码的可读性,也不利于代码调试和维护。
10.5 函数(方法)体的规模不能太大,尽量控制在200行代码之内。
说明:冗长的函数不利于调试,可读性差。
11. 头文件
11.1 如果不是确实需要,应该尽量避免头文件包含其它的头文件。
说明:头文件中应避免包含其它不相关的头文件,一次头文件包含就相当于一次代码拷贝。
11.2 申明成员类,应该引用该类申明,而不是包含该类的头文件。
正例:
@class SubClassName;
@interface ClassName : NSObject
{
SubClassName *_pSubClassName;
}
反例:
#import “SubClassName.h”;
@interface ClassName : NSObject
{
SubClassName *m_pSubClassName;
}
12. 可靠性
为保证代码的可靠性。
12.1 内存使用
12.1.1 防止内存操作越界。
说明:内存操作主要是指对数组、指针、内存地址等的操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细。
12.1.2 必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为nil。
12.1.3 变量在使用前应初始化,防止未经初始化的变量被引用。
说明:不同的编译系统,定义的变量在初始化前其值是不确定的。有些系统会初始化为0,而有些不是。
12.2 指针使用
12.2.1 指针类型变量必须初始化为nil。
12.2.2 指针不要进行复杂的逻辑或算术操作。
说明:指针加一的偏移,通常由指针的类型确定,如果通过复杂的逻辑或算术操作,则指针的位置就很难确定。
12.2.3 减少指针和数据类型的强制类型转化。
12.2.4 对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。
说明:尤其对全局变量赋值时,应进行合法性检查,以提高代码的可靠性、稳定性。
12.2.5 非初始化方法中的alloc操作之前必须要nil判断。
12.2.6 全局指针释放后置为nil值。
13. 其它补充
13.1 避免过多直接使用立即数。
正例:
ViewBounds.size.height = VIEW_BOUNDS_HEIGHT;
反例:
ViewBounds.size.height = 150; Height = 150;
13.2 枚举第一个成员要赋初始值。
正例:
typedef enum
{
WIN_SIZE_NORMAL = 0,
WIN_SIZE_SMALL
}WinSize;
反例:
typedef enum
{
WIN_SIZE_NORMAL,
WIN_SIZE_SMALL
}WinSize;
13.3 addObject之前要非空判断。
13.4 release版本代码去掉NSLog打印,除了保留异常分支的NSLog。
13.5 对于框架设计,逻辑层应尽量与UI层分离,降低耦合度。
13.6 同等难度下,优先考虑代码实现窗体创建。
说明:代码实现窗体创建容易移植,优先考虑代码实现来替代xib实现
13.7 初始化成员变量时可以考虑用代码块来创建,精简代码。
说明:这个方法有一个优点,所有的变量都在代码块中,也就是只在代码块的区域中有效,这意味着可以减少对其他作用域的命名污染,但缺点是可读性比较差。
正例:
self.opView1 = ({
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((self.view.frame.size.width-100)/2, (self.view.frame.size.height-100)/2-100, 100, 100)];
imageView.image = [UIImage imageNamed:@"93F5E460-9D31-4780-8511-37FF91033402"];
[self.view addSubview:imageView];
imageView;
});
13.8 避免循环引用
说明:如果【block内部】使用【外部声明的强引用】访问【对象A】, 那么【block内部】会自动产生一个【强引用】指向【对象A】
如果【block内部】使用【外部声明的弱引用】访问【对象A】, 那么【block内部】会自动产生一个【弱引用】指向【对象A】
正例:
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil(抢占的时候,strongSelf 还是非 nil 的)
[strongSelf doSomethingElse]; // strongSelf != nil }
else { // Probably nothing... return;
}
};
反例:
__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
[weakSelf doSomething]; // weakSelf != nil
// preemption, weakSelf turned nil
[weakSelf doSomethingElse]; // weakSelf == nil
};
13.9 建议加载xib,xib名称用NSStringFromClass(),避免书写错误
13.10 场景需求:在继承中,凡是要求子类重写父类的方法必须先调用父类的这个方法进行初始化操作;建议:父类的方法名后面加上NS_REQUIRES_SUPER; 子类重写这个方法就会自动警告提示要调用这个super方法。
正例:
// 注意:父类中的方法加`NS_REQUIRES_SUPER`,子类重写才有警告提示
- (void)prepare NS_REQUIRES_SUPER;
13.11 id类型属性不能用点语法,调用get方法只能用中括号调用,[id 方法名],利用iOS9新特性泛型就可以; 比如数组。
13.12 如果不是属性,尽量不要点语法,一个老程序员的建议。
13.13 使用第三方框架,尽量不要更改内部文件,而应该再次封装,个性定制。
13.14 接手一个新项目,快速的调试,查看某个模块或者方法的作用,需要注释掉一个方法,或者某个代码块,直接写return;而不是全选,注释掉
13.15 监听键盘的通知建议:
UIKIT_EXTERN NSString *const UIKeyboardWillChangeFrameNotification
而不是,下面代码;因为键盘可能因为改变输入法,切换成表情输入,切换成英文,那么frame可能会变高,变矮,不一定会发出下面这些通知,但是肯定会发上面的通知
UIKIT_EXTERN?NSString *const UIKeyboardWillShowNotification;
UIKIT_EXTERN?NSString *const UIKeyboardDidShowNotification;
UIKIT_EXTERN?NSString *const UIKeyboardWillHideNotification;
UIKIT_EXTERN?NSString *const UIKeyboardDidHideNotification;
13.16 大量操作图层会可能造成应用很卡,给用户体验差,所以尽量不要操作图层;比如设置按钮圆角,比如给button设置圆角。
13.17 给分类扩充方法,建议加上前缀,比如第三方框架SDWebImage,这样做跟系统的方法很容易区分开,减少了程序员之间的沟通成本,同理跟分类添加属性(利用运行时),建议加前缀,以防止苹果官方过一段时间添加了一模一样的属性名,比如给UITextField分类添加了placeholderColor这个属性,万一某天官方给placeholder扩充了这个命名一模一样的属性,那么就不好了。
13.18 抽取方法,或者写工具类,能写类方法,尽量写成类方法,减少了创建对象的步骤,比如给UIView扩充分类加载xib,viewWithXib。
13.19 声明一个属性,如果是对象,比如数组,不能以new单词开始,否则直接报错,因为new在OC中是生成一个对象的方法,有特殊含义;比如:
@property (nonatomic,strong) NSMutableArray *newTopicsM;
注意:如果newtopicsM是一个单词(区别于驼峰标志),这样写不会报错;如果是基本数据类型则不会报错,比如
@property (nonatomic,assign) int newNumber;
13.20 在自定义方法中,and这个词的用法应该保留。它不应该用于多个参数来说明,就像initWithWidth:height以下这个例子:
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
而不应该
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;