1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 | ## 命名规范 1.iOS命名原则 * 可读性高、可复用、易维护、可扩展 * 防止命名冲突(通过加前缀来保证) * 每个模块都要加上自己的前缀, 前缀在编程接口中非常重要, 可以区分软件的功能范畴并防止不同文件或者类之间命名发生冲突, 比如相册模块(PhotoGallery)的代码都以PG作为前缀: PGAlbumViewController, PGDataManager. * 可扩展则是要求写代码时要考虑后面的扩展需求, 这个属于架构层面的东东, 利用对应的设计模式来保证 2.iOS常量命令 1)对于常量的命名最好在前面加上字母k作为标记. 如: ``` static const NSTimeInterval kAnimationDuration = 0.3; ``` 2)定义作为NSDictionary或者Notification等的Key值字符串时加上 const 关键字, 以防止被修改. 如: ``` NSString * const UIApplicationDidEnterBackgroundNotification ``` 3)若常量作用域超出编译单元(实现文件), 需要在类外可见时, 使用 extern 关键字, 并加上该类名作为前缀. 如 ``` extern NSString * const PGThumbnailSize ``` <!--more--> 3.iOS枚举命令 枚举类型命名要加相关类名前缀并且枚举值命名要加枚举类型前缀. ``` typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { UIViewAnimationTransitionNone, UIViewAnimationTransitionFlipFromLeft, UIViewAnimationTransitionFlipFromRight, UIViewAnimationTransitionCurlUp, UIViewAnimationTransitionCurlDown, }; ``` 4.iOS变量和对象命名 1)给一个对象命名时建议采用修饰+类型的方式 ``` titleLabel //表示标题的label, 是UILabel类型 confirmButton //表示确认的button, 是UIButton类型 ``` 2)对于BOOL类型, 应加上 is 前缀 ``` - (BOOL)isEqualToString:(NSString *)aString ``` 3)如果某方法返回非属性的 BOOL 值, 那么应根据其功能, 选用 has 或 is 当前缀, 如 ``` - (BOOL)hasPrefix:(NSString *)aString ``` 4)如果某个命名已经很明确了, 为了简洁可以省去类型名. 比如scores, 很明显是个array了, 就不必命名成scoreArray了 5.iOS命名常规错误 ``` UserFollowerTableViewController // 不推荐 UserFollowerListController // OK UserLikedTagListController // 不推荐 TagUserLikedListController // OK,把显示的对象放在第一位 ``` 6.iOS通知命名 这里学习iOS的命名方法: ``` NSApplicationDidBecomeActiveNotification NSWindowDidMiniaturizeNotification NSTextViewDidChangeSelectionNotification NSColorPanelColorDidChangeNotification ``` 7.类名、局部变量、类成员命名 * 类名采用大驼峰(UpperCamelCase) * 类成员、方法小驼峰(lowerCamelCase) * 局部变量大小写首选小驼峰,也可使用小写下划线的形式(snake_case) * C函数的命名用大驼峰 8.命名规范--函数命名 * 1)如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用 do ,does这种多余的关键字,动词本身的暗示就足够了;动词打头的方法表示让对象执行一个动作 ``` - ( void )invokeWithTarget:(id)target; - ( void )selectTabViewItem:(NSTabViewItem *)tabViewItem; ``` * 2)如果方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法,注意不要添加 get 或者其他的动词前缀 正确,使用属性名来命名方法 ``` - (NSSize)cellSize; ``` 错误,添加了多余的动词前缀 ``` - (NSSize)calcCellSize; - (NSSize)getCellSize; ``` * 3)对于有多个参数的方法,务必在每一个参数前都添加关键词,关键词应当清晰说明参数的作用 正确,保证每个参数都有关键词修饰 ``` - ( void )sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; ``` 错误,遗漏关键词 ``` - ( void )sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; ``` 正确 ``` - (id)viewWithTag:(NSInteger)aTag; ``` 错误,关键词的作用不清晰 ``` - (id)taggedView:( int )aTag; ``` * 4)不要用and来连接两个参数,通常and用来表示方法执行了两个相对独立的操作(从设计上来说,这时候应该拆分成两个独立的方法): 错误,不要使用 "and" 来连接参数 ``` - ( int )runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; ``` 正确,使用 "and" 来表示两个相对独立的操作 ``` - (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag; ``` 9.命名规范--分组命名 * 使用英文,首字母大写,之后每个单词首字母都大写 * 每个分组使用模块的名字 * 使用的开源库统一放在“Library”分组下 * 使用的公共组件统一放在“Common”分组下 * 视图控制器及AppDelegate统一放在“Controllers”分组下 10.命名规范--图片命名 * 使用英文,首字母大写,之后每个单词首字母都大写 * 添加模块名作为前缀,避免冲突 * 图片应该与类文件一样,按模块分组放置 * 只要文件名叫做Icon.png,就会自动被当做是应用程序的图标 * 一个应用程序可以准备多种规格的图标,详情可以查看苹果官方文档,ios7 doc set /user expreience/guides/app icons on iPad and iphone * 一个app在启动过程中会全屏显示叫做Default.png的图片 11.命名规范--特殊类命名 * 如果是视图控制器的子类应添加后缀“ViewController”或者“Controller” * 如果是视图的子类应添加后缀“View” * 如果是按钮的子类应添加后缀“Button” 命名规范--补充命名 常量(预定义,局部常量等)使用小写k开头的驼峰法 举例:kInvalidHandle , kWritePerm 枚举类型命名首字母大写,之后每个单词首字母都大写,最后加“s” 枚举变量使用枚举类型去掉“s”作为前缀,每个单词首字母大写,中间不允许加下划线 举例: typedef enum UIControlEvents{ UIControlEventTouchDown, UIControlEventTouchUpInside }UIControlEvents; ## 编码规范 1.编码规范--判断nil或者YES/NO ``` if (someObject) { ... } if (!someObject) { ... } ``` 2.编码规范--条件赋值 如果是存在就赋值本身, 那就可以这样简写 ``` result = object ? : [self createObject]; ``` 3.编码规范--初始化方法 初始化的时候,直接赋值的好处是: 第一个好处还是简洁 第二个好处是可以防止初始化进去nil值造成crash ``` NSArray *names = @[ @"Brian" , @"Matt" , @"Chris" , @"Alex" , @"Steve" ]; NSDictionary *productManagers = @{ @"iPhone" : @"Kate" , @"iPad" : @"Kamal" }; NSNumber *shouldUseLiterals = @YES; NSNumber *buildingZIPCode = @10018; ``` 4.编码规范--定义属性 1)建议定义属性的时候把所有的参数写全, 尤其是如果想定义成只读的(防止外面修改)那一定要加上 readonly , 这也是代码安全性的一个习惯. 2)如果是内部使用的属性, 那么就定义成私有的属性(定义到.m的 class extension里面) 对于拥有Mutable子类型的对象(e.g. NSString, NSArray, NSDictionary)一定要定义成copy属性. Why? 示例: NSArray的array = NSMutableArray的mArray; 如果mArray在某个地方改变了, 那array也会跟着改变. So, make sense? 3)尽量不要暴露mutable类型的对象在 public interface , 建议在.h定义一个Inmutable类型的属性, 然后在.m的 get 函数里面返回一个内部定义的mutable变量. Why? For security as well! @property (nonatomic, readwrite, copy) NSString *name; 5.编码规范--BOOL赋值 ``` BOOL isAdult = age > 18; ``` 6.编码规范--拒绝死值 1)死值每次修改的时候容易被遗忘, 地方多了找起来就悲剧了. 而且定义成枚举或者 static 可以让错误发生在编译阶段. 另外仅仅看到一个数字, 完全不知道这个数字代表的意义. 纳尼? ``` if (car == Car.Nissan) or const int adultAge = 18; if (age > adultAge) { ... } ``` 7.编码规范--复杂的条件判断 清晰明了, 每个函数只做一件事! ``` if ([self canDeleteJob:job]) { ... } - (BOOL)canDeleteJob:(Job *)job { BOOL invalidJobState = job.JobState == JobState.New || job.JobState == JobState.Submitted || job.JobState == JobState.Expired; BOOL invalidJob = job.JobTitle && job.JobTitle.length; return invalidJobState || invalidJob; } ``` 8.编码规范--嵌套判断 一旦发现某个条件不符合, 立即返回, 条理更清晰 ``` if (!user.UserName) return NO; if (!user.Password) return NO; if (!user.Email) return NO; return YES; ``` 9.编码规范--参数过多 当发现实现某一功能需要传递的参数太多时, 就预示着你应该聚合成一个model类了...这样代码更整洁, 也不容易因为参数太多导致出错 ``` user里面有userName、password、email - ( void )registerUser(User *user) { // to do... } ``` 10.编码规范--把方法进行分类 1)使用#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//getter和setter方法 11.编码规范--注释符号 ``` /*************************************************************************** * 文件引用 ***************************************************************************/ /*************************************************************************** * 宏定义 ***************************************************************************/ /*************************************************************************** * 常量 ***************************************************************************/ /*************************************************************************** * 类型定义 ***************************************************************************/ /*************************************************************************** * 全局变量 ***************************************************************************/ /*************************************************************************** * 原型 ***************************************************************************/ / *************************************************************************** * 类特性 ***************************************************************************/ / *************************************************************************** * 类的实现 ***************************************************************************/ ``` * 块代码注释符号 1)块注释风格1 ``` ///////////////////////////////////// // @name UIButton控件生成相关API ///////////////////////////////////// ``` 2)块注释风格2 ``` // // 块功能说明 // ``` 3)块注释风格3 ``` /* * 块功能说明 */ ``` * 使用#program mark - 注释内容 >#program mark 是每个ios程序员都必须会用的技巧,通过#program mark 把代码分为个个部分,良好的注释是好代码的开始 * 注释原则 >代码中尽量少注释,让代码能自我描述。不过当需要注释的时候,能需要清除的解释某个代码块的含义和作用。注释应当保持最新,如果不必要请删除。 * #pragma clang 使用 1)取消xcode编译器内对于启用方法的警告 ``` #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // CODE:这段中出现使用所有的弃用方法都不会产生告警 #pragma clang diagnostic pop ``` 2)取消对未使用变量的警告,使用方法和上面相同,必须成对出现#pragma clang ``` //方法1: diagnostic --- #pragma clang diagnostic ignored "-Wunused-variable" //方法2: #pragma unused (foo) ``` 3)忽略内存泄露告警 ``` #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [someController performSelector: NSSelectorFromString( @"someMethod" )] #pragma clang diagnostic pop ``` * 手动产生一条警告 ``` #warning :手动产生一条告警 ``` * 手动产生一条错误 ``` #error : 手动产生一条错误 ``` 12.编码规范--花括号空格 ``` - ( void )methodName:(NSString *) string { ↑空格 ↑空格,推荐花括号在一行 if () { 空格↑ ↑空格,花括号不要另起一行 } else { 要换行↑ ↑空格,花括号不要另起一行 } } ``` 13.编码规范--编码注意 * 单个文件方法数不应超过30个 * 不要按类别排序(如把IBAction放在一块),应按任务把相关的组合在一起 * 禁止出现超过两层循环的代码,用函数或block替代 14.编码规范--编码例子 * 糟糕 ``` - (Task *)creatTaskWithPath:(NSString *)path { Task *aTask; if ([path isURL]) { if ([fileManager isWritableFileAtPath:path]) { if (![taskManager hasTaskWithPath:path]) { aTask = [[Task alloc] initWithPath:path]; } else { return nil; } } else { return nil; } } else { return nil; } return aTask; } ``` *建议 ``` - (Task *)creatTaskWithPath:(NSString *)path { if (![path isURL]) { return nil; } if (![fileManager isWritableFileAtPath:path]) { return nil; } if ([taskManager hasTaskWithPath:path]) { return nil; } Task *aTask = [[Task alloc] initWithPath:path]; return aTask; } ``` 15.编码规范--编码注释适当使用 * 尽量让代码可以自表述,而不是依赖注释。 * 注释应该表达那些代码没有表达以及无法表达的东西。 * 如果一段注释被用于解释一些本应该由这段代码自己表达的东西,我们就应该将这段注释看成一个改变代码结构或编码惯例直至代码可以自我表达的信号。 * 我们重命名那些糟糕的方法和类名,而不是去修补。我们选择将长函数中的一些代码段抽取出来形成一些小函数,这些小函数的名字可以表述原代码段的意图,而不是对这些代码段进行注释。 * 尽可能的通过代码进行表达。你通过代码所能表达的和你想要表达的所有事情之间的差额将为注释提供了一个合理的候选使用场合。对那些代码无法表达的东西进行注释,而不要仅简单地注释那些代码没有表达的东西。 * 方法内部禁止使用块注释。除非要临时注释大段代码,一般情况总应使用行注释。 16.编码规范--参数分行 * 正确使用:如果第一段名称过短,后续名称可以以Tab的长度(4个空格)为单位进行缩进 ``` - ( void ) short :(GTMFoo *)theFoo longKeyword:(NSRect)theRect evenLongerKeyword:( float )theInterval error:(NSError **)theError { ... } ``` * 错误使用:要么写在一行,要么全部分行 ``` [myObject doFooWith:arg1 name:arg2 error:arg3]; [myObject doFooWith:arg1 name:arg2 error:arg3]; ``` 17.编码规范--冒号两边 * 正确,在语法糖的 "[]" 或者 "{}" 两端留有空格 ``` NSArray *array = @[ [foo description], @"Another String" , [bar description] ]; NSDictionary *dict = @{ NSForegroundColorAttributeName : [NSColor redColor] }; ``` * 构造字典时,字典的Key和Value与中间的冒号:都要留有一个空格 * 正确,冒号 ':' 前后留有一个空格 ``` NSDictionary *option1 = @{ NSFontAttributeName : [NSFont fontWithName: @"Helvetica-Bold" size:12], NSForegroundColorAttributeName : fontColor }; ``` * 正确,按照Value来对齐 ``` NSDictionary *option2 = @{ NSFontAttributeName : [NSFont fontWithName: @"Arial" size:12], NSForegroundColorAttributeName : fontColor }; ``` * 错误,冒号前应该有一个空格 ``` NSDictionary *wrong = @{ AKey: @"b" , BLongerKey: @"c" , }; ``` * 错误,每一个元素要么单独成为一行,要么全部写在一行内 ``` NSDictionary *alsoWrong= @{ AKey : @"a" , BLongerKey : @"b" }; ``` * 错误,在冒号前只能有一个空格,冒号后才可以考虑按照Value对齐 ``` NSDictionary *stillWrong = @{ AKey : @"b" , BLongerKey : @"c" , }; ``` 18.编码规范--程序布局 1)程序布局目的:程序布局的目的是显示出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。更重要的是,统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。同时,对于普通程序员来说,养成良好的编程习惯有助于提高自己的编程水平,提高编程效率。因此,统一的、良好的程序布局和编程风格不仅仅是个人主观美学上的或是形式上的问题,而且会涉及到产品质量,涉及到个人编程能力的提高,必须引起大家重视。 2)布局中的空格:每个方法或者功能块之间为了结构清晰,应当有且只有一行空格,如下示例 ``` @ interface SomeClass:NSObject @property (noatomic, strong) UIView *aView -( void )someMethod; @end @implementation SomeClass - ( void )setAView:(NSInteger )aview { } -( void )someMethod { } @end ``` 3)布局中的Private Methods块:正常情况下ViewController里面不应该写 不是 delegate 方法的,不是 event response方法的,不是life cycle方法的,就是 private method了。对的,正常情况下ViewController里面一般是不会存在 private methods的,这个 private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。 ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个 private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。 4)布局中的初始化方法放哪里? 属性初始化放哪最好?建议在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 - ( void )viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.label]; } - ( void )viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.label.frame = CGRectMake(1, 2, 3, 4); } ``` 5)布局中的Getters and Setters放在最底部 一个view可能会有非常多的view和其他属性,如果getters and setters放在前面,就会导致在implementation代码顶部有大量的初始化代码,这就导致主要的逻辑代码挪到后面去了,其他人阅读代码是不太方便的。 19.编码规范--表达式 * if 语句 > 表达式大括号和其他大括号( if / else / switch / while 等.)总是在同一行语句打开但在新行中关闭。如果没有 else 并且括号内只有一行语句,可以和 if 语句同行,并且不需要括号。 ``` if (user.isHappy) { //Do something } else { //Do something else } if (somethingIsBad) return something; ``` * Switch语句 > 大括号在 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 (menuType) { case RWTLeftMenuTopItemMain: // ... break ; case RWTLeftMenuTopItemShows: // ... break ; case RWTLeftMenuTopItemSchedule: // ... break ; } ``` #### 参考链接 http: //www.jianshu.com/p/414bb5a53139 |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么构造函数需要尽可能的简单
· 探秘 MySQL 索引底层原理,解锁数据库优化的关键密码(下)
· 大模型 Token 究竟是啥:图解大模型Token
· 35岁程序员的中年求职记:四次碰壁后的深度反思
· 继承的思维:从思维模式到架构设计的深度解析
· 基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
· 由 MCP 官方推出的 C# SDK,使 .NET 应用程序、服务和库能够快速实现与 MCP 客户端
· 电商平台中订单未支付过期如何实现自动关单?
· 上周热点回顾(3.31-4.6)
· X86-64位简易系统开发 - 从BIOS阶段开始