Objective-c编码规范

本文介绍了Objective-C项目的编码规范,涵盖代码缩进、长度、组织、函数书写等方面,旨在提高代码可读性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编码风格

代码缩进

不要在工程里使用Tab键,使用空格来进行缩进。在Xcode > Preferences > Text Editing将Tab和自动缩进都设置为4个空格。(Google的标准是使用两个空格来缩进,但这里还是推荐使用Xcode默认的设置。)

代码长度

在Xcode > Preferences > Text Editing > Page guide at column:中将最大行长设置为80,过长的一行代码将会导致可读性问题。

代码组织

在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:

-列表内容

#pragma mark - Lifecycle
-(instancetype)init {}
-(void)dealloc {}
-(void)viewDidLoad {}
-(void)viewWillAppear:
-(BOOL)animated {}
-(void)didReceiveMemoryWarning {}

#pragma mark - Custom Accessors
-(void)setCustomProperty:(id)value {}
-(id)customProperty {}

#pragma mark - IBActions
-(IBAction)submitData:(id)sender {}

#pragma mark - Public
-(void)publicMethod {}

#pragma mark - Private
-(void)privateMethod {}

#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - NSCopying
-(id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject
-(NSString *)description {}

函数书写

典型的Objective-C函数应该是这样的:

-(void)writeVideoFrameWithData:(NSData *)frameData timeStamp:(int)timeStamp {

}

在-和(void)之间应该有一个空格,第一个大括号{的位置在函数所在行的末尾,同样应该有一个空格。(我司的C语言规范要求是第一个大括号单独占一行,但考虑到OC较长的函数名和苹果SDK代码的风格,还是将大括号放在行末。)
如果一个函数有特别多的参数或者名称很长,应该将其按照:来对齐分行显示:

-(id)initWithModel:(IPCModle)model
ConnectType:(IPCConnectType)connectType
Resolution:(IPCResolution)resolution
AuthName:(NSString *)authName
Password:(NSString *)password
MAC:(NSString *)mac
AzIp:(NSString *)az_ip
AzDns:(NSString *)az_dns
Token:(NSString *)token
Email:(NSString *)email
Delegate:(id)delegate;
在分行时,如果第一段名称过短,后续名称可以以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];

//第一段名称过短的话后续可以进行缩进
[myObj short:arg1
longKeyword:arg2
evenLongerKeyword:arg3
error:arg4];

协议(Protocols)

在书写协议的时候注意用<>括起来的协议和类型名之间是没有空格的,比如IPCConnectHandler(),这个规则适用所有书写协议的地方,包括函数声明、类声明、实例变量等等:

@interface MyProtocoledClass : NSObject {
@private
id _delegate;
}

-(void)setDelegate:(id)aDelegate;
@end

在实现协议的时候尽量隐藏在实现类的扩张类和分类里面比如:

@interface BasicHouseDetailController ()
{

}
闭包(Blocks)

根据block的长度,有不同的书写规则:

较短的block可以写在一行内。
如果分行显示的话,block的右括号}应该和调用block那行代码的第一个非空字符对齐。
block内的代码采用4个空格的缩进。
如果block过于庞大,应该单独声明成一个变量来使用。
^和(之间,^和{之间都没有空格,参数列表的右括号)和{之间有一个空格。

//较短的block写在一行内
[operation setCompletionBlock:^{ [self onOperationDone]; }];

//分行书写的block,内部使用4空格缩进
[operation setCompletionBlock:^{
[self.delegate newDataAvailable];
}];

//使用C语言API调用的block遵循同样的书写规则
dispatch_async(_fileIOQueue, ^{
NSString* path = [self sessionFilePath];
if (path) {
// …
}
});

//较长的block关键字可以缩进后在新行书写,注意block的右括号’}’和调用block那行代码的第一个非空字符对齐
[[SessionService sharedService]
loadWindowWithCompletionBlock:^(SessionWindow *window) {
if (window) {
[self windowDidLoad:window];
} else {
[self errorLoadingWindow];
}
}];

//较长的block参数列表同样可以缩进后在新行书写
[[SessionService sharedService]
loadWindowWithCompletionBlock:
^(SessionWindow *window) {
if (window) {
[self windowDidLoad:window];
} else {
[self errorLoadingWindow];
}
}];

//庞大的block应该单独定义成变量使用
void (^largeBlock)(void) = ^{
// …
};
[_operationQueue addOperationWithBlock:largeBlock];

//在一个调用中使用多个block,注意到他们不是像函数那样通过’:’对齐的,而是同时进行了4个空格的缩进
[myObject doSomethingWith:arg1
firstBlock:^(Foo *a) {
// …
}
secondBlock:^(Bar *b) {
// …
}];
数据结构书写

应该使用可读性更好的语法糖来构造NSArray,NSDictionary等数据结构,避免使用冗长的alloc,init方法。[适用十分确定里面的值不为 nil];

如果构造代码写在一行,需要在括号两端留有一个空格,使得被构造的元素于与构造语法区分开来:

//正确,在语法糖的”[]”或者”{}”两端留有空格
NSArray *array = @[ [foo description], @”Another String”, [bar description] ];
NSDictionary *dict = @{ NSForegroundColorAttributeName : [NSColor redColor] };

2 .如果构造代码不写在一行内,构造元素需要使用两个空格来进行缩进,右括号]或者}写在新的一行,并且与调用语法糖那行代码的第一个非空字符对齐:

