iOS KitiOS面试

Object-C规约

2018-05-14  本文已影响35人  勇往直前888

语言规约

命名规约

正例:

命名 说明
insertObject:atIndex: 好的命名
removeObjectAtIndex: 好的命名
removeObject: 这样命名也不错,因为方法将移除通过参数引用的对象。
destinationSelection 好的命名
setBackgroundColor: 好的命名

反例:

命名 说明
insert:at: 不清晰;插入什么?“at”表示什么?
remove: 不清晰:要移除什么?
destSel 不清晰,缩写含义不清
setBkgdColor: 缩写含义不清

一致性:命名含义应该具有前后,全局的一致性,同个功能也应该使用同个名称。

命名 说明
- (NSInteger)tag 该方法同时定义在 NSView、 NSCell、 NSControl 这三个类里面。

不能自我指涉:除掩码常量、通知外,名称不应该自我指涉(self-reference)。

正例:

命名 说明
NSString 可以使用
NSUnderlineByWordMask 掩码常量,可以使用Mask自我指涉
NSTableViewColumnDidMoveNotification 通知常量,可以使用Mask自我指涉

反例:

命名 说明
NSStringObject NSString本身已经是Object了,不需要再在名字里显示指出
命名 说明
alloc Allocate
alt Alternate
app Application.
calc Calculate
dealloc Deallocate
func Function
horiz Horizontal
info Information
init Initialize
int Integer
max Maximum
min Minimum
msg Message
nib Interface Builder archive
pboard Pasteboard
rect Rectangle
Rep Representation
temp Temporary
vert Vertical
fileExistsAtPath:isDirectory:
- (void)invokeWithTarget:(id)target; 
- (void)selectTabViewItem:(NSTabViewItem *)tabViewItem;

正例:

- (NSSize)cellSize;

反例:

 - (NSSize)calcCellSize;
 - (NSSize)getCellSize;

正例:

- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;

反例:

- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
命名 说明
- (instancetype)initWithFrame:(NSRect)frameRect; NSView
- (instancetype)initWithFrame:(NSRect)frameRect mode:(int)aMode cellClass:(Class)factoryId numberOfRows:(int)rowsHigh numberOfColumns:(int)colsWide; NSMatrix 是 NSView 的子 类。

正例:

- (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes;

反例:

- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;

正例:

- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;

正例:

- (void)setCanHide:(BOOL)flag;      
- (BOOL)canHide;
- (void)setShouldCloseDocument:(BOOL)flag;
- (BOOL)shouldCloseDocument; 

反例:

- (void)setDoesAcceptGlyphInfo:(BOOL)flag; 
- (BOOL)doesAcceptGlyphInfo;

正例:

- (void)getLineDash:(float *)pattern count:(int*)count phase:(float *)phase;

正例:

- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString
*)filename;

正例:

- (void)browserDidScroll:(NSBrowser *)sender;
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;

正例:

- (BOOL)windowShouldClose:(id)sender;

常量定义

示例:

typedef NS_ENUM(NSInteger,NSMatrixMode) {
NSMatrixModeRadio = 0,
NSMatrixModeHighlight = 1,
NSMatrixModeList = 2,
NSMatrixModeTrack = 3
} ;

示例:

typedef NS_OPTIONS(NSUInteger,NSMatrixModeMask) {
NSMatrixModeMaskBorderless     = 0,
NSMatrixModeMaskTitled = 1 << 0,
NSMatrixModeMaskClosable = 1 << 1,
NSMatrixModeMaskMiniaturizable = 1 << 2,
NSMatrixModeMaskResizable = 1 << 3
};
const float NSLightGray;
#ifdef DEBUG

正例:

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;

反例:

#define CompanyName @"Apple Inc."
#define magicNumber 42

常量应该在头文件中以这样的形式暴露给外部:

extern NSString *const ZOCCacheControllerDidClearCacheNotification;

并在实现文件中为它赋值。

正例:

