当前位置: 首页 > 科技观察

IOSTeamProgrammingSpecification

时间:2023-03-16 16:45:47 科技观察

前言需求是暂时的,只有变化才是永恒的,面向变化的编程,而不是面向需求的编程。不要过分追求技巧,降低程序的可读性。干净的代码使错误无法隐藏。编写明显没有错误的代码,而不是编写明显没有错误的代码。先解决眼前的问题,解决好,再考虑以后的扩展问题。一、命名规范1、统一要求要求含义明确,尽量不加注解理解其作用。如果不是,请添加注释,使用全名,不要使用缩写。2、类名大驼峰命名:每个单词首字母大写==例子:==MFHomePageViewController3、私有变量私有变量放在.m文件中声明以_开头,第一个单词首字母是小写的,后面单词的首字母全部大写。==例子:==NSString*_somePrivateVariable4,驼峰命名的属性变量:第一个单词以小写字母开头,后面单词的首字母全部大写。properties的关键字按照原子性、读写、内存管理依次推荐。Block和NSString属性应该使用copy关键字禁止使用synthesize关键字==例子:==typedefvoid(^ErrorCodeBlock)(iderrorCode,NSString*message);@property(nonatomic,readwrite,strong)UIView*headerView;//Note@property(nonatomic,readwrite,copy)ErrorCodeBlockerrorBlock;//将块复制到堆中@property(nonatomic,readwrite,copy)NSString*userName;5.宏和常量命名对于宏定义的常量#define预处理定义的常量全部大写,单词之间用_隔开。如果宏定义中有表达式或变量,则表达式或变量必须用括号括起来。对于类型常量对于限定在某个编译单元(实现文件)的常量,以字符k开头,比如kAnimationDuration,需要用staticconst修饰对于在类头文件中定义的对外可见的常量,使用类wheretheconstantisdefined在类名开头,比如EOCViewClassAnimationDuration,遵循Apple风格,在头文件中做一个extern声明,在实现文件中定义它的值==示例:==//定义的常量通过宏#defineANIMATION_DURATION0.3#defineMY_MIN(A,B)((A)>(B)?(B):(A))//局部类型常量staticconstNSTimeIntervalkAnimationDuration=0.3;//外部可见类型常量//EOCViewClass。hexternconstNSTimeIntervalEOCViewClassAnimationDuration;externNSString*constEOCViewClassStringConstant;//字符串类型//EOCViewClass。mconstNSTimeIntervalEOCViewClassAnimationDuration=0.3;NSString*constEOCViewClassStringConstant=@"EOCStringConstant";6、Enum枚举类型的命名与类的命名规则一致。Enum中枚举内容的名称需要以Enum类型的名称开头。NS_ENUM定义了一个通用的枚举,NS_OPTIONS定义了一个位移枚举例如:==typedefNS_ENUM(NSInteger,UIViewAnimationTransition){UIViewAnimationTransitionNone,UIViewAnimationTransitionFlipFromLeft,UIViewAnimationTransitionFlipFromRight,UIViewAnimationTransitionCurlUp,UIViewAnimationTransitionCurlDown,};typedefNS_OPTIONS(NSUInteger){U,IControlStateNormal=0,UIControlStateHighlighted=17,Delegate使用delegate作为后缀,比如使用optional修饰不能实现的方法,使用required修饰必须实现的方法当你的delegate方法太多时,可以拆分数据部分和其他逻辑部分,数据部分以dataSource为后缀。如果使用did和will通知Delegate已经发生或将要发生的变化,类的实例必须是回调方法的参数之一。如果回调方法的参数只是类本身,那么方法名必须符合实际含义如果回调方法中有两个以上的参数,则以类名开头,表示该方法属于哪个类==Example:==@protocolUITableViewDataSource@required//回调方法中有两个以上的参数-(NSInteger)tableView:(UITableView*)tableViewnumberOfRowsInSection:(NSInteger)section;@optional//回调方法的参数只有类本身-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView;//Defaultis1ifnotimplemented@protocolUITableViewDelegate@optional//Use`did`and`will`Notification`Delegate`-(nullableNSIndexPath*)tableView:(UITableView*)tableViewwillSelectRowAtIndexPath:(NSIndexPath*)indexPath;-(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath;不要使用new作为方法名称的前缀,也不要使用and连接属性参数。如果该方法描述了两个独立的行为,请使用和将它们连接起来。方法实现时,如果参数过长,每个参数占一行,用冒号对齐。一般方法不使用前缀名,私有方法可以使用统一的前缀来分组和标识方法名必须与对应的表达对象行为的参数名方法高度一致,执行方法要以动词开头返回方法要以return开头,但是做不要在get之前添加,除非它是间接返回一个或多个值。可以用情态动词(动词前的can、should、will等)进一步说明定语的意思,但不要用do、does,因为这些助动词没有实际意义。也不要用副词或形容词来修饰==例子:==//不要用and来连接属性参数-(int)runModalForDirectory:(NSString*)pathfile:(NSString*)nametypes:(NSArray*)fileTypes;//recommended-(int)runModalForDirectory:(NSString*)pathandFile:(NSString*)nameandTypes:(NSArray*)fileTypes;//反对//表示对象行为的方法,一个可执行的方法-(void)insertModel:(id)modelatIndex:(NSUInteger)atIndex;-(void)selectTabViewItem:(NSTableViewItem*)tableViewItem//返回方法-(instancetype)arrayWithArray:(NSArray*)array;//参数过长的情况-(void)longMethodWith:(NSString*)theFoorect:(CGRect)theRectinterval:(CGFloat)theInterval{//实现}//不加get-(NSSize)cellSize;//推荐-(NSSize)getCellSize;//反对//使用情态动词,做不使用doordoes-(BOOL)canHide;//recommended-(BOOL)shouldCloseDocument;//recommended-(BOOL)doesAcceptGlyphInfo;//against2.代码注释规范优秀的代码大多是自描述的,我们can使用代码本身表达了它在做什么,而不需要注释的帮助。但这并不意味着你不能写评论。以下三种情况比较适合写注释:公共接口(注释要告诉阅读代码的人当前类可以实现什么功能)。涉及比较深的专业知识的代码(注释要体现实现原理和思路)。容易产生歧义的代码(但严格来说,容易产生歧义的代码是不允许的)。除了以上三种情况,其他人如果只能靠注释来理解你的代码,那一定要反思代码哪里出了问题。***,对于评论的内容,应该解释“你为什么这么做”而不是“你做了什么”。1.importcomments如果有多个import语句,则将这些语句分组,每组的comments是可选的。//Frameworks#import;//Models#import"NYTUser.h"//Views#import"NYTButton.h"#import"NYTUserView.h"2.属性注释写在属性后面,两个空格隔开==示例:==@property(nonatomic,readwrite,strong)UIView*headerView;//注3、方法声明注:一个函数(方法)必须有一个字符串文档来说明,除非它:非公共、私有函数。很短。明显的。其余部分,包括公共接口、重要方法、类别和协议,应附有文档(注释):第二行以/开头第二行是总结语句第三行始终为空白并与开头对齐第二行写剩下的评论。建议这样写:/Thiscommentservestodemonstratetheformatofadocstring。注意summaryline总是最多stonelinelong,并且在openingblockcomment之后,每行textisprecededbyasinglespace。*/方法注释使用Xcode自带的注释快捷键:Commond+option+/==示例:==/**@paramtableView@paramsection@return*/-(CGFloat)tableView:(UITableView*)tableViewheightForHeaderInSection:(NSInteger)section{//...}4.代码块注释单line以//+空格开头,多行使用/**/注5、TODO使用//TODO:Description来标记一些未完成或不满意的地方==示例:==-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{//TODO:AddinitializationreturnYES;}三、代码格式规范1、在pointer*位置定义对象时,pointer*靠近变量==例子:==NSString*userName;2、方法的声明和定义中-、+和返回值之间留空格,方法名和第一个参数之间不留空格==例子:==-(void)insertSubview:(UIView*)查看索引:(NSInteger)索引;3.代码缩进项目中不要使用Tab键,使用空格进行缩进。在Xcode>Preferences>TextEditing中,将Tab和自动缩进设置为4个空格。Method和Method之间必须有一个空行。一元运算符和变量之间不能有空格,二元运算符和变量之间必须有空格==示例:==!bValuefLength=fWidth*2;-(void)sampleMethod1;-(void)sampleMethod2;4。使用#pragmamark对方法进行分组-对方法进行分组#pragmamark-LifeCycleMethods-(instancetype)init-(void)dealloc-(void)viewWillAppear:(BOOL)animated-(void)viewDidAppear:(BOOL)animated-(void)viewWillDisappear:(布尔)动画-(空)viewDidDisappear:(布尔)动画#pragmamark-OverrideMethods#pragmamark-IntialMethods#pragmamark-NetworkMethods#pragmamark-TargetMethods#pragmamark-PublicMethods#pragmamark-PrivateMethods#pragmamark-UITableViewDataSource#pragmamark-UITableViewDataSource#pragmamark-UITableViewDelegateLazyLoads#pragmamark-NSCopying#pragmamark-NSObjectMethods5,大括号为类的??方法写法:(参照苹果官方文档)其他使用场景(if、for、while、switch等):左括号在**后面*行==示例:==-(void)sampleMethod{BOOLsomeCondition=YES;if(someCondition){//dosomethinghere}}6.属性变量==示例:==@property(nonatomic,readwrite,strong)UIView*headerView;//注4,编码规范1,if语句①,必须列出所有分支(穷举所有情况),每个分支必须给出明确的结果==建议这样写:==varhintStr;if(count<3){hintStr="Good";}else{hintStr="";}==不建议这样写:==varhintStr;if(count<3){hintStr="Good";}②、不要使用如果分支太多,要善于使用return把错误的情况提前返回,把最正确的情况放在***里返回。==建议这样写:==if(!user.UserName)returnNO;if(!user.Password)returnNO;if(!user.Email)returnNO;returnYES;==不建议这样写这:==BOOLisValid=否;if(user.UserName){if(user.Password){if(user.Email)isValid=YES;}}returnisValid;③.如果条件太多,线太长就应该改。如果条件表达式很长,需要把它们提取出来赋值给一个BOOL值,或者提取一个方法==建议这样写:==if(condition1&&condition2&&condition3&&condition4){//Dosomething}BOOLfinalCondition=condition1&&condition2&&condition3&&condition4if(finalCondition){//Dosomething}if([selfcanDelete]){//Dosomething}-(BOOL)canDelete{BOOLfinalCondition1=condition1&&condition2BOOLfinalCondition2=condition3&&condition4returncondition1&&condition2;}==不建议这样写:==if(condition1&&condition2&&condition3&&condition4){/}/Dos语句变量应该在右边,常量在左边。==推荐:==if(6==count){}if(nil==object){}if(!object){}==不推荐:==if(count==6){}if(object==nil){}if(object==nil)容易被写成赋值语句,if(!object)写的很简洁⑤,每个分支的实现代码都要用大括号括起来==建议:==if(!error){returnsuccess;}==不推荐:==if(!error)returnsuccess;可以这样写:if(!error)returnsuccess;2、for语句①、for循环中不要修改循环变量,防止for循环失控。for(intindex=0;index<10;index++){...logicToChange(index)}②,避免使用continue和break。continue和break描述的是“什么时候不做某事”,所以为了理解它们所在的代码,我们需要在脑海中颠倒一下。其实这两个东西最好不要出现,因为我们的代码只需要体现“什么时候做什么”,通过适当的方法,可以消除这两个东西:如果出现continue,只需要将continue的conditionvarfilteredProducts=Array()forlevelinproducts{iflevel.hasPrefix("bad"){continue}filteredProducts.append(level)}我们可以看出,通过判断字符串是否包含“bad”这个前缀来过滤掉一些值。其实我们可以通过取反来避免使用continue:forlevelinproducts{if!level.hasPrefix("bad"){filteredProducts.append(level)}}消除breakinwhile:否定break和merge的条件whilein中的break主循环实际上相当于“不存在”。既然不存在,那么在初始条件语句中就可以完全排除。breakinwhile:while(condition1){...if(condition2){break;}}被否定并合并到主要条件中:while(condition1&&!condition2){...}消除带有返回值的方法中的中断:转换breaktoreturn立即返回。有些人喜欢这样做:在有返回值的方法中中断后,返回某个值。其实直接在break线上返回是完全可以的。funchasBadProductIn(products:Array)->Bool{varresult=falseforlevelinproducts{iflevel.hasPrefix("bad"){result=truebreak}}returnresult}遇到错误情况直接返回:funchasBadProductIn(products:Array)->Bool{forlevelinproducts{iflevel如果.hasPrefix("bad"){returntrue}}returnfalse}这样写,就不需要声明一个变量来保存需要返回的值。看起来非常简洁,可读性很强。3.开关语句①。每个分支都必须用大括号括起来。推荐这样写:switch(integer){case1:{//...}break;case2:{//...break;}default:{//...break;}}②,当使用枚举类型,不能有默认分支,除了枚举类型,必须有默认分支}caseRWTLeftMenuTopItemSchedule:{//...break;}}Switch语句使用枚举类型时,如果使用default分支,以后将无法通过编译器我们来检查一下新添加的枚举类型。四、函数1、一个函数只做一件事(单一原理)。每个功能的职责应该明确划分(就像一个类)。==推荐:==dataConfiguration()viewConfiguration()==不推荐:==voiddataConfiguration(){...viewConfiguration()}②。对于有返回值的函数(方法),每个分支必须有一个返回值==推荐:==intfunction(){if(condition1){returncount1}elseif(condition2){returncount2}else{returndefaultCount}}==不推荐:==intfunction(){if(condition1){returncount1}elseif(condition2){returncount2}}③。检查输入参数的正确性和有效性,参数错误立即返回;}//Dosomerightthing}④.如果不同的函数中有相同的函数,则应将相同的函数提取出来作为另一个函数的原始调用:voidlogic(){a();b();if(logic1condition){c();}else{d();}}将函数a和b提取为单独的函数);}⑤。将函数内部的复杂逻辑提取为单独的函数。往往可以把代码抽出来组成一个新的函数,然后在原来的地方调用,这样就可以用有意义的函数名代替注释,增加程序的可读性。举个发送邮件的例子:openEmailSite();login();writeTitle(title);writeContent(content);writeReceiver(receiver);addAttachment(attachment);send();提取:voidwriteEmail(title,content,receiver,attachment){writeTitle(title);writeContent(content);writeReceiver(receiver);addAttachment(attachment);}再看原代码:openEmailSite();login();writeEmail(标题、内容、收件人、附件)发送();