iOS 代码规范

iOS规范:仅适用于iOS的代码规范(使用Objective-C语言)。

一.代码应该简洁易懂,逻辑清晰

  • 不要过分追求技巧,降低程序的可读性。
  • 代码保证简洁,简洁的代码可以让bug无处藏身。

二.面向变化编程,而不是面向需求编程。

  • 在需求开始时,尽量考虑到代码的可扩展性,写出扩展性强,易修改的程序才是负责任的做法。

三:先保证程序的正确性,防止过度工程

1.先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。
2.先写出可用的代码,反复推敲,再考虑是否需要重用的问题。
3.先写出可用,简单,明显没有bug的代码,再考虑测试的问题。

四.IOS 规范


变量

1. 变量名必须使用驼峰格式

类,协议使用大驼峰:

CKBaseViewController.h
<HeaderViewDelegate>

对象等局部变量使用小驼峰:

NSString *personName = @"";
NSUInteger totalCount = 0;

2. 变量的名称必须同时包含功能与类型

UIButton *addBtn //添加按钮
UILabel *nameLbl //名字标签
NSString *addressStr//地址字符串

3. 系统常用类作实例变量声明时加入后缀

类型 后缀
UIViewController VC
UIView View
UILabel label
NSDictionary Dict
NSString str
... ...

常量

1. 常量以相关类名作为前缀

推荐这样写:

static const NSString LCStrName = ckStr ;

不推荐这样写:

static const NSString strName = ckStr;

2. 建议使用类型常量,不建议使用#define预处理命令

define定义:
系统在编译时候,就将其全部替换,而不会对其变量进行类型等属性检查,相对不是很安全,可能存在潜在的问题,而没有发现,宏定义的其他缺点可自行百度。

类型常量
在头文件 .h文件中
extern NSString *const CKStringConstant;
在实现文件 .m文件中
NSString *const CKStringConstant = @"VALUE";


CGRect函数

iOS内部已经提供了相应的获取CGRect各个部分的函数了,它们的可读性比较高,而且简短,推荐使用:
推荐这样写:

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);

不推荐这样写:

CGRect frame = self.view.frame;  
CGFloat x = frame.origin.x;  
CGFloat y = frame.origin.y;  
CGFloat width = frame.size.width;  
CGFloat height = frame.size.height;  
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

范型

建议在定义NSArray和NSDictionary时使用泛型,可以保证程序的安全性:

NSArray<NSString *> *testArr = [NSArray arrayWithObjects:@"Hello", @"world", nil];
NSDictionary<NSString *, NSNumber *> *dic = @{@"key":@(1), @"age":@(10)};

字面量语法

尽量使用字面量值来创建 NSString , NSDictionary , NSArray , NSNumber 这些不可变对象:
推荐这样写:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"}; 
NSNumber *shouldUseLiterals = @YES;NSNumber *buildingZIPCode = @10018;

不推荐这样写:

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

属性

1. 属性的命名使用小驼峰

推荐这样写:

@property (nonatomic, readwrite, strong) UIButton *confirmButton;

2. 属性的关键字推荐按照 原子性,读写,内存管理的顺序排列

@property (nonatomic, readwrite, copy) NSString *name;
@property (nonatomic, readonly, copy) NSString *gender;
@property (nonatomic, readwrite, strong) UIView *headerView;

3. Block属性应该使用copy关键字

推荐这样写:

typedef void (^ErrorCodeBlock) (id errorCode,NSString *message);
@property (nonatomic, readwrite, copy) ErrorCodeBlock errorBlock;

4. 使用getter方法做懒加载

实例化一个对象是需要耗费资源的,如果这个对象里的某个属性的实例化要调用很多配置和计算,就需要懒加载它,在使用它的前一刻对它进行实例化:

- (NSDateFormatter *)dateFormatter 
{
    if (!_dateFormatter) {
           _dateFormatter = [[NSDateFormatter alloc] init];
           NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
           [_dateFormatter setLocale:enUSPOSIXLocale];
           [_dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];
    } 
    return _dateFormatter;
}

