作者:快活林2007 | 来源:互联网 | 2023-05-17 21:06
第47条:熟悉系统框架若是不了解系统架构所提供的内容,那么就可能会把其中已经实现的东西再写一遍。将一系列代码封装为动态库(dynamiclibrary),并在其中放入描述其接口的头文
第47条:熟悉系统框架
若是不了解系统架构所提供的内容,那么就可能会把其中已经实现的东西再写一遍。将一系列代码封装为动态库(dynamic library),并在其中放入描述其接口的头文件,这样做出来的东西就叫框架。然而iOS应用程序不允许在其中包含动态库。
开发者使用最对的是Foundation框架,像NSObject、NSArray、NSDictionary等类都在其中。Foundation框架中的类使用NS前缀,此前缀是OC语言作为NeXTSPET操作系统的编程语言首度确定的。Foundation真可谓OC应用程序的基础,Foundation不仅提供了collection等基础核心功能,而且还提供了字符串处理这样的复杂功能。比如说,NSLinguisticTagger可以解析字符串并找到其中的全部名词、动词、代词等。
还有一个与Foundation相伴的框架,叫CoreFoundation。虽然从技术上讲CoreFoundation不是OC框架,但她却是编写OC应用程序应该熟悉的框架。CoreFoundation与Foundation不仅名字相似,而且还有更紧密的关系。有个功能叫做“无缝桥接”(toll-free bridging),可以把CoreFoundation中的C语言数据结构平滑转为Foundation中的OC对象,也可以反向转换。比方说Foundation框架中的字符串是NSString,而它可以转化为CoreFoundation里对应的CFString对象。
除了Foundation与CoreFoundation之外,还有很多系统库,其中包括但不限于下面列出的这些:
● CFNetwork 此框架提供了C语言级别的网络通信能力,它将“BSD套接字”抽象成易于使用的网络接口。如NSURLConnection
● CoreAudio 该框架所提供的C语言API可用来操作设备上的音频硬件。
● AVFoundation 此框架所提供的OC对象可用来回放并录制音视频,比如能在UI视图里播放视频。
● CoreData 此框架所提供的OC接口可将对象放入数据库
● CoreText 此框架提供的C语言接口可以高效执行文字排版及渲染操作。
可以看出OC编程一项重要特点,就是:经常需要使用底层C语言级API。用C语言来实现API的好处是,可以绕过OC的运行期系统,从而提升执行速度。当然由于ARC只负责OC对象,所以使用这些API时尤其需要注意内存管理问题。
UI框架是UIKit,它提供了Foundation与CoreFoundation之上的OC类。框架里含有UI元素,也含有粘合机制,令开发者可将所有相关内容组装为应用程序。在这些主要的UI框架下,是CoreAnimation与CoreGraphics框架。
CoreAnimation是OC语言写的,它提供了一些渲染图形并播放动画的工具。其又是QuartzCore一部分。
CoreGraphics 是C语言写的,其提供了2D渲染所必备的数据结构与函数。
还有很多框架建在UI框架之上,比如MapKit提供地图功能,Social框架提供社交网络功能。
第48条:多用块枚举,少用for循环
编程中经常需要遍历collection元素,做法有标准的for循环,OC1.0的NSEnumerator和OC2.0的fast NSEnumerator。语言引入“块”这一特性后,又多出来几种新的遍历方式。
for循环大家很熟悉,很简单。但是遍历字典和set的时候就稍麻烦,for循环有一个优势,可以反向遍历。在删除数组中一个元素的时候采用反向遍历
OC 1.0的NSEnumerator是个抽象基类,定义了两个方法,供具体的子类实现
-(NSArray*) allObjects;
-(id) nextObject;
举例,遍历数字
NSArray *anArray=/*....*/;
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while((object = [enumerator nextObject]) != nil){
//do something
}
快速遍历,就是for-in语句
直接上代码,这种办法比上面两种都高效,安全,简单,强烈推荐!在删除数组元素的时候,同样可以反序执行。
NSArray *anArray=/*...*/;
for (id object in anArray){
//do something
}
//dictionary
NSDictionary* dic = /*...*/;
for(id key in dic){
id value = dic[key];
//do something
}
//set
NSSet *aSet = /*...*/;
for (id object in aSet){
//do something
}
//反向遍历
NSArray *anArray=/*...*/;
for (id object in [anArray reverseObjectEnumerator]){
//do something
}
基于块的变量方式
当前OC中,最新引入的一种做法句ishi基于块来遍历。这种做法比前三种效率都高,但是代码量比for-in多。
NSArray *anArray=/*...*/;
[anArray enumerateObjectUsingBlock]:
^(id object,NSUInter idx,BOOL *stop){
// do something
if(shouldStop){
*stop = yes;
}
}];
其他collection类似
第49条:对自定义其内存管理语义的collection使用无缝桥接
上面提到过Foundation框架和CoreFoundation框架,Foundation中NSArray等collection,CoreFoundation中也有对应的CFArray,这两种创建数组的方式也许有区别,然而“无缝桥接”技术可以使得这两个类型之间平滑互转。下面代码演示了简单的无缝桥接:
NSArray *anNSArray = @[@1,@2,@3,@4,@5];
CFArrayRef aCFArray = (__bridge CFArrayRef)anNSArray;
NSLog(@"size of array =%li",CFArrayGetCount(aCFArray));
__bridge本身的意思是:ARC仍然具备这个OC对象的所有权。
第50条:构建缓存时选用NSCache而非NSDictionary
NSCache比NSDictonary好的地方是:当系统资源将要耗尽时,它可以自动删减缓存(删减“最近未使用的对象”)。下面这段代码演示了缓存的用法:
#import
//network fetcher class
typedef void(^EOCNetworkFetcherCompletionHandler)(NSData* data);
@interface EOCNetworkFetcher : NSObject
-(id)initWithURL(NSURL*)url;
-(void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandle)handler;
@end
// class that uses the network fetcher and caches results
@interface EOCClass :NSObject
@end
@implementation EOCClass{
NSCache* _cache;
}
-(id)init{
if(self = [super init]){
_cache = [NSCache new];
//cache a maximum of 100 URLs
_cache.countLimit = 100;
//the size in bytes of data is used as the cost,so this sets a cost limit of 5MB
_cache.totalCostLimit = 5*1024*1024;
}
return self;
}
-(void) downloadDataForUrl:(NSURL*)url{
NSData *cachedData = [_cache objectForKey:url];
if(cachedData){
//cached it
[self useData:cacheData];
}else{
//cache miss
EOCNetworkFetcher* fetcher = [[EOCNetworkFetcher alloc] initWithURL:url]
fetcher startWithCompletionHandler:^(NSData* data){
[_cache setObject:data forKey:url cost:data.length];
[self useData:data];
}];
}
}
@end
创建NSCache时,将其中可缓存的对象数目设定为100,将“总开销”上限设为5MB
第51条:精简initialize 与 load的实现代码
第52条:别忘了NSTimer会保留其目标对象
Foundation框架中有个类叫做NSTimer,开发者可以指定绝对的日期与时间,以便执行任务。
+(NSTimer) scheduledTimerWithTimeInterval: (NSTimerInterval)seconds target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats;
直接上代码吧
#import
@interface EOCClass : NSObject
-(void)startPolling;
-(void)stopPolling;
@end
@implementation EOCClass{
NSTimer* _pollTimer
}
-(id) init{
return [super init];
}
-(void) dealloc{
[_pollTimer invalidate];
}
-(void) stopPolling{
[_pollTimer invalidate]:
_pollTimer = nil;
}
-(void) startPolling{
_pollTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(p_doPoll) userInfo:nil repeats:YES];
}
-(void) p_doPoll{
//poll the resource;
}
@end
由于计时器的时候,目标对象是self,所以保留了self实例,由于计时器是用实例变量存放的,所以实例也保留了计时器。所以形成了循环引用。如果没有人调用invalidate那么这个循环引用一直存在。
如何写一个安全的NSTimer对象,解决办法就是:实现NSTimer的分类,并将self写成weak。所以,block中合适使用weak self就看是否有循环引用。例如:
#import
@interface NSTimer(EOCBlockSupport)
+(NSTimer*) eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end
@implementation NSTimer(EOCBlockSupport)
+(NSTimer*) eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{
return [self scheduledTimerWithTimerInterval:interval target:self selector:@selector(eoc_blockInvoke:) userInfo:[block copy] repeats:repeats];
}
+(void)eoc_blockInvoke:(NSTimer*)timer{
void(^block)() = timer.userInfo;
if(block){
block();
}
}
@end
//再修改一下startPolling接口
-(void) startPolling{
__weak EOCClass *weakSelf = self;
_pollTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:50 block:^
{ EOCClass *strOngSelf= weakSelf;
[strongSelf p_doPoll];
} repeats:yes];
}
这样就可以解决这个问题了.
(完)!