首页
关于
Search
1
唤端-LaunchApp
276 阅读
2
前端风格指南
197 阅读
3
前端CI/CD简述
155 阅读
4
H5兼容性问题(持续更新...)
145 阅读
5
IOS风格指南
139 阅读
默认分类
前端
javascript
CSS
基础
移动APP
后端
flutter
登录
Search
yu.zhai
有钱终成眷属,没钱亲眼目睹
累计撰写
6
篇文章
累计收到
1
条评论
首页
栏目
默认分类
前端
javascript
CSS
基础
移动APP
后端
flutter
页面
关于
搜索到
1
篇与
的结果
IOS风格指南
iOS Coding Style Guide命名规范我们尽可能遵守 Apple 的命名约定, 其推荐使用长的,描述性强的方法和变量名,使其阅读起来更加清晰易懂。不能随意使用缩写,导致其他人员阅读代码困难。Coding Guidelines for Cocoa前缀项目名称、类名、文件名都应该保持一致的前缀名,NAP iOS项目使用YN作为前缀。类名大驼峰式命名:每个单词的首字母都采用大写字母。@interface YNHomeViewController : YNBaseViewController @endproperty变量小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母全部大写。属性的关键字推荐按照 原子性,内存管理,读写的顺序排列。Block、NSString属性应该使用copy关键字。代理使用weak关键字防止循环引用。@property (nonatomic, strong, readonly) UILabel *subjectLabel; @property (nonatomic, copy) NSString *statusName; @property (nonatomic, weak) id <YNLogisticsProductsCellDelegate>delegate; @property (nonatomic, copy) void(^actionBlock)(__nullable id);枚举Enum中枚举内容的命名需要以该Enum类型名称开头。NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举。typedef NS_OPTIONS(NSUInteger, YNLogLevel){ YNLogLevelError = 1 << 0, YNLogLevelWarning = 1 << 1, YNLogLevelInfo = 1 << 2, YNLogLevelDebug = 1 << 3, YNLogLevelVerbose = 1 << 4 }; typedef NS_ENUM(NSUInteger, YNContentAlignment) { YNContentAlignmentLeft = 0, YNContentAlignmentCenter = 1, YNContentAlignmentRight = 2 };宏和常量对于宏定义的常量define 预处理定义的常量全部大写,单词间用 _ 分隔,宏定义中如果包含表达式或变量,表达式或变量必须用小括号括起来。对于类型常量对于局限于某编译单元(实现文件)的常量,以字符k开头,例如kAnimationDuration,且需要以static const修饰。对于定义于类头文件的常量,外部可见,则以定义该常量所在类的类名开头,例如EOCViewClassAnimationDuration, 仿照苹果风格,在头文件中进行extern声明,在实现文件中定义其值。//宏定义的常量 #define ANIMATION_DURATION 0.3 #define MY_MIN(A, B) ((A)>(B)?(B):(A)) //局部类型常量 static const NSTimeInterval kAnimationDuration = 0.3; //外部可见类型常量 //YNMagazinePresenter.h extern NSString * const YNMagazineChannelTypeFemaleNewest; //YNMagazinePresenter.m NSString * const YNMagazineChannelTypeFemaleNewest = @"1";方法方法名用小驼峰式命名。方法名不要使用new作为前缀。不要使用and来连接属性参数,如果方法描述两种独立的行为,使用and来串接它们。方法实现时,如果参数过长,则令每个参数占用一行,以冒号对齐。一般方法不使用前缀命名,私有方法可以使用统一的前缀来分组和辨识。方法名要与对应的参数名保持高度一致。//不要使用 and 来连接属性参数 - (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; //推荐 - (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; //反对 //表示对象行为的方法、执行性的方法 - (void)insertModel:(id)model atIndex:(NSUInteger)atIndex; - (void)selectTabViewItem:(NSTableViewItem *)tableViewItem //返回性的方法 - (instancetype)arrayWithArray:(NSArray *)array; //参数过长的情况 - (void)longMethodWith:(NSString *)theFoo rect:(CGRect)theRect interval:(CGFloat)theInterval; //不要加get - (NSSize)cellSize; //推荐 - (NSSize)getCellSize; //反对 方法参数不要在参数名中使用 pointer 或 ptr,让参数的类型来说明它是指针避免使用 one, two,...,作为参数名,更不要用1,2,...避免为节省几个字符而缩写采用以下参数标签与参数组合的惯例...action:(SEL)aSelector ...alignment:(int)mode ...atIndex:(int)index ...content:(NSRect)aRect ...doubleValue:(double)aDouble ...floatValue:(float)aFloat ...font:(NSFont *)fontObj ...frame:(NSRect)frameRect ...intValue:(int)anInt ...keyEquivalent:(NSString *0charCode ...length:(int)numBytes ...point:(NSPoint)aPoint ...stringValue:(NSString *)aString ...tag:(int)anInt ...target:(id)anObject ...title:(NSString *)aString代码注释规范优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助。但并不是说一定不能写注释,有以下三种情况比较适合写注释:公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。属性注释写在属性上方,三斜线开头斜线后面跟一个空格后面跟注释内容;可使用Xcode自带模板快捷添加(默认快捷键:⌥+⌘+/)。/// 文章ID,用于下面的更多精彩内容去重 @property (nonatomic, copy) NSArray <NSString *> *articleIds;方法声明注释公开接口,重要的方法,分类,以及协议,都应该伴随文档(注释):三斜线开头,斜线后跟一个空格;第一行为方法描述,第二行开始为方法参数以@param标记(默认快捷键:⌥+⌘+/)。/// 快速生成渐变图层 /// @param frame 图层位置大小 /// @param beginColor 起始颜色 /// @param endColor 结束颜色 + (CAGradientLayer *)gradientLayerWithFrame:(CGRect )frame beginColor:(UIColor *)beginColor endColor:(UIColor *) endColor;TODO使用// TODO: 说明 标记一些未完成的或完成的需要优化的地方。// TODO: restore scrollView state // This is temporary solution. Have to implement the save and restore scrollView state UIScrollView *superscrollView = strongLastScrollView;FIXME使用// FIXME: 说明 标记一些有bug的或以临时方案解决的地方。(使用// FIXME: 标记在检索器会出现提醒图)// FIXME: 等待接口字段 // FIXME: someday check the return codes on these binds.代码格式规范指针*位置跟在类型后面用空格隔开紧跟着变量名。NSURL *url; - (id)initWithChildren:(NSArray *)children;方法声明和实现+、-后面跟一个空格,方法名和第一个参数之间不留空格;方法体的第一个大括号跟在方法名末尾用空格隔开。// 方法声明 - (id)initWithChildren:(NSArray *)children; + (instancetype)actionWithTitle:(NSString *)title style:(UIPreviewActionStyle)style handler:(void (^)(UIPreviewAction *action, UIViewController *previewViewController))handler; // 方法实现 - (id)initWithChildren:(NSArray *)children { self = [super initWithChildren:children]; return self; } + (instancetype)actionWithTitle:(NSString *)title style:(UIPreviewActionStyle)style handler:(void (^)(UIPreviewAction *action, UIViewController *previewViewController))handler { UIPreviewAction *action = [[UIPreviewAction alloc] init]; action.title = title; ... return action; }对implementation方法进行分组#pragma mark - Life cycle - (void)viewDidLoad { [super viewDidLoad]; } #pragma mark - Private methods #pragma mark - Delegates #pragma mark - Event response #pragma mark - Setter #pragma mark - Getter编码规范if语句尽量列出所有分支(穷举所有的情况),而且每个分支都须给出明确的结果。不要使用过多的分支,要善于使用return来提前返回错误的情况,把最正确的情况放到最后返回。条件过多,过长的时候应该换行;条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值,或者抽取出一个方法。NSString *hintStr; if (count < 3) { hintStr = @"Good"; } else { hintStr = @""; }if (!user.userName) return NO; if (!user.password) return NO; if (!user.email) return NO; return YES;if (condition1 && condition2 && condition3) { // Do something } BOOL finalCondition = condition1 && condition2 && condition3 && condition4 if (finalCondition) { // Do something } if ([self canDelete]){ // Do something } - (BOOL)canDelete { BOOL finalCondition1 = condition1 && condition2 BOOL finalCondition2 = condition3 && condition4 return condition1 && condition2; }for循环避免在for循环内修改循环变量,防止for循环失去控制。如果必须修改,可以创建一个临时变量进行遍历或者使用NSArray的enumerateObjectsUsingBlock:方法进行遍历。NSArray *arr = [NSArray arrayWithArray:mArr]; for (NSMutableDictionary *dic in arr) { if ([dic[@"a"] isEqualToString:@"3"]) { [mArr removeObject:dic]; } } [mArr enumerateObjectsUsingBlock:^( id obj, NSUInteger idx, BOOL *stop) { if ([[obj objectForKey: @"a"] isEqualToString:@"3"]) { *stop = YES ; [mArr removeObject:obj]; } }];方法单一性原则,每个函数的职责都应该划分的很明确(就像类一样)。对于有返回值的方法,每一个分支都必须有返回值。对输入参数的正确性和有效性进行检查。如果在不同的方法内部有相同的功能,应该把相同的功能抽取出来单独作为另一个方法。将方法内部比较复杂的逻辑提取出来作为单独的方法。// 推荐写法 - (void)viewDidLoad { [super vidwDidLoad]; [self setupUI]; [self prepareData]; } - (void)setupUI { } - (void)prepareData { } // 不推荐 - (void)viewDidLoad { [super vidwDidLoad]; [self build]; } - (void)build { // setupUI ... // prepareData ... }// 推荐写法 - (int)function { if(condition1){ return count1 }else if(condition2){ return count2 }else{ return defaultCount } } // 不推荐 - (int)function { if(condition1){ return count1 }else if(condition2){ return count2 } }- (void)functionWithParam:(id)param { if([param isUnavailable]){ return; } //Do some right thing }属性和变量数据成员保持最小公开原则在不需要改变一个属性时,添加readonly最好使用auto-synthesis。当使用属性,实例变量时应该使用self.来访问,这意味着所有的属性将很容易区分,因为它们都使用 self. 开头nil / Nil / NULL / NSNullObjective-C中默认所有的指针指向nil,nil最显著的行为是可以有消息发送给它,结果返回0if (name != nil && [name isEqualToString:@"Steve"]) { ... } // …可以被简化为: if ([name isEqualToString:@"steve"]) { ... }SymbolValueMeaningNULL(void *)0literal null value for C pointersnil(id)0literal null value for Objective-C objectsNil(Class)0literal null value for Objective-C classesNSNull[NSNull null]singleton object used to represent nullBooleansObjective-C用BOOL来编码真值。它是signed char的typedef,并且用宏YES和NO来相应的表示真和假;在Objective-C中,当遇到处理真值的参数,属性和实例变量时,使用类型BOOL。当分配字面值时,使用宏YES和NO;如果BOOL属性的名称表示为一个形容词,该属性可以省略“is”字头,但需要为get方法按照常规指定名称。@property (assign, getter=isEditable) BOOL editable;判断真假时不要与字面值比较// 推荐 if (someObject) { } if (![anotherObject boolValue]) { } // 不推荐 if (someObject == nil) {} if ([anotherObject boolValue] == NO) {} if (isAwesome == YES) {} // Never do this. if (isAwesome == true) {} // Never do this.NameTypedefHeaderTrue ValueFalse ValueBOOLsigned charobjc.hYESNObool_Bool (int)stdbool.htruefalsebooleanunsigned charMacTypes.hTRUEFALSENSNumber__NSCFBooleanFoundation.h@(YES)@(NO)CFBooleanRefstructCoreFoundation.hkCFBooleanTruekCFBooleanFalse团队约定字典类型参数使用YNParameters进行包装,可以避免空数据带来的crash。YNParameters *parameter = [YNParameters new]; [parameter addParameter:@"order_id" value:order_id]; [parameter addParameter:@"current_page" value:current_page]; [parameter addParameter:@"order_type" value:orderType]; [YNSensorsAnalytic trackEvent:YNSensorsAnalytic_OrderPageOrderDelete properties:parameter.parameterDict];数组取值使用YYKit的objectOrNilAtIndex:方法,避免数组越界。__kindof UIViewController *viewController = [self.viewControllers objectOrNilAtIndex:index];字符串文案使用yn_safeString:方法进行转换,避免显示异常。NSString *title = [NSString yn_safeString:model.title];使用枚举定义有限类型而非简单数字判断。typedef NS_ENUM(NSUInteger, YNContentAlignment) { YNContentAlignmentLeft = 0, YNContentAlignmentCenter = 1, YNContentAlignmentRight = 2, }; if (aligment == YNContentAlignmentCenter) { ... } else { ... } switch (aligment) { case YNContentAlignmentCenter:{ ... break; } case YNContentAlignmentRight:{ ... break; } default: { ... break; } } // 不推荐 if (aligment == 2) { ... } else { ... } switch (aligment) { case 1:{ ... break; } case 2:{ ... break; } default: { ... break; } }所有测试代码使用DEBUG宏包起来,不将测试代码上传到远程仓库。#if DEBUG - (void)mock { self.title = @"PDP"; self.brand = @"NET-A-PORTER"; } #endif所有接口地址统一在YNNetInterface进行维护。
2022年12月04日
139 阅读
0 评论
0 点赞