美文网首页
Object-C编程规范

Object-C编程规范

作者: liangxiuchen | 来源:发表于2016-08-24 19:07 被阅读0次
    主要参考:github:https://github.com/raywenderlich/objective-c-style-guide 等汇总

    <p>
    1.变量命名规范
    定义有意义的变量,采用驼峰方式。变量名能直接反应其意义和类型除去NSNumber, NSString外,对于NSArray, NSDictionary 或其它对象,采用+后缀的方式。标识其类型。
    <pre>
    <code>//good</code>
    <code>@property (nonatomic, strong) UIImageView *logoImageView;</code>
    <code>@property (nonatomic, strong) UILabel *titleLabel;</code>
    <code>@property (nonatomic, strong) UILabel *subTitleLabel;</code>
    <code>@property (nonatomic, strong) UIButton *payButton;</code>

    <code>NSNumber *cartNum;</code>
    <code>NSString *promotionTitle;</code>
    <code>NSArray *trades; // 以英文单词复数形式</code>
    <code>NSDictionary *adDict; // 以dict结尾</code>
    <code>// Bad</code>
    <code>@property (nonatomic, strong) UIImageView *imageView1;</code>
    <code>@property (nonatomic, strong) UIImageView *imageView2;</code>
    </pre>

    1. 常用类型变量初始化(NSString, NSDictionary, NSArray, and NSNumber)
      <pre>
      <code>// Good</code>
      <code>NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];</code>
      <code>NSDictionary *productManagerDict = @{
      @"iPhone": @"Kate",
      @"iPad": @"Kamal",
      @"Mobile Web": @"Bill"
      };</code>
      <code>NSNumber *shouldUseLiterals = @YES;</code>
      <code>NSNumber *buildingZIPCode = @10018;</code>

    <code>// 获取NSArray成员和NSDictionary成员的读取和对应的Mutable类型的设置,优先采用简化方式:</code>
    <code>NSString *firstName = names[0];</code>
    <code>NSString *manager = productManagerDict[@"iPad"];
    </code>

    <code>// Bad</code>
    <code>NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];</code>
    <code>NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];</code>
    <code>NSNumber *shouldUseLiterals = [NSNumber </code><code>numberWithBool:YES];</code>
    <code>NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];</code>

    <code>NSString *firstName = [names objectAtIndex:0];</code>
    <code>NSString *manager = [names objectForKey:@"iPad"];</code>
    </pre>
    3.类,方法接口和属性定义
    <p>.h文件中仅仅放置需要提供给其他地方使用的属性和方法(需要暴露的东西),其他属性可以直接定义在.m文件中, 不要把所有方法或属性都拉到.h中,尽量使用property方式定义属性,避免原来直接定义ivar的形式;
    .h文件中,尽量减少直接引用其他头文件,可以通过@class方式声明即可。在.m文件中进行对应的引用。
    在block中,不要用ivar形式访问属性,一律使用self.property的形式,防止隐性的self retain被忽视,从而造成retain cycle.
    对应的实现方法块可以通过#pragma -mark 来进行分组标识
    </p>
    <pre>
    <code>// .h文件</code>
    <code>// 通过@class减少直接import头文件</code>
    <code>@class BSTDemo1,BSTDemo2;</code>
    <code>@interface BSTService : NSObject</code>
    <code>+ (instancetype)sharedManager;</code>
    <code>- (void)loadXXWithType:(NSArray *)XXTypes block:(LoadSuccessBlock)block;</code>
    <code>- (void)clearCache;</code>
    <code>@property (nonatomic, strong) NSArray *XXKeys;</code>
    <code>@property (nonatomic, assign) BOOL hasXX(isXX);
    </code>
    <code>@end</code>

    <code>// .m文件</code>
    <code>@interface BBAdsService ()</code>
    <code>// 定义私有属性</code>
    <code>@property (nonatomic, strong) BSTDemo1 *XXModel;</code>
    <code>@end</code>
    <code>// 对外接口</code>
    <code>- (void)loadXXWithType:(NSArray *)XXTypeArray block:(LoadSuccessBlock)block</code>
    {
    <code>// ...</code>
    <code>[self doGetXX:XXTypeArray expires:XXX block:^(BBXXModel *model)</code> {
    <code>// ...</code>
    <code>// 注意,此处有对self的retain,此block不能为self的成员,否则就retain cycle了</code>
    <code>self.displayXXModel = model;</code>
    <code>// Bad</code>
    <code>// _displayXXModel = model;</code>
    <code> // ...</code>
    }];</code>
    }
    </pre>

    1. 工程目录组织和代码分块 #pragma mark

    .m文件中代码组织:
    第一部分,放初始化,dealloc相关代码,无需添加marker
    第二部分,对于有生命周期的,通过life cycle标识,把所有的生命周期方法集中到一起处理。无生命周期的自定义类或view等,无需添加
    第三部分,所有实现的delegate,需要一一明确分块标识出来。
    第四部分,以event handler为标识,放所有事件响应代码,包括button,gesture, notification, kvo等
    第五部分,放自定义方法,可以自己定义marker, 如不想定义,则直接用methods
    最后一部分,以accessors为标识,放所有getter 和 setter代码
    <pre>
    <code>// 初始化的,可不用</code>
    <code>- (instancetype)init;</code>
    <code>- (void)dealloc;</code>

    <code>// 对于 ViewController 或 Application, 没有生命周期则不需要</code>
    <code>#pragma mark - life cycle</code>
    <code>- (void)viewDidLoad;</code>

    <code>// 所有的delegate实现,必须要一一标识出来</code>
    <code>#pragma mark - xxxxDelegate</code>
    <code>- (void)onSelectIndex:(NSInteger)index;</code>

    <code>// (button, gesture, notification)等事件处理方法</code>
    <code>#pragma mark - event handler</code>
    <code>- (void)clickFavorBtnAction:(UIButton *)btn;</code>

    <code>#pragma mark - if u want, put your custom markers here, or just write bellow 'methods'</code>
    <code>#pragma mark - methods</code>

    <code>#pragma mark - accessors</code>
    <code>// 尽量使用懒加载方式构建</code>
    <code>- (UIButton)favorBtn</code>
    {
    if (_favorBtn == nil) {
    <code> // initialize _favorBtn;</code>
    }
    return favorBtn;
    }
    </pre>
    5.dispatch
    *, block
    优先使用dispatch_after, dispatch_async, dispatch_sync等方法,替代原来的NSObject中的延迟执行和在线程执行处理等方法。
    使用block注意通过weakSelf和strongSelf方式来避免循环引用,在block中引用self需要格外小心。
    可以使用libextobjc开源库中的@weakify(self) 和@strongify(self)(或者自己仿写宏)(https://github.com/stapelberg/libxcb)
    <pre>
    //单例子的正常写法(此处可以继承我的一个单例抽象类)https://github.com/OrangeVarko/Singleton/tree/master/CLXSingleton
    <code>+ (instancetype)sharedInstance
    {</code>
    <code> static id sharedInstance = nil;</code>
    <code>static dispatch_once_t onceToken;</code>
    <code>dispatch_once(&onceToken, ^{</code>
    <code>sharedInstance = [[self alloc] init];</code>
    });</code>
    <code>return sharedInstance;</code>
    }

    // Good
    <code>[UIView animateWithDuration:1.0 animations:^{</code>
    <code>// something</code>
    <code>} completion:^(BOOL finished) {</code>
    <code>// something</code>
    }];</code>

    // Bad
    <code>[UIView animateWithDuration:1.0</code>
    <code>animations:^{</code>
    <code>// something</code>
    }
    <code>completion:^(BOOL finished) {</code>
    <code>// something</code>
    }];

    // Good
    <code>__weak typeof(self) weakSelf = self;</code>
    <code>self.request.onComplete = ^(id model) {</code>
    <code>typeof(self) strongSelf = weakSelf;</code>
    <code>[strongSelf hideProgressHud];</code>
    ...
    }
    //推荐这种
    @weakify(self);
    <code>self.request.onComplete = ^(id model) {</code>
    <code>@strongify(self);//此处一定要</code>
    <code>[strongSelf hideProgressHud];</code>
    ...
    }

    // Bad (self循环引用)
    <code>self.request.onComplete = ^(id model) {</code>
    <code> [self hideProgressHud];</code>
    }
    </pre>

    1. 代码风格(换行,空格)

    if else while等关键字与 ( 和 { 间加一个空格
    双目运算符(== &&等),注意两边空格, 单目运算符(! ++等)不要添加空格
    对于较长的语句,注意换行,方法调用多参数方法调用注意将:对齐
    反复使用的内容,可以先用临时变量存起来,减少语句长度。
    <pre>
    <code>- (BOOL)checkSomeFromCache:(NSArray *)adsTypes complete:(LoadSuccessBlock)complete </code>
    {

    <code> for (id i in expression) {</code>
    <code>if (expression1 && expression2) {</code>
    <code>//do something</code>
    }
    }
    <code>while (expression1 == expression2) {</code>
    <code> //do something</code>
    }
    }

    // Good
    // 下面多处使用,先临时存储
    <code>NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];</code>

    <code>NSString * lastestAlertTime = [userDefaults objectForKey:lastestAlertTimeKey];</code>
    <code>NSNumber * currentShowCount = [userDefaults objectForKey:currentShowCountKey];</code>
    <code>NSString * lastShowImg = [userDefaults objectForKey:imgKey];</code>
    // ...
    <code>[userDefaults setObject:@(currentShowCount.integerValue + 1) forKey:currentShowCountKey];</code>
    <code>[userDefaults setObject:nowTime forKey:lastestAlertTimeKey];</code>
    <code>[userDefaults setObject:dict[@"img"] forKey:imgKey];</code>
    <code>[userDefaults synchronize];</code>

    // Bad
    <code>NSString * lastestAlertTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastestAlertTimeKey];</code>
    <code>NSNumber * currentShowCount = [[NSUserDefaults standardUserDefaults] objectForKey:currentShowCountKey];</code>
    <code>NSString * lastShowImg = [[NSUserDefaults standardUserDefaults] objectForKey:imgKey];</code>
    // ...
    <code>[[NSUserDefaults standardUserDefaults] setObject:@(currentShowCount.integerValue + 1) forKey:currentShowCountKey];</code>
    <code>[[NSUserDefaults standardUserDefaults] setObject:nowTime forKey:lastestAlertTimeKey];</code>
    <code>[[NSUserDefaults standardUserDefaults] setObject:dict[@"img"] forKey:imgKey];</code>
    <code>[[NSUserDefaults standardUserDefaults] synchronize];</code>
    </pre>
    8.日志
    调试日志在测试完成后,统一换成DBLog(小件员自己写的宏)等方式,防止在release版本中存在日志输出
    <pre>
    <code>#ifndef Macro_h</code>
    <code>#define Macro_h</code>
    <code>//自定义日志函数</code>
    <code>#ifdef DEBUG</code>

    <code>#define DBLog(format, ...) do { </code>
    <code> fprintf(stderr, "<%s : %d> %s\n",</code>
    <code> [[[NSString stringWithUTF8String:FILE] </code><code>lastPathComponent] UTF8String], </code>
    LINE, func);</code>
    <code> (NSLog)((format), ##VA_ARGS); </code>
    <code> fprintf(stderr, "-------\n"); </code>
    <code>} while (0)</code>

    <code>#else</code>

    <code># define DBLog(...);</code>

    <code>#endif</code>
    </pre>

    1. try, catch
      减少try,catch的使用,可能造成ARC不work,尽量用其他方式实现,除非你能保证发生异常时,所有对象都及时释放。
      <pre>
      <code>- (void)exampleFunction {</code>
      ABMultiValueRef phone = ABRecordCopyValue(person, <code>kABPersonPhoneProperty);</code>
      long index = <code>ABMultiValueGetIndexForIdentifier(phone,identifier);</code>
      <code> try {</code>
      <code>NSString *phoneNO = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phone, index + 1);</code>
      <code>CFRelease(phone);//此处代码执行不到,直接跳到catch中了。导致内存泄漏</code>
      <code> } catch {</code>
      <code>DBLog(@“数据越界异常”);</code>
      }
      }
      </pre>
    2. 需要尽量避免魔鬼数字出现,可以通过正确的命名,enum,宏等提高代码可阅读性
      <pre>
      //good
      <code>typedef NS_ENUM(NSUInteger,BSTCountDownStatus)</code>
      {
      <code> BSTCountDownStatusCancelled,//时间未到,人为停止</code>
      <code>BSTCountDownStatusFinished,//计时完成</code>
      };
      <code>if (status == BSTCountDownStatusCancelled) {</code>
      //do..
      <code>} else if (status == BSTCountDownStatusFinished) {</code>
      //do..
      <code>} else {</code>
      //do..
      }

    //bad
    <code>if (status == 0) {</code>
    //do..
    <code>} else if (status == 1) {</code>
    //do..
    <code>} else {</code>
    //do..
    }
    </pre>

    1. 尽量使用常量定义,避免宏定义
      <pre>
      //bad
      <code>#define KUserPhone @"userPhone"</code>
      //good
      <code>static NSString *const kUserPhone = @"userPhone";</code>
      </pre>
      12.采用提前return的方式,减少if语句的嵌套
      <pre>
      //good
      <code>- (void)viewDidAppear:(BOOL)animated</code>
      {
      <code> [super viewDidAppear:animated];</code>
      <code>if (self.URLRequest == nil) return;</code>
      <code>[self.webView loadRequest:self.URLRequest];</code>
      }
      //bad
      <code>- (void)viewDidAppear:(BOOL)animated</code>
      {
      <code> [super viewDidAppear:animated];</code>
      <code> if (self.URLRequest) {</code>
      <code> [self.webView loadRequest:self.URLRequest];</code>
      }
      }
      </pre>
      13.NSTimer潜在的循环引用
      <pre>
      <code>//.h</code>
      <code>@property (nonatomic, strong) NSTimer *timer;</code>
      <code>//.m</code>
      <code>self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(printTest) userInfo:nil repeats:YES];</code>
      <code>//-------------------------------------------------------</code>
      <code>//自己封装NSTimer 以block的形式消除循环引用</code>
      <code>@weakify(self);</code>
      <code>self.timer = [NSTimer BST_scheduledTimerWithTimeInterval:1 block:^(NSTimer *timer) {</code>
      <code> @strongify(self);</code>
      <code> [self printTest];</code>
      <code>NSLog(@"BST_scheduledTimer");</code>
      <code>} repeats:YES];</code>
      </pre>
      14.通过category扩充原来对象,利用Associated Objects动态添加属性
      <pre>
      <code>@interface UIView (MIAdditions)</code>

    <code>@property (nonatomic, strong) NSObject *customData;</code>

    <code>@end</code>

    <code>@implementation UIView (MIAdditions)</code>

    <code>- (NSObject *)customData</code>
    {
    <code>return objc_getAssociatedObject(self, @selector(customData));</code>
    }

    <code>- (void)setCustomData:(NSObject *)data</code>
    {
    <code>objc_setAssociatedObject(self, @selector(customData), data, OBJC_ASSOCIATION_RETAIN_NONATOMIC);</code>
    }

    @end
    </pre>
    15.对model中的字典和数组,在取值前多用runtime里面的类型自省方法: [xx isKindOfClass:[XX class]],[xx isMemberOfClass:[XX class]]
    <pre>
    <code>//good</code>
    <code>//真正做打电话请求</code>
    <code>- (void)requestServiceMakeCall</code>
    {
    <code> //@note:后端把主叫和被叫人接口字段弄反了</code>
    <code> [SetPhoneNumService makeDoubleCallWith:self.callingPhone calling:self.calledPhone billCode:self.calledBillCode success:^(id response) {</code>
    <code> if (!response) return;</code>

        <code>NSString * const codeKey = @"code";</code>
        <code>APPCallFeedBackStatus feedBack = APPCallFeedBackStatusCallFailed;</code>
       <code> if ([response[codeKey] isKindOfClass:[NSNumber class]]) {</code>
          <code>  NSNumber *status = response[codeKey];</code>
           <code> [self saveCallRecordWithStatus:status];</code>
           <code> feedBack = status.integerValue == 0 ?  </code> <code>APPCallFeedBackStatusCallSuceess : </code><code>APPCallFeedBackStatusCallFailed;</code>
       <code>}</code>
      <code>  [self dismissWithStatus:feedBack];</code>
    

    <code> } fail:^(NSString *error, NSInteger statusCode) {</code>
    <code> [self feedBackWithStatus:APPCallFeedBackStatusNetWorkError];</code>
    <code>}];</code>
    <code>}</code>
    </pre>
    </p>

    相关文章

      网友评论

          本文标题:Object-C编程规范

          本文链接:https://www.haomeiwen.com/subject/zydgsttx.html