NSArray *array = @[
@”This”,
@”is”,
@”an”,
@”array”
];

NSDictionary *dictionary = @{
NSFontAttributeName : [NSFont fontWithName:@”Helvetica-Bold” size:12],
NSForegroundColorAttributeName : fontColor
};

构造字典时,字典的Key和Value与中间的冒号:都要留有一个空格,多行书写时,也可以将Value对齐:

//正确,冒号’:’前后留有一个空格
NSDictionary *option1 = @{
NSFontAttributeName : [NSFont fontWithName:@”Helvetica-Bold” size:12],
NSForegroundColorAttributeName : fontColor
};

//正确,按照Value来对齐
NSDictionary *option2 = @{
NSFontAttributeName : [NSFont fontWithName:@”Arial” size:12],
NSForegroundColorAttributeName : fontColor
};

在使用不确定是否存在的读写一定要使用安全方法

//NSArray && NSMutableArray
- (id)objectAtIndexSafely:(NSUInteger)index;
- (void)addObjectSafely:(id)anObject;
- (void)insertObjectSafely:(id)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndexSafely:(NSUInteger)index;
- (void)replaceObjectAtIndexSafely:(NSUInteger)index withObject:(id)anObject;

//NSDictionary && NSMutableDictionary
- (id)objectForKeySafely:(id)key;
- (void)removeObjectForKeySafely:(id)aKey;

-(void)setObjectSafely:(id)anObject forKey:(id )aKey;

BOOL的使用

BOOL在Objective-C中被定义为signed char类型,这意味着一个BOOL类型的变量不仅仅可以表示YES(1)和NO(0)两个值,所以永远不要将BOOL类型变量直接和YES比较:

//正确
BOOL great = [foo isGreat];
if (great) {
// …be great!
}

// 判断object 是否 nil
id someObject = [[NSObject alloc] init];
if (someObject) {}
if (![anotherObject boolValue]) {}

不要将其它类型的值作为BOOL来返回,这种情况下,BOOL变量只会取值的最后一个字节来赋值,这样很可能会取到0(NO)。但是,一些逻辑操作符比如&&,||,!的返回是可以直接赋给BOOL的:

//正确
- (BOOL)isBold {
return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;
}

//正确,逻辑操作符可以直接转化为BOOL
- (BOOL)isValid {
return [self stringValue] != nil;
}
- (BOOL)isEnabled {
return [self isValid] && [self isBold];
}

枚举类型

