多用块枚举 少有for循环
遍历collection有四种方式,如下:
- 最新、最先进的方式,而且能够通过GCD来并发执行遍历操作
NSArray *anArray = /*...*/;
[anArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){
if(shouldStop){
*stop = YES;//相当于break
}
}];
NSDictionary *aDictionary = /*...*/
[aDictionary enumerateObjectsUsingBlock:^(id key, id object, BOOL *stop){
if(shouldStop){
*stop = YES;//相当于break
}
}];
NSSet *aSet = /*...*/
[aSet enumerateObjectsUsingBlock:^(id object, BOOL *stop){
if(shouldStop){
*stop = YES;//相当于break
}
}];
- 快速遍历
NSArray *anArray = /*...*/;
for(id object in anArray)
{
...
}
NSDiction *aDictionary = /*...*/;
for(id object in aDictionary)
{
...
}
NSSet *aSet = /*...*/;
for(id object in aSet)
{
...
}
//反序遍历
NSArray *anArray = /*...*/;
for(id object in [anArray reverseObjectEnumerator])
{
...
}
- NSEnumerator
NSArray *anArray = /*...*/
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while([object = [enumerator nextObject]] != nil)
{
...
}
NSDictionary *aDictionary = /*...*/
NSEnumerator *enumerator = [aDictionary keyEnumerator];
id key;
while([key = [enumerator nextObject]] != nil)
{
NSDictionary *dic = aDictionary[key];
...
}
NSSet *aSet = /*...*/
NSEnumerator *enumerator = [aSet objectEnumerator];
id object;
while([object = [enumerator nextObject]] != nil)
{
...
}
- for循环
NSArray *anArray = /*...*/;
for(int i = 0; i < anArray.count, ++i)
{
...
}
NSDiction *aDictionary = /*...*/;
NSArray *keys = [aDictionary allKeys];
for(int i = 0; i < keys.count, ++i)
{
id object = aDictionary[keys[i]];
...
}
NSSet *aSet = /*...*/;
NSArray *objects = [aSet allObjects];
for(int i = 0; i < objects.count, ++i)
{
id object = objects[i];
...
}
构建缓存时使用NSCache而非NSDictionary
原因
- 使用NSCache,当系统资源耗尽时,能够自动删减内存,而且会删除最久未使用的对象,而NSDictionary却需要开发者手工删减内存。
- NSCache是线程安全的,可以多个线程同时访问
- NSPurgeableData与NSCache结合使用效果更好
示例
//FSJClass.h
#import <Foundation/Foundation.h>
@interface FSJClass : NSObject
@end;
//FSJClass.m
@implementation FSJClass (){
NSCache *_cache;
}
-(id)init
{
if(self = [super init])
{
_cache = [[NSCache alloc] init]
_cache.countLimit = 100;
_cache.totalCostLimit = 5 * 1024 * 1024;
}
return self;
}
-(void)DownloadDataForURL:(NSURL*)url
{
NSPurgeableData *cacheData = [_cache objectForKey:url];
if(cacheData)
{
[cacheData beginContentAccess];
//use cache data
[cacheData endContentAccess];
}else{
//cache miss
FSJNetworkFetcher *fetcher =[[FSJNetworkFetcher alloc] initWithURL:url];
[fetcher startWithCompletionHandler:^(NSData *data){
//这句相当于[purgeableData beginContentAccess],因此已经mark
NSPurgeableData *purgeableData = [NSPurgeableData dataWithdata:data];
[_cache setObject:purgeableData forKey:url cost:purgeableData.length];
//use cache data
[purgeableData endContentAccess];
}];
}
}
@end
精简initialize和load的实现代码
时下编写代码时不需要用它,仅在调试的时候使用,例如判断该分类是否已经加载到系统中
- 假如运行期系统中的每个类(class)以及分类(category),必定会调用此方法,而且仅调用一次,调用的时机是加载阶段
- 先调用类里面的load,在调用分类里的
- 调用子类的load之前,必定执行所有超类躲得load
- 代码依赖了其他程序库,那么程序库里的相关类的load方法也必定会执行,但是其调用顺序不能确定
- 应用程序执行load方法会阻塞
- load方法不参与覆写机制
+(void)load;
在首次使用某个类之前,系统会调用initialize,且只调用一次
- 无法在编译期设定的全局常量,可以在initialize方法里初始化
+(void)initialize;
initialize与load的区别
- 惰性调用。initialize只有程序用到相关类的时候,才会调用。而load在加载阶段就把所有类的load方法执行一遍
- 线程安全。只有执行initialize的那个线程可以操作类或类实例,其他线程都要先阻塞,等着initialize执行完
- 如果某个类未实现它,而其超类实现了,那么就会运行超类的实现代码,这就是覆写机制,因此应该判断当前要初始化哪个类
NSTimer会保留目标对象
因为NSTimer会保留目标对象,而目标对象会持有NSTimer对象,因此二者陷入循环引用,需要invalidate使NSTimer失效打破循环。
- 如果开发者设置为非重复执行模式,执行完任务后,计时器会失效;
- 调用invalidate也会使计时器失效,特别是设置为重复执行模式的时候,需要开发者调用invalidate来停止计时器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
直接调用以上方法,target指向self,self又拥有NSTimer,这样容易产生保留环,只有开发者手动调用invalidate使NSTimer无效来打破保留环,但是代码又不一定保证调用invalidate,因此容易产生内存泄露。对此,以下方法可以打破保留环:
#import <Foundation/Foundation.h>
@interface NSTimer (FSJBlockSupport)
+(NSTimer *)fsj_scheduledTimerWithTimeInterval:(NSTimeInterval)ti block:(void(^)())block brepeats:(BOOL)yesOrNo;
@end
@implementation NSTimer (FSJBlockSupport)
+(NSTimer *)fsj_scheduledTimerWithTimeInterval:(NSTimeInterval)ti block:(void(^)())block brepeats:(BOOL)yesOrNo
{
//让target指向NSTimer,这样不会产生保留环
return [NSTimer scheduledTimerWithTimeInterval:ti target:self selector:@selector(fsj_blockInvoke:) userInfo:[block copy] repeats:yesOrNo];
}
+(void)fsj_blockInvoke:(NSTimer*)timer
{
void (^block)() = timer.userInfo;
if(block){
block();
}
}
@end
按照如下方法调用
-(void)dealloc
{
[_timer invalidate];
}
-(void)startTimer
{
//生成weakself,NSTimer持有self,但不拥有self,打破循环引用
__weak FSJClass *weakSelf = self;
_timer = [NSTimer fsj_scheduledTimerWithTimeInterval:0.5 block:^{
//strongSelf只在block中有效,超出作用域会被回收
FSJClass *strongSelf = weakSelf;
[strongSelf doSomething];
} brepeats:YES];
}
-(void)doSomething
{
//do something
}
本文介绍Objective-C中多种遍历方法及优劣对比,包括使用GCD并发执行的最新枚举方式,并探讨NSCache相较于NSDictionary的优势及应用实例。
919

被折叠的 条评论
为什么被折叠?



