类型常量:使用类型常量代替#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(); }
如果块是直接定义的,没有捕获任何外围变量,那么它的全部信息能在编译器确定,(区别于栈块、堆块)称作全局块。
-