美文网首页
<禅与 Objective-C 编程艺术>阅读记录

<禅与 Objective-C 编程艺术>阅读记录

作者: mobilefeng | 来源:发表于2016-08-10 14:07 被阅读30次

    1. nil检查

    • if(!boolValue)代替if(boolValue == nil)

    例子:AMKElement判断非主线程

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
        return;
    }
    

    2. 将次要分支写在前,并用return,防止一大坨代码包在if里面

    推荐:

    - (void)someMethod {
      if (![someOther boolValue]) {
          return;
      }
      //Do something important
    }
    

    不推荐:

    - (void)someMethod {
      if ([someOther boolValue]) {
        //Do something important
      }
    }
    

    例子:

    // 若不在主线程,则切到主线程再调用本方法
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
        return;
    }
    
    // 在主线程
    // 处理一大坨事情
    

    3. 复杂的if语句,应该把判断条件提取出来设为一个BOOL变量

    例子:

    BOOL isMathced = [predicate evaluateWithObject:element];
    if (isMathced) {
        UIView *view = (UIView *)element;
    }
    return isMathced;
    

    4. 三元运算符

    例子:

    UILabel *label = (labels.count > 0 ? labels[0] : nil);
    
    • 当三元运算符的第一个分支就是判断条件时,建议写成:
      result = object ? : [self createObject];

    例子:

    UIWindow *window = _viewController.view.window ?: [[[UIApplication sharedApplication] delegate] window];
    

    5.错误处理

    推荐:

    NSError *error = nil;
    if (![self trySomethingWithError:&error]) {
        // Handle Error
    }
    

    不推荐:

    NSError *error;
    [self trySomethingWithError:&error];
    if (error) {
        // Handle Error
    }
    

    错误例子:

    • <s>如上删除文件,在删除的过程中可能对error变量进行赋值,然后打印error,可知错误信息</s>
    NSError *error = nil;
    [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
    if (error) {
        NSLog(@"Move failed: %@", error);
    }
    
    • 好吧我一开始理解错了,这个例子给的是NSError常规的用法,但是此条规则的本意是不推荐检查error的引用,而应该检查方法的返回值

    正确例子:

    NSError *error = nil;
    if (![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
        NSLog(@"Move failed: %@", error);
    }
    

    6.常量

    • 推荐使用驼峰写法
    • 建议用static声明为静态常量,不建议用define定义常量,除非你要定义宏

    例子:

    static const double kElementSufficientlyVisiblePercentage = 0.75;
    
    • 需要暴露给外部的常量,在头文件中用extern标示,并在实现文件中赋值

    例子:

    // xxx.h
    extern NSString *const AMKJavaScriptWillStartLoadingNotification;
    // xxx.m
    NSString *const AMKJavaScriptWillStartLoadingNotification = @"AMKJavaScriptWillStartLoadingNotification";
    

    7.方法名

    • 参数前加一个描述性的关键词
    • 不建议用and来表示多个参数

    推荐:

    - (void)setExampleText:(NSString *)text image:(UIImage *)image;
    - (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
    - (id)viewWithTag:(NSInteger)tag;
    - (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
    

    不推荐:

    - (void)setT:(NSString *)text i:(UIImage *)image;
    - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
    - (id)taggedView:(NSInteger)tag;
    - (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
    - (instancetype)initWith:(int)width and:(int)height;  // Never do this.
    

    8.字面值

    • 建议用字面值创建不可变的NSString, NSDictionary, NSArray, 和 NSNumber 对象,不要将 nil 传进 NSArray 和 NSDictionary 里

    推荐:

    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", @"Mobile Web", nil];
    NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
    NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];
    
    • 对于可变版本,推荐使用 NSMutableArray, NSMutableString 等
    • @[] mutableCopy这种写法不推荐
    • 但是还是使用可变拷贝mutableCopy

    推荐:

    NSMutableArray *tmpList = [[NSMutableArray alloc] init];
    NSMutableString *mutableString = [originalString mutableCopy];
    

    不推荐:

    NSMutableArray *tmpList = [@[] mutableCopy];
    

    9.初始化方法

    • 指定初始化方法(designated initializer)
    • 标注哪一个初始化方法是designated,用编译器指令 __attribute__((objc_designated_initializer)) 这样如果新的designated initializer没有调用超类的designated initializer,就会警告

    例子:

    #define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
    
    - (instancetype)init;
    - (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
    

    10.instancetype V.S. id

    这篇文章对两者的差别讲的蛮清楚的
    http://blog.csdn.net/wzzvictory/article/details/16994913

    • 原来未知的返回类型对象,都 id 关键词;直到clang 3.5开始提供 instancetype 关键词
    • 关联返回类型:会返回一个方法所在类类型的对象,系统的alloc、new、init等是关联返回类型的,比如[NSArray alloc]返回的就是NSArray*[[NSArray alloc] init]返回的也是NSArray*
    • instancetype的作用,就是使那些非关联返回类型的方法返回所在类的类型
    // 申明返回类型为id
    @interface NSArray  
    + (id)constructAnArray;  
    @end 
    
    [NSArray constructAnArray]; // 得到的返回类型和申明的一样,即id
    
    // 申明返回类型为instancetype
    @interface NSArray  
    + (instancetype)constructAnArray;  
    @end 
    
    [NSArray constructAnArray]; // 得到的返回类型是所在类的类型,即NSArray*
    
    • 返回类类型的好处是,编译器能够在编译截断就判断返回类型能否实现
    // 编译时会报错 : "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
    [[[NSArray alloc] init] mediaPlaybackAllowsAirPlay];
    // 编译时不会报错
    [[NSArray array] mediaPlaybackAllowsAirPlay];
    
    • instancetype 的优势是能返回所在类类型(id 只能返回未知类型)
    • id 的优势是既能当返回值,又能当参数(instancetype 只能当返回值)
    • 所以最好的做法就是,用 id 当参数,用 instancetype 当返回值(当然针对的是会返回类的实例的方法)
    • 这样做法一个额外的好处就是,可以一目了然知道哪些方法返回了类的实例

    例子

    - (instancetype)initWithAccessibilityElement:(id)accessibilityElement{
        self = [super init];
        if (self) {
            _accessibilityElement = accessibilityElement;
        }
        return self;
    }
    

    写到这里,例子很多是从@巴格的InAppMonkey中摘录出来的,不得不说,代码功底很好,几乎所有写法都是Best Practice,值得学习!!!

    11. 懒加载

    • 当实例化一个对象耗费资源较多,就需要重写getter方法以延迟实例化,而不是在init方法里给对象分配内存

    例子

    - (NSString*)automationDirectory{
        if (!_automationDirectory) {
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            _automationDirectory = [paths objectAtIndex:0];
            _automationDirectory = [_automationDirectory stringByAppendingPathComponent:@"TMUIAutomation"];
        }
        if (![[NSFileManager defaultManager] fileExistsAtPath:_automationDirectory]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:_automationDirectory withIntermediateDirectories:NO attributes:nil error:nil];
        }
        return _automationDirectory;
    }
    

    12. 参数断言

    • 你的方法可能要求一些参数来满足特定的条件(比如不能为nil),在这种情况下最好使用 NSParameterAssert() 来断言条件是否成立或是抛出一个异常

    例子

    - (NSString *)grey_printDescriptionForElement:(id)element atLevel:(NSUInteger)level {
        AMKSureNotStopReturnValue(nil)
    
        NSParameterAssert(element);
        NSMutableString *printOutput = [NSMutableString stringWithString:@""];
        
        //...
    }
    

    13. Category

    • 建议在category类名中使用前缀

    例子

    @interface NSString(AMK)
    
    - (NSString *)amk_decodeHTMLCharacterEntities;
    - (NSString *)amk_encodeHTMLCharacterEntities;
    
    @end
    

    14. Protocol

    • 这块 Zen 给的建议没有看明白

    15. NSNotification

    • 当自定义NSNotification时,应该把通知的名字定义为一个字符串常量,然后在公开接口中将其声明为extern

    例子

    // AMKBridge.h
    extern NSString *const AMKReloadNotification;
    // AMKBridge.m
    NSString *const AMKReloadNotification = @"AMKReloadNotification";
    

    16. pragma

    • 当你使用ARC的时候,编译器帮你插入了内存管理相关的调用。但是这样可能产生一些烦人的事情。比如你使用 NSSelectorFromString 来动态地产生一个 selector 调用的时候,ARC不知道这个方法是哪个并且不知道应该用那种内存管理方法,你会被提示 performSelector may cause a leak because its selector is unknown
    • 如果你知道你的代码不会导致内存泄露,你可以通过加入这些代码忽略这些警告

    例子

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [navBar.topItem.leftBarButtonItem.target performSelector:navBar.topItem.leftBarButtonItem.action withObject:navBar.topItem.leftBarButtonItem];
    #pragma clang diagnostic pop
    

    相关文章

      网友评论

          本文标题:<禅与 Objective-C 编程艺术>阅读记录

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