NSColorListIOException
NSColorListNotEditableException 
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException

正例:

NSApplicationDidBecomeActiveNotification  
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

类定义规约

正例:

@interface Item : NSObject
@property (nonatomic, copy) NSString* name;
@end

反例:

@interface Item : NSObject {
    NSString* _name
}
@end
@property(getter=my_XXX ,setter=my_setXXX) id xxx;
if (self == [NSFoo class]) {
    // the initializing code
  }

正例:

[NSImage self];

反例:

[NSImage initialize];

反例:

@interface ALPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation ALPerson
- (void)setName:(NSString *)name {
    self.name = name;//死循环!
}
@end

调用时所有参数应该在同一行:

[myObject doSomethingWith:arg1 name:arg2 error:arg3];

或者每行一个参数,以冒号对齐:

[myObject doSomethingWith:arg1
               name:arg2
              error:arg3];

方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。

[myObj short:arg1
    longKeyword:arg2
    evenLongerKeyword:arg3];
@property (nonatomic, strong, nonnull) Sark *sark;
@property (nonatomic, copy, readonly, nullable) NSArray *friends;
+ (nullable NSString *)friendWithName:(nonnull NSString *)name;

正例

@interface ZOCNewsViewController : UIViewController

- (instancetype)initWithNews:(ZOCNews *)news __attribute__((objc_designated_initializer));
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil __attribute__((unavailable("Invoke the designated initializer,call initWithNews:")));
- (instancetype)init __attribute__((unavailable("Invoke the designated initializer,call initWithNews:"));

@end

@implementation ZOCNewsViewController

- (id)initWithNews:(ZOCNews *)news
{
    //调用直接父类的 designated initializer
    self = [super initWithNibName:nil bundle:nil];
    if (self) {
        _news = news;
    }
    return self;
}

// 重载直接父类的  designated initializer
// 如果你没重载 initWithNibName:bundle: ,而且调用者决定用这个方法初始化你的类(这是完全合法的)。 initWithNews: 永远不会被调用,所以导致了不正确的初始化流程,你的类的特定初始化逻辑没有被执行。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    // call the new designated initializer
    return [self initWithNews:nil];
}

@end

反例

@implementation ParentObject

//designated initializer    
- (instancetype)initWithURL:(NSString*)url title:(NSString*)title {
    if (self = [super init]) {
        _url = [url copy];
        _title = [title copy];
    }
    return self;
}
//secondary initializer
- (instancetype)initWithURL:(NSString*)url {
    return [self initWithURL:url title:nil];
}

@end

@interface ChildObject : ParentObject

@end

@implementation ChildObject
//designated initializer
- (instancetype)initWithURL:(NSString*)url title:(NSString*)title {
    //在designated intializer中调用 secondary initializer,错误的
    if (self = [super initWithURL:url]) {

    }
    return self;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 这里会死循环
    ChildObject* child = [[ChildObject alloc] initWithURL:@"url" title:@"title"];
}
@end

注释规约

代码组织规约

示例:

@interface UIViewController (UIViewControllerRotation)

+ (void)attemptRotationToDeviceOrientation NS_AVAILABLE_IOS(5_0) __TVOS_PROHIBITED;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 6_0) __TVOS_PROHIBITED;
@end

@interface UIViewController (UILayoutSupport)
@property(nonatomic,readonly,strong) id<UILayoutSupport> topLayoutGuide NS_AVAILABLE_IOS(7_0);
@property(nonatomic,readonly,strong) id<UILayoutSupport> bottomLayoutGuide NS_AVAILABLE_IOS(7_0);
@end

@interface UIViewController (UIKeyCommand)
- (void)addKeyCommand:(UIKeyCommand *)keyCommand NS_AVAILABLE_IOS(9_0);
- (void)removeKeyCommand:(UIKeyCommand *)keyCommand NS_AVAILABLE_IOS(9_0);

@end

示例:

#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 {}

