zoukankan      html  css  js  c++  java
  • Effective Objective-C手记

    类型常量:使用类型常量代替#define

    1. 普通常量

    //.m
    #import "xxxxx.h"
    static const NSTimerInterval kAnimationDuration = 0.3; //放在.m文件的#import之后即可,常量名称以k开头。
    @implemenation xxxxx
    @end

    2. 公开常量

    //注意不同类型的写法,最好以类名作为前缀。
    //xxxxx.h
    extern NSString *const xxxxxStringConstant;
    extern const NSTimerInterval xxxxxAnimationDuration;
    
    //xxxxx.m
    NSString *const xxxxxStringConstant = @"VALUE";
    const NSTimerInterval xxxxxAnimationDuration = 0.3;

    枚举类型的声明:用NS_ENUM代替普通枚举,用NS_OPTIONS代替定义选项的枚举

    1. NS_ENUM极其传统写法

    //声明一个EOCConnectionState枚举
    typedef NS_ENUM(NSUInteger, EOCConnectionState) {
        EOCConnectionStateDisconnected,
        EOCConnectionStateConnecting,
        EOCConnectionStateConnected,
    };
    
    //传统写法:
    enum EOCConnectionState : NSUInteger {
        EOCConnectionStateDisconnected,
        EOCConnectionStateConnecting,
        EOCConnectionStateConnected,
    };
    typedef enum EOCConnectionState EOCConnectionState;

    2. NS_OPTIONS极其传统写法

    //声明一个EOCPermittedDirection选项枚举
    typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
        EOCPermittedDirectionUp,
        EOCPermittedDirectionDown,
        EOCPermittedDirectionLeft,
        EOCPermittedDirectionRight,
    };
    
    //传统写法:
    enum EOCPermittedDirection: NSUInteger {
        EOCPermittedDirectionUp     = 1 << 0,
        EOCPermittedDirectionDown   = 1 << 1,
        EOCPermittedDirectionLeft   = 1 << 2,
        EOCPermittedDirectionRight  = 1 << 3,
    };
    typedef enum EOCPermittedDirection EOCPermittedDirection;

    属性特质:原子性、读写权限、内存管理语义、方法名

    1. 原子性:
    atomic或者不写,表示使用同步锁;
    nonatomic,表示不使用同步锁。

    在iOS开发中同步锁会非常影响性能,所以包括SDK在内一般都使用nonatiomic,也就是说iOS系统中的属性通常不是线程安全的。
    在OSX中不存在性能问题。

    2. 读写权限:
    readwrite,同时拥有getter和setter方法;
    readonly,只有getter方法。

    该属性由@synthesize实现,编译器才会自动生成相应的getter、setter。新版本不再需要指定@synthesize了。

    3. 内存管理语义:
    assign,对简单类型的赋值操作。
    strong,强类型。
    weak,弱类型。
    unsafe_unretained,与weak类似,不同的是,当对象被销毁时,属性值不会自动被清空。
    copy,与strong类似,不同的是,设置方法不会保留新值,而是将其拷贝。

    由于NSString*的新值可能被指定为NSMutableString的实例,为防止此属性值被修改,使用copy特质。
    延伸:新值可能为mutable的对象的,都应该copy。

    //若声明copy特质的属性
    @property (copy) NSString *title;
    
    //在实现自定义的初始化方法时,也要遵从copy
    _title = [aNSString copy];
    
    /* 注意:其他的特质也是同样道理。 */

    4. 方法名:
    getter=<name>,指定getter方法名,如果属性是Boolean型,可以指定is前缀,如: @property (noatomic, getter=isOn) BOOL on; 
    setter=<name>,指定setter方法名,不常用。

    实现description方法:NSObject返回“<类名, 指针>”格式字符串,重写它,方便NSLog和(lldb)调试。

    1. description方法

    - (NSString *)description
    {
        return [NSString stringWithFormat:@"<%@: %p, "%@ %@">", [self class],self,_firstName,_lastName]; 
    }

    2. debugDescription方法:如果不重写,就是简单调用description方法。这个方法在(lldb)调试的时候,用po命令打印输出,如:po person

    -(NSString *)debugDescription {
        return [NSString stringWithFormat:@"<%@: %p, %@>",
                [self class],
                self,
                @{@"firstName":_firstName,
                  @"lastName":_lastName,
                  }];
    }

    多个属性可以使用NSDictionary的description方法,如上面这个实现。

    输出窗口:

    (lldb) po person
    <XXXPerson: 0x7fd871f00080, {
        firstName = Bob;
        lastName = Wei;
    }>
    
    2015-06-08 17:32:41.094 test-coredata[4774:269533] person = <XXXPerson: 0x7fd871f00080, "Bob Wei">

    实现NSCopying协议:重写copyWithZone方法。

    //.h
    #import <Foundation/Foundation.h>
    
    @interface XXXPerson : NSObject <NSCopying>
    @property (nonatomic, copy, readonly) NSString *firstName;
    @property (nonatomic, copy, readonly) NSString *lastName;
    - (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
    - (void)addFriend:(XXXPerson *)person;
    - (void)removeFriend:(XXXPerson *)person;
    @end
    
    //.m
    #import "XXXPerson.h"
    
    @implementation XXXPerson {
        NSMutableSet *_friends;
    }
    
    - (void)addFriend:(XXXPerson *)person {
        [_friends addObject:person];
    }
    
    - (void)removeFriend:(XXXPerson *)person {
        [_friends removeObject:person];
    }
    
    - (id)copyWithZone:(NSZone *)zone {
        XXXPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName lastName:_lastName];
        copy->_friends = [_friends mutableCopy];
        return copy;
    }
    
    @end

    如果实现NSMutableCopying协议:重写mutableCopyWithZone方法,并且应该总是返回可变对象(mutableCopy方法),而重写的copyWithZone总应该返回不变对象(copy方法)。

    以上代码实现了一个浅拷贝,如果需要实现深拷贝,代码如下:

    - (id)deepCopy {
        XXXPerson *copy = [[[self class] alloc] initWithFirstName:_firstName lastName:_lastName];
        copy->_friends = [[NSMutableSet alloc] initWithSet:_friends copyItems:YES];
        return copy;
    }

    NSMutableSet的initWithSet:方法中,copyItems为YES时,会复制每一项,即实现了NSSet集合的深拷贝。

    委托和数据协议:用一个结构体来防止[delegate respondsToSelector:]方法被反复调用。

    //.h
    #import <Foundation/Foundation.h>
    
    @class XXXNetworkFetcher;
    
    @protocol XXXNetworkFetcherDelegate <NSObject>
    
    @optional
    - (void)networkFetcher:(XXXNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
    - (void)networkFetcher:(XXXNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
    - (void)networkFetcher:(XXXNetworkFetcher *)fetcher didUpdateProgressTo:(float)progress;
    
    @end
    
    @interface XXXNetworkFetcher : NSObject
    
    @property (weak, nonatomic) id<XXXNetworkFetcherDelegate> delegate;
    
    @end
    
    //.m
    #import "XXXNetworkFetcher.h"
    
    @interface XXXNetworkFetcher() {
    //C语言特性:“位段”(bitfield)数据类型,右边数字1表示只占用一个二进制位
    struct { unsigned int didReceiveData :1; unsigned int didFailWithError :1; unsigned int didUpdateProgressTo :1; } _delegateFlags; } @end @implementation XXXNetworkFetcher - (void)setDelegate:(id<XXXNetworkFetcherDelegate>)delegate { _delegate = delegate; _delegateFlags.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)]; _delegateFlags.didFailWithError = [delegate respondsToSelector:@selector(networkFetcher:didFailWithError:)]; _delegateFlags.didUpdateProgressTo = [delegate respondsToSelector:@selector(networkFetcher:didUpdateProgressTo:)]; } - (void)foo { NSData *data = [[NSData alloc] init]; if (_delegateFlags.didReceiveData) { [_delegate networkFetcher:self didReceiveData:data]; } } @end

    块block:栈块、堆块和全局块。

    定义块的时候会被分配到栈中,它只在定义范围内有效。如果在if..else语句中定义块(栈块),有可能导致块呗编译器复写掉。加入copy方法后会将块拷贝到堆中(堆块),就避免了上述可能发生的错误:

    - (void)foo {
        void(^block)();
        _switch = !_switch;
        if (_switch) {
            block = [^{
                NSLog(@"Block A");
            } copy];
        } else {
            block = [^{
                NSLog(@"Block B");
            } copy];
        }
        block();
    }

    如果块是直接定义的,没有捕获任何外围变量,那么它的全部信息能在编译器确定,(区别于栈块、堆块)称作全局块。

    -

  • 相关阅读:
    康复计划
    Leetcode 08.02 迷路的机器人 缓存加回溯
    Leetcode 38 外观数列
    Leetcode 801 使序列递增的最小交换次数
    Leetcode 1143 最长公共子序列
    Leetcode 11 盛水最多的容器 贪心算法
    Leetcode 1186 删除一次得到子数组最大和
    Leetcode 300 最长上升子序列
    Leetcode95 不同的二叉搜索树II 精致的分治
    Leetcode 1367 二叉树中的列表 DFS
  • 原文地址:https://www.cnblogs.com/Bob-wei/p/4550532.html
Copyright © 2011-2022 走看看