当使用enum时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()来帮助和鼓励你使用固定的基本类型。

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
RWTLeftMenuTopItemMain,
RWTLeftMenuTopItemShows,
RWTLeftMenuTopItemSchedule
};

也可以指定值
typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
RWTPinSizeMin = 1,
RWTPinSizeMax = 5,
RWTPinCountMin = 100,
RWTPinCountMax = 500,
};

Case语句

当一个case语句包含多行代码时,大括号应该加上。

switch (condition) {
case 1:

// …
break;
case 2: {

// …

// Multi-line example using braces
break;
}
case 3:

// …
break;
default:

// …
break;
}

有很多次,当相同代码被多个cases使用时,一个fall-through应该被使用。一个fall-through就是在case最后移除’break’语句,这样就能够允许执行流程跳转到下一个case值。为了代码更加清晰,一个fall-through需要注释一下。

switch (condition) {
case 1:

// * fall-through! *
case 2:

// code executed for values 1 and 2
break;
default:

// …
break;
}

当在switch使用枚举类型时,’default’是不需要的。例如

RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;

switch (menuType) {
case RWTLeftMenuTopItemMain:

// …
break;
case RWTLeftMenuTopItemShows:

// …
break;
case RWTLeftMenuTopItemSchedule:

// …
break;
}
私有属性

私有属性应该在类的实现文件中的类扩展(匿名分类)中声明,命名分类(比如RWTPrivate或private)应该从不使用除非是扩展其他类。匿名分类应该通过使用+Private.h文件的命名规则暴露给测试。

@interface RWTDetailViewController ()

@property (strong, nonatomic) GADBannerView *googleAdView;
@property (strong, nonatomic) ADBannerView *iAdView;
@property (strong, nonatomic) UIWebView *adXWebView;

@end
条件语句

条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,even more dangerous defect可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。

if (!error) {
return success;
}
三元操作符

当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。

Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

Init方法

Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id

-(instancetype)init {
self = [super init];
if (self) {

// …
}
return self;
}

类构造方法

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

CGRect函数

当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。引用Apple的CGGeometry:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

ARC的使用

在init和dealloc中不要用存取方法访问实例变量
当initdealloc方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在这两个方法内直接访问实例变量。

//正确,直接访问实例变量
- (instancetype)init {
self = [super init];
if (self) {
_bar = [[NSMutableString alloc] init];
}
return self;
}
- (void)dealloc {
[_bar release];
[super dealloc];
}

按照定义的顺序释放资源
在类或者Controller的生命周期结束时,往往需要做一些扫尾工作,比如释放资源,停止线程等,这些扫尾工作的释放顺序应当与它们的初始化或者定义的顺序保持一致。这样做是为了方便调试时寻找错误,也能防止遗漏。

保证NSString在赋值时被复制
NSString非常常用,在它被传递或者赋值时应当保证是以复制(copy)的方式进行的,这样可以防止在不知情的情况下String的值被其它对象修改。
-(void)setFoo:(NSString *)aFoo {
_foo = [aFoo copy];
}
nil检查

因为在Objective-C中向nil对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干”,所以,只在程序中使用nil来做逻辑上的检查。

另外,不要使用诸如nil == Object或者Object == nil的形式来判断。

//正确,直接判断
if (!objc) {

}

点分语法的使用

不要用点分语法来调用方法,只用来访问属性。这样是为了防止代码可读性问题。

//正确,使用点分语法访问属性
NSString *oldName = myObject.name;
myObject.name = @”Alice”;

Delegate要使用

一个类的Delegate对象通常还引用着类本身,这样很容易造成引用循环的问题,所以类的Delegate属性要设置为弱引用。