但是也有对这种做法的争议:getter方法可能会产生某些副作用,例如如果它修改了全局变量,可能会产生难以排查的错误。

5. 除了init和dealloc方法,建议都使用点语法访问属性

使用点语法的好处:

setter:

setter会遵守内存管理语义(strong, copy, weak)。
通过在内部设置断点,有助于调试bug。
可以过滤一些外部传入的值。
捕捉KVO通知。

getter:

允许子类化。
通过在内部设置断点,有助于调试bug。
实现懒加载(lazy initialization)。

注意:
懒加载的属性,必须通过点语法来读取数据。因为懒加载是通过重写getter方法来初始化实例变量的,如果不通过属性来读取该实例变量,那么这个实例变量就永远不会被初始化。
在init和dealloc方法里面使用点语法的后果是:因为没有绕过setter和getter,在setter和getter里面可能会有很多其他的操作。而且如果它的子类重载了它的setter和getter方法,那么就可能导致该子类调用其他的方法。

6. 尽量使用不可变对象

建议尽量把对外公布出来的属性设置为只读,在实现文件内部设为读写。具体做法是:

  • 在头文件中,设置对象属性为readonly
  • 在实现文件中设置为readwrite。

这样一来,在外部就只能读取该数据,而不能修改它,使得这个类的实例所持有的数据更加安全。而且,对于集合类的对象,更应该仔细考虑是否可以将其设为可变的。

如果在公开部分只能设置其为只读属性,那么就在非公开部分存储一个可变型。所以当在外部获取这个属性时,获取的只是内部可变型的一个不可变版本,例如:

在公共API中:

@interface EOCPerson : NSObject

@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSSet *friends //向外公开的不可变集合

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;

@end

在这里,我们将friends属性设置为不可变的set。然后,提供了来增加和删除这个set里的元素的公共接口。

在实现文件里:

@interface EOCPerson ()

@property (nonatomic, copy, readwrite) NSString *firstName;
@property (nonatomic, copy, readwrite) NSString *lastName;

@end

@implementation EOCPerson {
     NSMutableSet *_internalFriends;  //实现文件里的可变集合
}

- (NSSet*)friends 
{
     return [_internalFriends copy]; //get方法返回的永远是可变set的不可变型
}

- (void)addFriend:(EOCPerson*)person 
{
    [_internalFriends addObject:person]; //在外部增加集合元素的操作
    //do something when add element
}

- (void)removeFriend:(EOCPerson*)person 
{
    [_internalFriends removeObject:person]; //在外部移除元素的操作
    //do something when remove element
}

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName 
{

     if ((self = [super init])) {
        _firstName = firstName;
        _lastName = lastName;
        _internalFriends = [NSMutableSet new];
    }
 return self;
}

方法

1. 方法名中不应使用and,而且签名要与对应的参数名保持高度一致

推荐这样写:

- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

不推荐这样写:

- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;

2. 方法实现时,如果参数过长,则令每个参数占用一行,以冒号对齐。

- (void)doSomethingWith:(NSString *)theFoo
                   rect:(CGRect)theRect
               interval:(CGFloat)theInterval
{
   //Implementation
}

3. 私有方法应该在实现文件中申明。

@interface ViewController ()
- (void)basicConfiguration;
@end

@implementation ViewController
- (void)basicConfiguration
{
   //Do some basic configuration
}
@end

4. 方法名用小写字母开头的单词组合而成

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

5. 方法名前缀

  • 刷新视图的方法名要以refresh为首。
  • 更新数据的方法名要以update为首。
    推荐这样写:
- (void)refreshHeaderViewWithCount:(NSUInteger)count;
- (void)updateDataSourceWithViewModel:(ViewModel*)viewModel;


1. 类的名称应该以三个大写字母为前缀;创建子类的时候,应该把代表子类特点的部分放在前缀和父类名的中间

推荐这样写:

//父类
ZOCSalesListViewController

//子类
ZOCDaySalesListViewController
ZOCMonthSalesListViewController

2. initializer && dealloc

