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

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

-

原文地址:https://www.cnblogs.com/Bob-wei/p/4550532.html