每个方法应该只做一件事情。当函数过长时,它做的事情通常会不明确,后续会很难理解与维护。

超过三层已经很难理解一个函数的作用,可以将其中的一些逻辑抽离成一个单独的函数

太长不容易调试,且不直观。

正例:

BOOL isConditionSatisfied = (1 == a.x &&  3==b.y && 2 == c.x);
if (isConditionSatisfied){
 doSomething()
}

反例:

if (a.x = 1 && b.y =3 && c.x = 2){
 doSomething()
}

正例:

if (!error) {
    return success;
}

反对:

if (!error)
    return success;

if (!error) return success;

最佳实践

多线程

正例:

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

正例:

//多线程环境下调用
- (NSCache *)contactCache
{
    if (!_contactCache) {
        @synchronized(self) { 
            if (!_contactCache) {
                _contactCache = [[NSCache alloc] init];
                _contactCache.name = @"contactCache";
            }
        }
    }
    return _contactCache;
}

说明:异步线程默认是没有runloop的,除非手动创建;而主线程是系统会自动创建Runloop的。所以在异步线程调用是请先确保该线程是有Runloop的。

- (void)onMultiThreadNotificationTrigged:(NSNotification *)notify
{
   __weak typeof(self) weakSelf = self;
   __strong typeof(self) strongSelf = wself;
   if (! weakSelf) { 
    return; 
   }

   [strongSelf doSomething];
}

反例:

// 禁止。出现死锁,报错:EXC_BAD_INSTRUCTION。原因:在主队列中同步的添加一个block到主队列中
- (void)viewDidLoad {
  [super viewDidLoad];

  dispatch_queue_t mainQueue = dispatch_get_main_queue();
  dispatch_block_t block = ^() {
      NSLog(@"%@", [NSThread currentThread]);
  };
  dispatch_sync(mainQueue, block);
}

正例:

 // 推荐
- (void)viewDidLoad {
  [super viewDidLoad];

  dispatch_queue_t mainQueue = dispatch_get_main_queue();
  dispatch_block_t block = ^() {
      NSLog(@"%@", [NSThread currentThread]);
  };
  dispatch_async(mainQueue, block); //使用异步操作
}
  • afterDelay会增加receiver的引用计数,cancel则会对应减一
  • 如果在receiver的引用计数只剩下1 (仅为delay)时,调用cancel之后会立即销毁receiver,后续再调用receiver的方法就会crash

正例:

__weak typeof(self) weakSelf = self;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (!weakSelf)
{
//NSLog(@"self被销毁");
    return;
}

[self doOther];

正例:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 
   if (pasteboard.string.length > 0) {//这个方法会阻塞线程
      NSString *text = [pasteboard.string copy];
      [pasteboard setValue:@"" forPasteboardType:UIPasteboardNameGeneral];
      if (text == nil || [text isEqualToString:@""]) {
          return ;
      }
      dispatch_async(dispatch_get_main_queue(), ^{
          [self processShareCode:text];
      });
   }
});

内存管理

  • 对实现 NSCopying协议的对象使用copy方式。通常情况下,诸如NSString、NSURL, block,NSArray 这样的对象应该能被copy;
  • UIView的对象则应该可以被保持。strong引用 子实例,weak引用parent.
  • 基础类型使用assign
  • 容易出现重复创建对象,甚至crash问题
  • initdealloc阶段,self是一个不完整的对象。
  • 由于accessor方法是可以被子类重写的,在调用父类init初始化的时候,使用self访问属性会调到子类重写的(如果有)gettersetter,这就出现了先于子类init访问其属性或调用子类方法的情况,如果子类gettersetter中有一些特殊的处理逻辑,在某些极端情况下就可能出现行为不一致的问题。 由于在init函数返回前,对象结构和结构是不稳定的,在init函数内对任何方法的调用(尤其是public方法)都应该慎之又慎。dealloc同理。

正例:

NSMutableArray *dataList = [NSMutableArray new];
NSMutableArray *imageList = [NSMutableArray new];
[dataList enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
    @autoreleasepool {
        NSData *data = dataList[idx];
        UIImage *image = [[UIImage alloc] initWithData:data];
        //可能对 image 进行一些处理,裁剪之类的
        [imageList addObject:image];
    }
}];
  • 防止在scrollView滑动时页面退出,delegate释放,出现crash问题
  • 苹果在iOS9上已经将以上类的delegatedatasourceassign改为了weak,如果只支持9.0以上,则不需要手动置nil
-(void)dealloc{
    [self unsafeMethod:self]; 
    //因为当前已经在self所指向对象的销毁阶段,如果在unsafeMethod:中将self放到了autorelease pool中,那么self会被retain住,计划下个runloop周期再进行销毁;但是dealloc运行结束后,self对象的内存空间就直接被回收了,self变成了野指针
    //当到了下个runloop周期,self指向的对象实际上已经被销毁,会因为非法访问造成crash问题
}

内存过高将会导致app被kill,并且没有crash堆栈。而申请大内存将会增加内存峰值,更容易出现内存过高而crash。

集合

包括,但不限于 NSMutableDictionay,NSMutableArray,NSMutableSet

正例:

//正确的写法
- (void)checkAllValidItems{
    [_arrayLock lock];
    NSArray *array = [oldArray copy];
    [_arrayLock unlock];
    [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        //do something using obj

    }];
}

反例:

//错误的写法
-(void)checkAllValidItems{
    [self.allItems enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
         //do something using obj
         //如果在enumerate过程中其它线程对self.allItems进行了变更操作,这里就会引发crash
     }];
}

正例:

@property (readonly) NSArray<NSURL *> *imageURLs;

NSDictionary<NSString *, NSNumber *> *mapping = @{@"a": @1, @"b": @2};

反例:

@property (readonly) NSArray *imageURLs;

NSDictionary *mapping = @{@"a": @1, @"b": @2};

字符串

- (NSString *)dt_substringToIndex:(NSUInteger)index
 {
    //... 越界判断
    NSRange wRange = [self rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, index)];
    return [self substringWithRange:wRange];
 }

正例:

- (void) exclusiveMethod1{
    [self.lock lock];

    if (condition == true){
        //这里要记得unlcok,否则下次在进入这个方法就会发生线程被死锁的问题
        [self.lock unlock];
        return;
    }

    [self.lock unlock];
}

- (void) exclusiveMethod2{
    [self.lock lock];

    @try{
        //异常发生
    }@catch(NSException* ex){
    }@finally{
        //此处需要进行锁的回收
        [self.lock unlock];
    }
}

IO

(1) Documents目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。
(2) AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
(3) Library目录:这个目录下有两个子目录:
Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
(4) tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。

UI

- (void)didReceiveMemoryWarning{
    [super didReceiveMemoryWarning];
    [self.view doSomething]; //如果当VC已经被创建,但是view还没有加入到view层级中时(比如Tabbar初始化之后的非选中VC),此时接收到了内存警告,那么self.view会被直接创建,没有加入到层级,导致其子view可能处于异常的状态
}

说明:如果view不在展示时,获取window会是nil,而不是真正的app所在的window.

参考文章
可以直接: [[[UIApplication sharedApplication].delegate window] endEditing:YES];

原因:截屏会消耗大内存和耗性能,不建议使用该技术方案.
推荐使用 snapshotViewAfterScreenUpdates

NSDictionary,NSSet会对加入的对象做strong引用,而NSMapTable、NSHashTable会对加入的对象做weak引用。

Category

正例:

@interface NSString(category)
- (NSString*)ali_stripWhiteSpace;
@end

反例:

@interface NSString(category)
- (NSString*)stripWhiteSpace;
@end

正例:

NSString+ALiCategory.h

反例:

NSStringALiCategory.h
NSString_ALiCategory.h

异常

其它

说明:object通常是指发出notification的对象,如果在发送notification的同时要传递一些信息,请使用userInfo,而不是object.