推荐:

  • 将 dealloc 方法放在实现文件的最前面
  • 将init方法放在dealloc方法后面。如果有多个初始化方法,应该将指定初始化方法放在最前面,其他初始化方法放在其后。
2.1 dealloc方法里面应该直接访问实例变量,不应该用点语法访问
2.2 init方法的写法:
  • init方法返回类型必须是instancetype,不能是id。
  • 必须先实现[super init]。
- (instancetype)init 
{ 
    self = [super init]; // call the designated initializer 
    if (self) { 
        // Custom initialization 
    } 
    return self; 
}
2.3 指定初始化方法

指定初始化方法(designated initializer)是提供所有的(最多的)参数的初始化方法,间接初始化方法(secondary initializer)有一个或部分参数的初始化方法。

注意事项1:间接初始化方法必须调用指定初始化方法。

@implementation ZOCEvent 

//指定初始化方法
- (instancetype)initWithTitle:(NSString *)title date:(NSDate *)date 
location:(CLLocation *)location
{ 
    self = [super init]; 
      if (self) {
         _title = title; 
         _date = date; 
         _location = location; 
      } 
    return self; 
} 

//间接初始化方法
-  (instancetype)initWithTitle:(NSString *)title date:(NSDate *)date
{ 
    return [self initWithTitle:title date:date location:nil];
}

//间接初始化方法
-  (instancetype)initWithTitle:(NSString *)title 
{ 
    return [self initWithTitle:title date:[NSDate date] location:nil];
}
 @end

注意事项2:如果直接父类有指定初始化方法,则必须调用其指定初始化方法

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 
    if (self) {
    }
    return self; 
}

注意事项3:如果想在当前类自定义一个新的全能初始化方法,则需要如下几个步骤

  • 定义新的指定初始化方法,并确保调用了直接父类的初始化方法。
  • 重载直接父类的初始化方法,在内部调用新定义的指定初始化方法。
  • 为新的指定初始化方法写文档。

例:

@implementation ZOCNewsViewController

//新的指定初始化方法
- (id)initWithNews:(ZOCNews *)news 
{
    self = [super initWithNibName:nil bundle:nil]; 
    if (self) {
        _news = news;
    }
    return self;
} 

// 重载父类的初始化方法
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
    return [self initWithNews:nil]; 
}
@end

在这里,重载父类的初始化方法并在内部调用新定义的指定初始化方法的原因是你不能确定调用者调用的就一定是你定义的这个新的指定初始化方法,而不是原来从父类继承来的指定初始化方法。
假设你没有重载父类的指定初始化方法,而调用者却恰恰调用了父类的初始化方法。那么调用者可能永远都调用不到你自己定义的新指定初始化方法了。
而如果你成功定义了一个新的指定初始化方法并能保证调用者一定能调用它,你最好要在文档中明确写出哪一个才是你定义的新初始化方法。或者你也可以使用编译器指令attribute((objc_designated_initializer))来标记它。

3. 所有返回类对象和实例对象的方法都应该使用instancetype

将instancetype关键字作为返回值的时候,可以让编译器进行类型检查,同时适用于子类的检查,这样就保证了返回类型的正确性(一定为当前的类对象或实例对象)

推荐这样写:

@interface ZOCPerson
+ (instancetype)personWithName:(NSString *)name; 
@end

不推荐这样写:

@interface ZOCPerson
+ (id)personWithName:(NSString *)name; 
@end

4. 在类的头文件中尽量少引用其他头文件

有时,类A需要将类B的实例变量作为它公共API的属性。这个时候,我们不应该引入类B的头文件,而应该使用向前声明(forward declaring)使用class关键字,并且在A的实现文件引用B的头文件。

5. 类的布局

#pragma mark - Life Cycle Methods
- (instancetype)init
- (void)dealloc

- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated

#pragma mark - Override Methods

#pragma mark - Intial Methods

#pragma mark - Network Methods

#pragma mark - Target Methods

#pragma mark - Public Methods

#pragma mark - Private Methods

#pragma mark - UITableViewDataSource  
#pragma mark - UITableViewDelegate  

