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 = << ,
EOCPermittedDirectionDown = << ,
EOCPermittedDirectionLeft = << ,
EOCPermittedDirectionRight = << ,
};
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 :;
unsigned int didFailWithError :;
unsigned int didUpdateProgressTo :;
} _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();
}
如果块是直接定义的,没有捕获任何外围变量,那么它的全部信息能在编译器确定,(区别于栈块、堆块)称作全局块。
-
Effective Objective-C手记的更多相关文章
- 【iOS】关联属性存取数据
有时候我们需要在现有的类存放一些额外的信息,通常的做法是继承一个子类,然后定义新增加的属性,然而如果我们为每个需要的类都添加一个类显得太麻烦了,objc提供了一个关联属性的特性,可以给一个对象关联一个 ...
- iOS 学习资源
这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...
- Automake
Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...
- iOS学习资料整理
视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS S ...
- iOS 学习
iOS 学习资料 (适合初学者) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解 ...
- iOS 学习资料汇总
(适合初学者入门) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解了一些 iOS ...
- ios书籍推荐
1.Objective-C Programming 内容不多, 却都是精华, 有了一点 C 语言基础可以快速阅读此书, 大概一天时间就可以看完, 看完后对 iOS 开发能够有个基本的印象. 2.iO ...
- iOS Learning
转载自:http://www.cocoachina.com/ios/20150111/10894.html iOS 学习资料整理 2015-01-11 20:20 编辑: suiling 分类:iOS ...
- BlocksKit的使用
一.引言 众所周知Block已被广泛用于iOS编程.它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调.Block比传统回调函数有2点优势: 允许在调用点上下文书写执行逻辑,不用分离函数 ...
随机推荐
- Testlink安装访问提示“应用程序DEFAULT WEB SITE”中的服务器错误
错误摘要:HTTP错误403.14 - ForbiddenWeb服务器被配置为不列出此目录的内容.
- 什么是PHP闭包???
闭包函数:临时创建一个没有名称的函数,经常作为回调函数来用. 通俗的说就是:子函数可以使用父函数中的局部变量,这种行为叫做闭包. 1.匿名函数赋值 $demo=function($str){ echo ...
- 【C语言】linux C写入本地文件
//定义写入文件 FILE *pFile; //定义文件路径变量 ]; //变量赋值 sprintf(local_file,"/tmp/test.json"); //打开文件 pF ...
- vim tabs
:tabnew 增加一个标签:tabc 关闭当前的tab:tabo 关闭所有其他的tab:tabs 查看所有打开的tab:tabp 或gT 前一个:tabn 或gt 后一个:tabfirst或:tab ...
- spring boot 以jar的方式启动常用shell脚本
用spring boot框架做的项目,将第三方包全部打在jar里面,通过shell脚本启动和停止服务,常用的shell脚本模板如下: #!/bin/bashJAVA_OPTIONS_INITIAL=- ...
- HDU 1159 Common Subsequence(POJ 1458)
Common Subsequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- Android签名机制之---签名过程详解
http://www.2cto.com/kf/201512/455388.html 一.前言 又是过了好长时间,没写文章的双手都有点难受了.今天是圣诞节,还是得上班.因为前几天有一个之前的同事,在申请 ...
- jsonp跨域总结
同源限制: 浏览器不允许一个域的脚本请求另一个域的文档,通俗来说,不允许访问不同协议.不同域名或不同端口的文档 跨域处理方法: 1.jsonp 前提: 浏览器虽然对ajax请求做出了限制,但scrip ...
- nodejs往文件写入内容代码
const fs = require("fs"); // fs.wirteFile有三个参数 // 1,第一个参数是要写入的文件路径 // 2,第二个参数是要写入得内容 // 3, ...
- INSPIRED启示录 读书笔记 - 第41章 产品经理的反省清单
十大问题 1.产品能吸引目标消费者的关注吗? 2.产品的设计是否人性化,是否易于操作? 3.产品能在竞争中取胜吗?即使是面对未来风云变化的市场,依旧有取胜的把握吗? 4.我了解目标用户吗?产品(不是理 ...