示例:

NSDate* now = [NSDate date];
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
NSString* string = "1996-12-19T16:39:57-08:00";
NSDate* date = fmt.dateFromString(string);

工程规约

版本管理规约

主版本号:当你做了不兼容的 API 修改,
次版本号:当你做了向下兼容的功能性新增,
修订号:当你做了向下兼容的问题修正。
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

分支管理

功能(feature)分支
预发布(release)分支
修补bug(fixbug)分支

包管理

Show Me The Code!

千言万语,不如直接看代码。这里我们简单写了一个Demo,用于展示规约中比较常见的一些条款的应用。

#import <Foundation/Foundation.h>

/**
 性别的枚举值

 - ALIPersonGenderMale: 男人
 - ALIPersonGenderFemale: 女人
 - ALIPersonGenderUnknown: 不知道是男是女
 */
typedef NS_ENUM(NSInteger, ALIPersonGender) {
    ALIPersonGenderMale,
    ALIPersonGenderFemale,
    ALIPersonGenderUnknown
};

/**
 人的基本类型
 */
@interface ALIPerson : NSObject

/**
 名称
 */
@property(nonatomic, copy, nonnull) NSString *name;

/**
 性别
 */
@property(nonatomic, assign) ALIPersonGender gender;

/**
 初始化方法

 @param aName 人的名字
 @param aGender 人的性别
 @return 返回人的实例
 */
- (nonnull instancetype)initWithName:(nonnull NSString *)aName gender:(ALIPersonGender)aGender;

@end


/**
 一个人的体育运动
 */
@interface ALIPerson (Sports)

/**
 扩展方法强调要有前缀,以免冲突
 */
- (void)alPlayBasketBallWithPerson:(nullable ALIPerson *)person;

@end

/**
 程序员常用的语言枚举

 - ALICoderLanguagesJava: Java
 - ALICoderLanguagesC: C语言
 - ALICoderLanguagesCPP: C++
 - ALICoderLanguagesJavaScript: JavaScript
 - ALICoderLanguagesPHP: PHP是世界上最好的语言
 - ALICoderLanguagesRuby: ruby
 - ALICoderLanguagesPython: python
 - ALICoderLanguagesOC: Objective-C
 */
typedef NS_OPTIONS(NSUInteger, ALICoderLanguages){
    ALICoderLanguagesNone = 0,
    ALICoderLanguagesJava = 1 << 0,
    ALICoderLanguagesC = 1 << 1,
    ALICoderLanguagesCPP = 1 << 2,
    ALICoderLanguagesJavaScript = 1 << 3,
    ALICoderLanguagesPHP = 1 << 4,
    ALICoderLanguagesRuby = 1 << 5,
    ALICoderLanguagesPython = 1 << 6,
    ALICoderLanguagesOC = 1 << 7
};

/**
 程序员的基本协议
 */
@protocol ALICoderProtocol <NSObject>

/**
 对大家说一句helloworld

 @return 具体的helloworld内容
 */
- (nonnull NSString *)sayHelloWorld;


/**
 喜欢的语言

 @return 具体的语言,这里为了做事例用了mask
 */
- (ALICoderLanguages)preferLanguages;

@end

//Notification消息使用全局的 NSString 对象进行标识,其名称按如下的方式组合:
//[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
//程序员即将写出一个bug的通知
extern NSString * _Nonnull const ALIChinaCoderWillWriteBugsNotification;
//程序员已经制造了一个bug的通知
extern NSString * _Nonnull const ALIChinaCoderDidWriteBugsNotification;

/**
 程序员的代理,主要负责在程序员要写出bug的时候告诉代理,让代理处理下,是否要修复bug,或者打醒程序员
 */
@protocol ALIChinaCoderDelegate <NSObject>

/**
 程序员即将写出bug的回调

 @param coder 程序员本身
 @param partner 程序员的搭档
 */