#pragma mark - Lazy Loads

#pragma mark - NSCopying  

#pragma mark - NSObject  Methods

分类

1. 分类添加的方法需要添加前缀和下划线

推荐这样写:

@interface NSDate (ZOCTimeExtensions)
 - (NSString *)zoc_timeAgoShort;
@end

不推荐这样写:

@interface NSDate (ZOCTimeExtensions) 
- (NSString *)timeAgoShort;
@end
2. 把类的实现代码分散到便于管理的多个分类中

一个类可能会有很多公共方法,而且这些方法往往可以用某种特有的逻辑来分组。我们可以利用Objecctive-C的分类机制,将类的这些方法按一定的逻辑划入几个分区中。
例:
先看一个没有使用无分类的类:

#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject

@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;

/* Friendship methods */
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;

/* Work methods */
- (void)performDaysWork;
- (void)takeVacationFromWork;

/* Play methods */
- (void)goToTheCinema;
- (void)goToSportsGame;

@end

分类之后:

#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject

@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;

- (id)initWithFirstName:(NSString*)firstName

andLastName:(NSString*)lastName;

@end

@interface EOCPerson (Friendship)

- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;

@end

@interface EOCPerson (Work)

- (void)performDaysWork;
- (void)takeVacationFromWork;

@end

@interface EOCPerson (Play)

- (void)goToTheCinema;
- (void)goToSportsGame;

@end

其中,FriendShip分类的实现代码可以这么写:

// EOCPerson+Friendship.h
#import "EOCPerson.h"

@interface EOCPerson (Friendship)

- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;

@end

// EOCPerson+Friendship.m
#import "EOCPerson+Friendship.h"

@implementation EOCPerson (Friendship)

- (void)addFriend:(EOCPerson*)person 
{
   /* ... */
}

- (void)removeFriend:(EOCPerson*)person 
{
   /* ... */
}

- (BOOL)isFriendsWith:(EOCPerson*)person 
{
   /* ... */
}

@end

单例

1. 单例不能作为容器对象来使用

单例对象不应该暴露出任何属性,也就是说它不能作为让外部存放对象的容器。它应该是一个处理某些特定任务的工具,比如在iOS中的GPS和加速度传感器。我们只能从他们那里得到一些特定的数据。

2. 使用dispatch_once来生成单例

推荐这样写:

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

不推荐这样写:

+ (instancetype)sharedInstance 
{ 
 static id sharedInstance; 
 @synchronized(self) { 
 if (sharedInstance == nil) {  sharedInstance = [[MyClass alloc] init]; 
 } } 
 return sharedInstance; 
}

相等性的判断

判断两个person类是否相等的合理做法:
-  (BOOL)isEqual:(id)object 
{

     if (self == object) {  
            return YES; //判断内存地址
    } 

    if (![object isKindOfClass:[ZOCPerson class]]) { 
     return NO; //是否为当前类或派生类 
     } 

     return [self isEqualToPerson:(ZOCPerson *)object]; 

}

//自定义的判断相等性的方法
-  (BOOL)isEqualToPerson:(Person *)person 
{ 
        if (!person) {  
              return NO;
        } 
        BOOL namesMatch = (!self.name && !person.name) || [self.name isEqualToString:person.name]; 
        BOOL birthdaysMatch = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday]; 
        return haveEqualNames && haveEqualBirthdays; 
}

方法文档

一个函数(方法)必须有一个字符串文档来解释,除非它:

非公开,私有函数。
很短。
显而易见。

而其余的,包括公开接口,重要的方法,分类,以及协议,都应该伴随文档(注释):

以/开始
第二行识总结性的语句
第三行永远是空行
在与第二行开头对齐的位置写剩下的注释。

建议这样写:

/This comment serves to demonstrate the format of a doc string.

Note that the summary line is always at most one line long, and after the opening block comment,
and each line of text is preceded by a single space.
*/

看一个指定初始化方法的注释:

/ 
  *  Designated initializer. *
  *  @param store The store for CRUD operations.
  *  @param searchService The search service used to query the store. 
  *  @return A ZOCCRUDOperationsStore object.
  */ 
- (instancetype)initWithOperationsStore:(id<ZOCGenericStoreProtocol>)store searchService:(id<ZOCGenericSearchServiceProtocol>)searchService;

多用队列,少用同步锁来避免资源抢夺

多个线程执行同一份代码时,很可能会造成数据不同步。建议使用GCD来为代码加锁的方式解决这个问题。

方案一:使用串行同步队列来将读写操作都安排到同一个队列里:

_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);

//读取字符串
- (NSString*)someString 
{

         __block NSString *localSomeString;
         dispatch_sync(_syncQueue, ^{
            localSomeString = _someString;
        });
         return localSomeString;

}

//设置字符串
- (void)setSomeString:(NSString*)someString 
{

     dispatch_sync(_syncQueue, ^{
        _someString = someString;
    });
}

这样一来,读写操作都在串行队列进行,就不容易出错。

但是,还有一种方法可以让性能更高:

方案二:将写操作放入栅栏快中,让他们单独执行;将读取操作并发执行。

_syncQueue = dispatch_queue_create("com.custom.queue", DISPATCH_QUEUE_CONCURRENT);

//读取字符串
- (NSString*)someString 
{

     __block NSString *localSomeString;
     dispatch_sync(_syncQueue, ^{
        localSomeString = _someString;
    });
     return localSomeString;
}//设置字符串
- (void)setSomeString:(NSString*)someString 
{

     dispatch_barrier_async(_syncQueue, ^{
        _someString = someString;
    });

}

显然,数据的正确性主要取决于写入操作,那么只要保证写入时,线程是安全的,那么即便读取操作是并发的,也可以保证数据是同步的。
这里的dispatch_barrier_async方法使得操作放在了同步队列里“有序进行”,保证了写入操作的任务是在串行队列里。


NSArray& NSMutableArray

1. addObject之前要非空判断。

2. 取下标的时候要判断是否越界。

3. 取第一个元素或最后一个元素的时候使用firtstObject和lastObject


NSNotification

1. 通知的名称

建议将通知的名字作为常量,保存在一个专门的类中:

// Const.h
extern NSString * const ZOCFooDidBecomeBarNotification

// Const.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

2. 通知的移除

通知必须要在对象销毁之前移除掉。


其他

1. Xcode工程文件的物理路径要和逻辑路径保持一致。

2. 忽略没有使用变量的编译警告

对于某些暂时不用,以后可能用到的临时变量,为了避免警告,我们可以使用如下方法将这个警告消除:

- (NSInteger)giveMeFive 
{ 
 NSString *foo; 
 #pragma unused (foo) 
 return 5; 
}

3. 手动标明警告和错误

手动明确一个错误:

- (NSInteger)divide:(NSInteger)dividend by:(NSInteger)divisor 
{ 
 #error Whoa, buddy, you need to check for zero here! 
 return (dividend / divisor); 
}

手动明确一个警告:

- (float)divide:(float)dividend by:(float)divisor 
{ 
 #warning Dude, don't compare floating point numbers like this! 
     if (divisor != 0.0) { 
        return (dividend / divisor); 
     } else {  
        return NAN; 
 } 
}

参考文献:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容

  • 这篇规范一共分为三个部分: 核心原则:介绍了这篇代码规范所遵循的核心原则。 通用规范:不局限于iOS的通用性的代码...
    樊开囧阅读 556评论 0 1
  • iOS编程规范0规范 0.1前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可 测试、高效...
    iOS行者阅读 4,464评论 21 35
  • 修订历史 版本号修改内容修改日期修改人 说明 本文将以条款的形式逐条说明每个规范,每个条款会标记[强制]、[推荐]...
    比特_0bd7阅读 679评论 0 0
  • 一、前言 本规范基于Google Objective-C Style Guide,对其中的说明性语句及非ARC部分...
    IIronMan阅读 879评论 0 6
  • -------------------------------------编码原则----------------...
    yanhooIT阅读 832评论 0 11