若是不了解系统架构所提供的内容,那么就可能会把其中已经实现的东西再写一遍。将一系列代码封装为动态库(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框架提供社交网络功能。
编程中经常需要遍历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类似
上面提到过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对象的所有权。
NSCache比NSDictonary好的地方是:当系统资源将要耗尽时,它可以自动删减缓存(删减“最近未使用的对象”)。下面这段代码演示了缓存的用法:
#import <Foundation/Foundation.h> //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
Foundation框架中有个类叫做NSTimer,开发者可以指定绝对的日期与时间,以便执行任务。
+(NSTimer) scheduledTimerWithTimeInterval: (NSTimerInterval)seconds target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats;
直接上代码吧
#import <Foundation/Foundation.h> @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 <Foundation/Foundation.h> @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]; }
这样就可以解决这个问题了.
(完)!