代码规范

带出几十位从零开始学iOS的实习生或试用期的开发人员后,觉得真的是千人千面,每个人写的代码都风格迥异,如果没有一个文档规范,每次都和新人进行口头的说教,大概自己是不用敲代码了,所以吃了亏了就开始编写iOS的编程规范。由于本人在写iOS代码前一直是C语言的开发,所以很多规范都受C语言的影响。
[图片上传中。。。(1)]编程规范.png

与大家分享下我总结的编程规范,有不合适的请大家指出(最好能举例说明为何不好,并给一个好的推荐_

  1. 指导原则
    (1)首先是为人编写程序,其次才是计算机。
    软件的生命周期贯穿产品的开发,测试,生产,用户使用,版本升级和后期维护等过程,只有易读,易维护的软件代码才具有生命力。
    (2)保持代码的简明清晰,避免过分的编程技巧。
    简单是最美。不要过分追求技巧,否则会降低程序的可读性。
    (3)编程时首先达到正确性,其次考虑效率。
    编程首先考虑的是满足正确性,健壮性,可维护性,可移植性等质量因素。
    (4)编写代码时需要考虑到代码的可测试性。
    不可以测试的代码是无法保障质量的。实现设计功能的同时,要提供可以测试、验证的方法。
    (5)函数(方法)是为一特定功能而编写,不是万能工具箱。
    方法是一个处理单元,是由特定功能的,所以应该很好地规划方法,不能是所有东西都放在一个方法里实现。
    (6)鼓励多注释。
    当代码有改动时,注释一定要修改,并且不要添加多余或重复的注释。
  2. 布局
    程序布局的目的是现实出程序良好的逻辑结构,提高程序的准确性、连续性、可读性、可维护性。统一的程序布局和编程风格,有助于提高整个项目的开发质量,提高开发效率,降低开发成本。
    遵循统一的布局顺序书写头文件和实现文件:
    文件头注释#import文件内部使用的宏常量定义文件内部使用的数据类型全局变量本地变量类定义/实现
    (1)If、else、else if、for、while、do等语句自占一行,执行语句不得紧跟其后,不论执行语句有多少都要加{}。
    “{”前面添加一个空格,紧跟语句后。方法(函数)时,“{”另起一行并独 占一行书写。
    if ( numberA > numberB ) { return numberA;}

(2)定义指针类型的变量,“*****” 应该放在变量前。
NSString *str1 = @"Hello World!";

(3)源代码中关系较为紧密的代码应尽可能相邻。
CGFloat fWidth;CGFloat fLength;CGFloat fHeight;

(4)禁止使用TAB键,必须使用空格进行缩进,缩进为4个空格。
在Xcode->Preferences->Text Editing->Indentation->Prefer indent using中,将值设置为Spaces。
(5)程序的分界符“{”和“}”在if、else、else if、for、while、do等语句时,“{”前添加空格紧跟语句后。在方法(函数)应独占一行并且位于同一列,同时与引用他们的语句对齐。“{}”之内的代码块使用缩进规则对齐。

  • (void)dealloc{ // Do Something}if (isUpdated) { // Do Something}

(6)相关的赋值语句等号对齐。
promotionsEntity.promotionImageStr = activityItemDict[@"promotion_image"];promotionsEntity.promotionIdNum = activityItemDict[@"promotion_id"];promotionsEntity.promotionNameStr = activityItemDict[@"promotion_name"];promotionsEntity.promotionColorStr = activityItemDict[@"promotion_color"];

(7)在switch语句中,每一个case分支和default要用“{}”括起来,“{}”中的内容需要缩进。
(8)函数(方法)块之间使用一个空行分隔。
(9)一元操作符如!、~、++、--、、&、和[]、.、->、前后不加空格。
!bValue~iValue++iCount
strSource&fSum

(10)多元运算符和他们的操作数之间至少需要一个空格。
fWidth = 5 + 5;fLength = fWidth * 2;fHeight = fWidth + fLength;

(11)关键字之后要留空格。“(”后和“)”前 不添加空格。
if、for、while等关键字之后应留一个空格再跟左括号”(”。
if (0 == fWidth)

(12)方法名与形参不能留空格,返回类型与方法标识符有一个空格。
方法名后紧跟”:”,然后紧跟形参,返回类型”(”与”-”之间有一个空格。

  • (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

(13)注释符与注释内容之间要用一个空格进行分割。
/* 设置Frame的值 */// TO DO

(14)长表达式(超过80列)要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当地缩进,使排版整齐。
if ( 0 == fWidth || 0 < fLength )

(15)函数(方法)声明时,类型与名称不允许分行书写。

  • (void)didReceiveMemoryWarning

(16)类中功能模块以#pragma mark – 分割,上空两行,下空一行

pragma mark – UITextFieldDelegate

(17)方法实现时,参数过长则每个参数用一行,以冒号对齐。

  • (void)doSomethingWith:(NSString *)theFoo rect:(CGRect)theRect interval:(CGFloat)theInterval{

如果方法名比参数名短,则每个参数占用一行,至少缩进4个字符,且为垂直对齐(非冒号对齐)。

  • (void)short:(NSString *)theFoot longKeyword:(CGRect)theRect evenLongerkeyword:(float)theInterval{}

(18)方法调用沿用声明方法的习惯。
在同一行
[self doSomethingWith:@"test" rect:self.view.frame interval:1.0f];

或 冒号对齐
[self doSomethingWith:@"test" rect:self.view.frame interval:1.0f];

(19)@public 和 @private 使用单独一行,且缩进1个字符
(20)Protocals声明中类型标识符、代理名称、尖括号间不留空格。
id<MyProtocalDelegate> delegate;

在类声明中包含多个protocal,每个protocal占用一行,缩进2个字符。
@interface CustomBackButtonViewController () <UITextFieldDelegate, MyProtocalDelegate, UITabBarControllerDelegate, UITabBarDelegate>

也可以与第一个对齐,保持清晰易读
@interface ShopViewController () <UIGestureRecognizerDelegate, HXSClickEventDelegate, UITableViewDelegate, UITableViewDataSource>

如果并非所有方法都是必须得,使用@optional标示。
(21)UIViewController.m的文件布局,不同变量和方法的先后顺序:
<1>#import “LocationCustomButton.h”

最开始是导入需要使用到的其他class头文件,头文件按类型分类
// controller#import “TextSelectionViewController.h"// model#import "PayPasswordUpdateModel.h"#import "WalletModel.h"// views#import "PayPasswordAlertView.h"

<2>#define KEY_BANK_TAIL @“bank_tail"

添加宏定义,将在文件中需要使用到的常量,字符串等用宏定义。
<3>@property (nonatomic, strong) NSArray *bankCardList;

property的属性定义。不用将需要使用的methods声明,在iOS7后Methods已经不需要先声明再使用了。
<4>- (void)viewDidLoad

pragma mark - Life Cycle

添加生命周期的方法

  • (void)viewWillAppear:(BOOL)animated - (void)viewDidAppear:(BOOL)animated- (void)viewWillDisappear:(BOOL)animated-(void)viewDidDisappear:(BOOL)animated- (void)didReceiveMemoryWarning- (void)dealloc

<5>#pragma mark - override

重载父类的方法
<6>#pragma mark - Intial Methods

初始化的方法,如

  • (void)initialNavigationBar- (void)initialTableView

<7>#pragma mark - Target Methods

点击事件或通知事件
<8>#pragma mark - UITextFieldDelegate

Delegate的事件,将所有的delegate放在同一个pragma下
<9>#pragma mark - Setter Getter Methods

所有的property使用懒加载,并将setter或getter放在底部,不影响业务代码的阅读。
<10>#pragma mark - private method

私有方法的代码尽量抽取创建公共class。

  1. 注释
    (1)多行注释采用”/* … */”, 单行注释采用 “// …”.
    (2)一般情况下,源程序有效注释量必须在30%以上。
    注释语言不宜太多也不能太少,注释语言必须准确、易懂、简洁。
    (3)注释应与其描述的代码相近,对代码的注释应放在其上方或右方相邻位置,不可放在下方,如放于上方则需要与其上面的代码用空行隔开。
    (4)注释与所描述内容进行同样地缩排。
    (5)文件最前面的注释,是保留了工程自动生成的注释,但是需要进行如下修改:文件作者改为个人的名字,公司名为Insigma Hengtian software Ltd. 如:
    //// ViewController.m// test//// Created by ArthurWang on 14-5-7.// Copyright (c) 2014年 Insigma HengTian Software Ltd. All rights reserved.//

(6)类、协议、结构体注释。如:
/* @class HTServerDatamanager @abstract 异步连接服务器管理类 @discussion 异步请求服务器,接收到响应后,通过回调把数据回传到对象 */

(7)成员方法、接口注释。如:
/* @method initWithTarget:selector: @abstract 类初始化函数 @discussion 本类使用时必须调用此函数进行初始化 @param target 响应回调对象 @param selector 回调对象的selector @result 类对象 */

  1. 命名
    (1)标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。
    标识符应当直观且可以拼读,可望文知意,英文单词一般不要太复杂,用词应当准确。
    (2)严格禁止使用连续的下划线,下划线也不能出现在标识符头或结尾。(实例变量及特殊用法除外)
    CGFloat variable__name;NSString *variale___name;

(3)程序中不要出现仅靠大小区分的相似的标识符。
NSString *contentOfView;NSString *ContentOfView;

(4)宏、常量名都要使用大写字母,用下划线‘_’分割单词。

define URL_GAIN_QUOTE_LIST @"/v1/quote/list"#define URL_UPDATE_QUOTE_LIST @"/v1/quote/update"#define URL_LOGIN @"/v1/user/login”

(5)程序中局部变量不要与全局变量重名。
尽管局部变量和全局变量的作用域不同而不会发生语法错误,但容易使人误解。
(6)使用一致的前缀来区分变量的作用域。
g_ 全局变量s_ 模块内静态变量
(7)方法名用小写字母开头的单词组合而成。
方法名力求清晰、明了、通过方法名就能够判断方法的主要功能。方法名中不同意义字段之间不要用下划线连接,而要把每个字段的首字母大写以示区分。

  • (NSString *)descriptionWithLocale:(id)locale;

(8)尽量避免名字中出现数字编号,如Value1, Vlaue2等,除非逻辑上的确需要编号。
(9)声明实例变量,都采用property。
不在类声明和实现的“{”与“}”之间声明。
@interface LumberjackViewController (){ NSArray *dataSource; // 错误:}

(10)类名(及其category 和protocal)的首字母大写,使用字母大写的形式分割单词。

  1. 变量
    变量、常量和数据类型是程序编写的基础,是直接关系到程序设计的成败。
    (1)一个变量有且只有一个功能,尽量不要把一个变量用作多种用途。
    一个变量只用来表示一个特定功能,不要把一个变量作多种用途。
    (2)循环语句与判断语句中,不允许对其它变量进行计算与赋值。
    错误: if ( 100 > (fWidth = 50 * fLength) )
    (3)宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。

define MY_MIN(A, B) ((A)>(B)?(B):(A))

(4)宏名大写字母
(5)对于全局变量通过统一的函数访问。
(6)最好不要在语句块内声明局部变量。
(7)系统常用类作实例变量声明时加入后缀:
UIViewController: VC UIImage: ImgUIImageView:ImagView UIView:ViewUILabel: Lbl UIButton:BtnUINavigationBar:Nbar UIToolbar:TbarUISearchBar:Sbar UITextField:TextFieldUITextView:TextView NSArray:ArrayNSMutableArray:Marray NSDictionary:DictNSMutableDictionary:Mdict NSString:StrNSMutableString:MStr NSSet:SetNSMutableSet:Mset

(8)属性声明严把权限,对不需要外部修改的属性使用readonly。
(9)NSString使用copy而非retain。
在ARC中NSString的使用Strong与Copy的效果一样。
(10)CFType 使用@dynamic,禁止使用@synthesize
(11)除非必须,使用nonatomic。
(12)定义NSArray和NSDictionary使用泛型,提高代码可读性和健壮性。
NSArray<NSString *> *testArr = [NSArray arrayWithObjects:@"Hello", @"world", nil];NSDictionary<NSString *, NSNumber *> *dic = @{@"key":@(1), @"age":@(10)};

在* 符号前面都添加一空格。

  1. 表达式
    表达式是语句的一部分,他们是不可分割的。
    (1)一条语句只完成一个功能。
    复杂的语句阅读起来,难于理解,并容易隐含错误。
    (2)在表达式中使用括号,使表达式的运算顺序更清晰。
    由于将运算符的优先级与结合律熟记是比较困难的,为了防止产生歧义并提高可读性,即使不加括号时运算顺序不会改变,也应当用括号确定表达式的操作顺序。
    if ( (( 0 == iYear%4 ) && ( 0 != iYear%100 )) || ( 0 == iYear%400 ) )

(3)避免表达式中的附加功能,不要编写太复杂的复合表达式。
错误:int iResult = iYear++-++iMonth+iDay++;
(4)不可将布尔变量和逻辑表达式直接与YES、NO或则1、0进行比较。
if (isSuccess) //真if (!isSuccess) //假

(5)在条件判断语句中,当整型变量与0比较时,不可模仿布尔变量的风格,应当将整型变量用“==”或“!=”直接与0比较。
if (0 == iYear)if ( 0 != iMonth )

(6)应当将指针变量用“==”或“!=”与nil比较。
指针变量的零值是“空”(即nil),nil的值与0相同,但是两者含义不同。
if ( nil == strName )

(7)在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。
避免漏掉break语句造成程序错误,同时保持程序简洁。对于多个分支相同处理的情况可以共用一个break,但是要用注释加以说明。
(8)不可在for循环内修改循环变量,防止for循环失去控制。
(9)循环嵌套次数不大于3次。
(10)do while 语句和while语句仅使用一个条件。
(11)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。
(12)for语句的循环控制变量的取值采用“半开半闭区间”写法。
这样做更能适应数组的特点,数组的下标属于一个“半开半闭区间”。
int iMax[1000];for (int i = 0; i < 1000; i++){ NSLog(@"%d", iMax[i]);}

(13)将int值转换为BOOL时应特别小心。
(14)OC中,BOOL被定义为unsigned char,这意味着除了YES(1)和NO(0)外它还可以是其他值。禁止将int直接转换为BOOL。
(15)将整型值转换为BOOL的方法:使用三元运算符返回YES/NO,或使用&&,||。
(16)BOOL、_BOOL和bool之间的转换是安全的,但是BOOL和Boolean间的转换不是安全的,所以将Boolean堪称整型值。
(17)在OC中,只允许使用BOOL。

  1. 函数
    (1)方法不能为多个目的服务。
    一个方法一个功能。
    (2)在接口中应该尽量少使用外部定义的类型(减少耦合)。
    (3)避免函数有太多的参数,参数个数尽量控制在5个以内。
    如果参数的确比较多,不妨把这些参数定义成一个结构(或一个类)。
    (4)对于有返回值的函数(方法),每一个分支都必须有返回值。
    为了保证对被调用函数返回值的判断,有返回值的函数中都每一个退出点都需要有返回值。
    (5)对输入参数的正确性和有效性进行检查。
    很多程序错误和崩溃是由非法参数引起的。
    (6)防止将函数(方法)的参数作为工作变量。
    将函数的参数作为工作变量,有可能错误地改变参数内容。对必须改变的参数,最好先用局部变量代之,最后再将该局部变量的内容赋给该参数。
    (7)函数(方法)体的规模不能太大,尽量控制在200行之内。
    冗长的函数不利于调试,可读性差。
    (8)禁止直接调用NSObject的类方法+new,也不要在子类中重载它。使用alloc和init方法。
    (9)创建对象时尽量使用autorelease,创建临时对象时,尽量同时在同一行中autorelease掉,而非使用单独的release语句。
    (10)Dealloc的顺序要与变量声明的顺序相同。
    这样有利于review代码。如果dealloc中调用其他方法来release变量,将被release的变量以注释的形式标注清楚。先release自身成员变量,再调用父类dealloc方法。
  2. 头文件
    (1)申明成员类,应该引用该类申明,而不是包含该类的头文件。
    @class MyViewController;@interface ViewController : UIViewController{ MyViewController *_myViewController;}

(2)共同的接口、结构体、常量和数据类型要定义在同一个头文件里。
(3)使用#import引入OC和OC++头文件,使用#include引入c和c++头文件。

  1. 可靠性
    为保证代码的可靠性,编程时请遵循如下基本原则,优先级递减:正确性稳定性可测试性规范/可读性全局效率局部效率个人表达方式/个人方便性
    (1)防止内存操作越界
    内存操作主要是指对数组、指针、内存地址等得操作,内存操作越界是软件系统主要错误之一,后果往往非常严重,引起崩溃。
    (2)当变量释放后,需要将变量置为nil。
    避免因为野指针引起的程序崩溃。
    (3)变量在使用前应初始化,防止未初始化的变量被引用。
    引用未初始化的变量,会引起程序的崩溃。
    (4)指针类型变量必须初始化为nil。
    (5)指针不要进行复杂的逻辑或算术操作。
    通过复杂的逻辑或算术操作后,指针的位置就很难确定。
    (6)减少指针和数据类型的强制类型转化。
    强制类型转化如果类型强转错误会引起崩溃。
    (7)对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。
    (8)非初始化方法中的alloc操作之前必须要nil判断。
    (9)在编写派生类的赋值时,主要不要忘记对基类的成员变量重新赋值。
  • (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning];}

(10)私有方法应该在实现文件中申明。
@interface ViewController ()- (void)login;@end

  1. 断言
    断言是对某种假设条件进行检查,可以快速发现并定位软件文件,同时对系统错误进行自动报警。
    (1)整个软件系统应该采用统一的断言。
    assert(str);

(2)正式软件产品中应把断言及其它的调测代码去掉。
加快软件运行速度。

  1. 其他
    (1) 避免过多直接使用立即数。
    应该都使用宏定义,采用立即数不容易理解含义并容易出错。
    (2) 枚举第一个成员要赋初始值。
    (3) addObject之前要非空判断。
    (4) release版本代码去掉NSLog打印,除了保留异常分支的NSLog。
    (5) 禁止在代码中直接写死字符串资源,必须要用字符串ID替代。
    应该考虑多语言国际化,尽量使用NSLocalizedStringFromTable实现对字符串ID的引用。
    (6) 对于框架设计,逻辑层尽量与UI层分离,降低耦合度。
    (7) delegate对象使用assign,禁止使用retain。
    因为retain会引起导致循环索引导致内存泄露,并且对类型的内存泄露无法被Instrument发现,极难调试。
    (8) Controller独立于View和Controller。
    不要在View相关的类中添加过多的业务逻辑代码,这让代码的可重用性很差。Controller负责业务逻辑代码,且Controller的代码与View尽量无关。
    (9)init方法和dealloc方法是最常用的方法,所以将他们放在类实现的开始位置。
    (10) 使用空格将相同的变量、属性对齐,使用换行分组。
    // END
    总结写完了,复制黏贴好累啊。发现很多条在开发中都没有遵守,因为需求变动实在太频繁了,并且要求的开发时间实在太短。吐槽下产品组,能不能多考虑多考虑然后听听我们开发的意见啊。!_!
    文/蝴蝶之梦天使(简书作者)原文链接:http://www.jianshu.com/p/08be5b30ff82著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • iOS编程规范0规范 0.1前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可 测试、高效...
    iOS行者阅读 4,418评论 21 35
  • 推荐文章:禅与 Objective-C 编程艺 前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、...
    WolfTin阅读 2,737评论 0 1
  • 代码格式 使用空格而不是制表符 Tab 不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Pr...
    small_Sun阅读 1,337评论 1 3
  • 带出几十位从零开始学iOS的实习生或试用期的开发人员后,觉得真的是千人千面,每个人写的代码都风格迥异,如果没有一个...
    蝴蝶之梦天使阅读 6,848评论 18 110
  • 晨起天色灰若墨, 风吹彻骨拌身凉。 绿树摇曳随风摆, 至此六一儿童节。 致已是回忆的六一儿童节! 我在郑州,你在哪?
    闪闪发光的小金矿阅读 179评论 0 0