• 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();
    }

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

    -

  • 相关阅读:
    XMLHttpRequest简介
    BC30138: 无法在路径“C:\WINDOWS\TEMP\”中创建临时文件: 拒绝访问。
    开机explorer无法启动,无法进入桌面
    选择DataGrid中的CheckBox控件后该行背景变色
    CSS菜单制作工具
    ScriptX打印控件的使用
    JS实时预览上传图片缩略图
    readyState的五种状态详解
    xmlHttpRequest的status的值的含义
    C#调用word打印
  • 原文地址:https://www.cnblogs.com/Bob-wei/p/4550532.html
Copyright © 2020-2023  润新知