/* delegate /
@property (nonatomic, weak) id delegate;

黄金路径

当使用条件语句编码时,左手边的代码应该是”golden” 或 “happy”路径。也就是尽量不要嵌套if语句,多个返回语句也是OK。

-(void)someMethod {
if (![someOther boolValue]) {
return;
}

//Do something important
}
单例模式

-(instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});

return sharedInstance;
}

错误处理

当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。

NSError *error;
if (![self trySomethingWithError:&error]) {

// Handle Error
}

常量

常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。

static NSString * const RWTAboutViewControllerCompanyName = @”RayWenderlich.com”;

static CGFloat const RWTImageThumbnailHeight = 50.0;

属性使用

所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是storage、atomicity,与在Interface Builder连接UI元素时自动生成代码一致。

@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (strong, nonatomic) NSString *tutorialName;
注释

读没有注释代码的痛苦你我都体会过,好的注释不仅能让人轻松读懂你的程序,还能提升代码的逼格。注意注释是为了让别人看懂,而不是仅仅你自己。

文件注释
每一个文件都必须写文件注释,文件注释通常包含

文件所在模块
作者信息
历史版本信息
版权信息
文件包含的内容,作用
一段良好文件注释

/*********************************************************************
Copyright (C), 2011-2013, Andrew Min Chang

File name:  AMCCommonLib.h
Author:     Andrew Chang (Zhang Min) 
E-mail:     LaplaceZhang@126.com

Description:    
        This file provide some covenient tool in calling library tools. One can easily include 
    library headers he wants by declaring the corresponding macros. 
        I hope this file is not only a header, but also a useful Linux library note.

History:
    2012-??-??: On about come date around middle of Year 2012, file created as "commonLib.h"
    2012-08-20: Add shared memory library; add message queue.
    2012-08-21: Add socket library (local)
    2012-08-22: Add math library
    2012-08-23: Add socket library (internet)
    2012-08-24: Add daemon function
    2012-10-10: Change file name as "AMCCommonLib.h"
    2012-12-04: Add UDP support in AMC socket library
    2013-01-07: Add basic data type such as "sint8_t"
    2013-01-18: Add CFG_LIB_STR_NUM.
    2013-01-22: Add CFG_LIB_TIMER.
    2013-01-22: Remove CFG_LIB_DATA_TYPE because there is already AMCDataTypes.h

Copyright information: 
        This file was intended to be under GPL protocol. However, I may use this library
    in my work as I am an employee. And my company may require me to keep it secret. 
    Therefore, this file is neither open source nor under GPL control. 

**********************************************************************/

好的代码应该是“自解释”(self-documenting)的,但仍然需要详细的注释来说明参数的意义、返回值、功能以及可能的副作用。

方法、函数、类、协议、类别的定义都需要注释,推荐采用Apple的标准注释风格,好处是可以在引用的地方alt+点击自动弹出注释,非常方便。

有很多可以自动生成注释格式的插件,推荐使用VVDocumenter:

良好的注释:

/**
* Create a new preconnector to replace the old one with given mac address.
* NOTICE: We DO NOT stop the old preconnector, so handle it by yourself.
*
* @param type Connect type the preconnector use.
* @param macAddress Preconnector’s mac address.
*/
- (void)refreshConnectorWithConnectType:(IPCConnectType)type Mac:(NSString *)macAddress;

/**
* Stop current preconnecting when application is going to background.
*/
-(void)stopRunning;

/**
* Get the COPY of cloud device with a given mac address.
*
* @param macAddress Mac address of the device.
*
* @return Instance of IPCCloudDevice.
*/
-(IPCCloudDevice )getCloudDeviceWithMac:(NSString )macAddress;

// A delegate for NSApplication to handle notifications about app
// launch and shutdown. Owned by the main app controller.
@interface MyAppDelegate : NSObject {

}
@end
协议、委托的注释要明确说明其被触发的条件:

/* Delegate - Sent when failed to init connection, like p2p failed. /
-(void)initConnectionDidFailed:(IPCConnectHandler *)handler;
不要使用new方法

尽管很多时候能用new代替alloc init方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa的规范就是使用alloc init方法,使用new会让一些读者困惑。

Public API要尽量简洁

共有接口要设计的简洁,满足核心的功能需求就可以了。不要设计很少会被用到,但是参数极其复杂的API。如果要定义复杂的方法,使用类别或者类扩展。

标题基于SpringBoot+Vue的学生交流互助平台研究AI更换标题第1章引言介绍学生交流互助平台的研究背景、意义、现状、方法与创新点。1.1研究背景与意义分析学生交流互助平台在当前教育环境下的需求及其重要性。1.2国内外研究现状综述国内外在学生交流互助平台方面的研究进展与实践应用。1.3研究方法与创新点概述本研究采用的方法论、技术路线及预期的创新成果。第2章相关理论阐述SpringBoot与Vue框架的理论基础及在学生交流互助平台中的应用。2.1SpringBoot框架概述介绍SpringBoot框架的核心思想、特点及优势。2.2Vue框架概述阐述Vue框架的基本原理、组件化开发思想及与前端的交互机制。2.3SpringBoot与Vue的整合应用探讨SpringBoot与Vue在学生交流互助平台中的整合方式及优势。第3章平台需求分析深入分析学生交流互助平台的功能需求、非功能需求及用户体验要求。3.1功能需求分析详细阐述平台的各项功能需求,如用户管理、信息交流、互助学习等。3.2非功能需求分析对平台的性能、安全性、可扩展性等非功能需求进行分析。3.3用户体验要求从用户角度出发,提出平台在易用性、美观性等方面的要求。第4章平台设计与实现具体描述学生交流互助平台的架构设计、功能实现及前后端交互细节。4.1平台架构设计给出平台的整体架构设计,包括前后端分离、微服务架构等思想的应用。4.2功能模块实现详细阐述各个功能模块的实现过程,如用户登录注册、信息发布与查看、在线交流等。4.3前后端交互细节介绍前后端数据交互的方式、接口设计及数据传输过程中的安全问题。第5章平台测试与优化对平台进行全面的测试,发现并解决潜在问题,同时进行优化以提高性能。5.1测试环境与方案介绍测试环境的搭建及所采用的测试方案,包括单元测试、集成测试等。5.2测试结果分析对测试结果进行详细分析,找出问题的根源并
内容概要:本文详细介绍了一个基于灰狼优化算法(GWO)优化的卷积双向长短期记忆神经网络(CNN-BiLSTM)融合注意力机制的多变量多步时间序列预测项目。该项目旨在解决传统时序预测方法难以捕捉非线性、复杂时序依赖关系的问题,通过融合CNN的空间特征提取、BiLSTM的时序建模能力及注意力机制的动态权重调节能力,实现对多变量多步时间序列的精准预测。项目不仅涵盖了数据预处理、模型构建与训练、性能评估,还包括了GUI界面的设计与实现。此外,文章还讨论了模型的部署、应用领域及其未来改进方向。 适合人群:具备一定编程基础,特别是对深度学习、时间序列预测及优化算法有一定了解的研发人员和数据科学家。 使用场景及目标:①用于智能电网负荷预测、金融市场多资产价格预测、环境气象多参数预报、智能制造设备状态监测与预测维护、交通流量预测与智慧交通管理、医疗健康多指标预测等领域;②提升多变量多步时间序列预测精度,优化资源调度和风险管控;③实现自动化超参数优化,降低人工调参成本,提高模型训练效率;④增强模型对复杂时序数据特征的学习能力,促进智能决策支持应用。 阅读建议:此资源不仅提供了详细的代码实现和模型架构解析,还深入探讨了模型优化和实际应用中的挑战与解决方案。因此,在学习过程中,建议结合理论与实践,逐步理解各个模块的功能和实现细节,并尝试在自己的项目中应用这些技术和方法。同时,注意数据预处理的重要性,合理设置模型参数与网络结构,控制多步预测误差传播,防范过拟合,规划计算资源与训练时间,关注模型的可解释性和透明度,以及持续更新与迭代模型,以适应数据分布的变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值