Effective Objective-C(第47-52条)系统架构,foundation、for-in、NSTimer

第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 <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

创建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 <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那么这个循环引用一直存在。

Effective Objective-C(第47-52条)系统架构,foundation、for-in、NSTimer_第1张图片

    如何写一个安全的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];
}

这样就可以解决这个问题了.

(完)!

你可能感兴趣的:(系统架构,NSTimer)