- (void)coder:(nonnull ALIPerson<ALICoderProtocol> *)coder willWriteBugsWithPartner:(nullable ALIPerson<ALICoderProtocol> *)partner;

/**
 程序员已经写出bug的回调

 @param coder 程序员本身
 @param partner 程序员的搭档,我们一般认为跟妹子一起,由于程序员喜欢装逼,更容易写出bug
 */
- (void)coder:(nonnull ALIPerson<ALICoderProtocol> *)coder didWriteBugsWithPartner:(nullable ALIPerson<ALICoderProtocol> *)partner;


@end

/**
 中国程序员
 */
@interface ALIChinaCoder : ALIPerson<ALICoderProtocol>

@property(nonatomic, weak, nullable) id<ALIChinaCoderDelegate> delegate;
/**
 结对编程

 @param partner 一起编程的程序员
 */
- (void)pairProgrammingWithPartner:(nullable ALIPerson<ALICoderProtocol> *)partner;

@end

/**
 美国程序员
 */
@interface ALIUSACoder : ALIPerson<ALICoderProtocol>

@end
#import "ALIPerson.h"

@implementation ALIPerson

#pragma mark - Life Cycle

- (instancetype)initWithName:(NSString *)aName gender:(ALIPersonGender)aGender{
    if (self = [super init]) {
        _name = aName;
        _gender = aGender;
    }
    return self;
}

@end

NSString * const ALIPersonNameYaoMing = @"姚明";

@implementation ALIPerson (Sports)

- (void)alPlayBasketBallWithPerson:(ALIPerson *)person{
    if ([person.name isEqualToString:ALIPersonNameYaoMing]) {
        NSLog(@"把球给我,我要回家!");
    }else{
        NSLog(@"看我装逼看我飞");
    }
}

@end

//尽量声明变量而非用宏
NSString * const ALIChinaCoderNameNotFound = @"没人跟你说你好世界";

NSString * const ALIChinaCoderWillWriteBugsNotification = @"ALChinaCoderWillWriteBugsNotification";
NSString * const ALIChinaCoderDidWriteBugsNotification = @"ALChinaCoderDidWriteBugsNotification";

@implementation ALIChinaCoder

#pragma mark - ALCoderProtocol

- (NSString *)sayHelloWorld{
    if (!self.name) {
        return ALIChinaCoderNameNotFound;
    }
    return [NSString stringWithFormat:@"%@说你好世界",self.name];
}

- (ALICoderLanguages)preferLanguages{
    return ALICoderLanguagesOC|ALICoderLanguagesJava|ALICoderLanguagesC;
}

#pragma mark - public

- (void)pairProgrammingWithPartner:(ALIPerson<ALICoderProtocol> *)partner{
    switch (partner.gender) {
        case ALIPersonGenderMale: {
            NSLog(@"我去。。");
            break;
        }
        case ALIPersonGenderFemale: {
            [[NSNotificationCenter defaultCenter] postNotificationName:ALIChinaCoderWillWriteBugsNotification object:self];
            [self.delegate coder:self willWriteBugsWithPartner:partner];
            NSLog(@"哦也。。");
            NSLog(@"看我手把手教妹子写代码");
            [[NSNotificationCenter defaultCenter] postNotificationName:ALIChinaCoderDidWriteBugsNotification object:self];
            [self.delegate coder:self didWriteBugsWithPartner:partner];
            break;
        }
        default: {
            NSLog(@"看情况再说。。");
            break;
        }
    }
}

@end

NSString * const ALIUSACoderNameNotFound = @"No one say hello world";

@implementation ALIUSACoder

#pragma mark - ALICoderProtocol

- (NSString *)sayHelloWorld{
    if (!self.name) {
        return ALIUSACoderNameNotFound;
    }
    return [NSString stringWithFormat:@"%@ say Hello World",self.name];
}

- (ALICoderLanguages)preferLanguages{
    return ALICoderLanguagesPHP;
}


@end
上一篇 下一篇

猜你喜欢

热点阅读