学习笔记:
使用C语言来实现API的
好处:
可以绕过Objective-C的运行期系统,从而提升执行效率。
缺点:
由于ARC只负责Objective-C的对象,所以使用这些API时尤其需要注意内存管理问题。
用纯C写成的框架与用Objective-C写成的一样重要,若想成为优秀的Objective-C开发者,应该掌握C语言的核心概念。
多用块枚举,少用for循环
遍历collection有四种方式。最基本的办法是for循环,其次是NSEnumerator遍历法及快速遍历法,最新,最先进的方式是“块枚举法”。
若提前知道待遍历的collection含有何种对象,则应修改块签名,指出对象的具体类型。
对自定义其内存管理语义的collection使用无缝桥接
转换操作中的__bridge告诉ARC如何处理转换所涉及的Objective-C对象。
__bridge本身的意思是:ARC仍然具备这个Objective-C对象的所有权。
NSArray *anArray = @[@1,@2];
CFArrayRef aCFArray = (__bridge CFArrayRef)anArray;
NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
而__bridge_retained则与之相反,意味着ARC将交出对象的所有权。若是前面那段代码改用它来实现,那么用完数组之后就要加上CFRelese(aCFArray)以释放其内存。
NSArray *anArray = @[@1,@2];
CFArrayRef aCFArray = (__bridge_retained CFArrayRef)anArray;
NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
CFRelease(aCFArray);
如果想把CFArrayRef转换为NSArray *,并且想令ARC获得对象的所有权,那么就可以采用此种转换方式。
NSArray *anArray = @[@1,@2];
CFArrayRef aCFArray = (__bridge CFArrayRef)anArray;
NSLog(@"Size of array = %li",CFArrayGetCount(aCFArray));
NSArray *anotherArray = (__bridge_transfer NSArray *)aCFArray;
这三种转换方式称为“桥式转换”。
为什么纯Objective-C来编写应用程序时,为什么要用到这种功能。
因为Foundation框架中的Objective-C类所具备的某些功能,是CoreFoundation框架中的C语言数据结构所不具备的,反之亦然。
在使用Foundation框架中的字典对象时会遇到一个大问题,那就是其键的内存管理语义为“拷贝”,而值得管理语义为“保留”。除非使用强大的无缝桥接技术,否则无法改变其语义。
#import "EOCClass.h"
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
//initializer element is not a compile-time constant
CFMutableDictionaryRef aCFDictionary = nil;
NSMutableDictionary *anDictionary = nil;
@implementation EOCClass
const void * EOCRetainCallBack(CFAllocatorRef allocator,const void * value){
return CFRetain(value);
}
void EOCReleaseCallBack(CFAllocatorRef allocator,const void * value){
CFRelease(value);
}
CFDictionaryKeyCallBacks keyCallbacks = {
0,
EOCRetainCallBack,
EOCReleaseCallBack,
NULL,
CFEqual,
CFHash
};
CFDictionaryValueCallBacks valueCallbacks = {
0,
EOCRetainCallBack,
EOCReleaseCallBack,
NULL,
CFEqual
};
+ (void)initialize{
aCFDictionary = CFDictionaryCreateMutable(NULL, 0, &keyCallbacks, & valueCallbacks);
anDictionary = (__bridge_transfer NSMutableDictionary*)aCFDictionary;
[anDictionary setValue:@"a" forKey:self];
//这里不会报错,po打印出anDictionary是
//{
// EOCClass = a;
//}
//此处修改内存管理语义,对键执行“保留”而非“拷贝”操作。
}
- (instancetype)init{
if (self = [super init]) {
self.eocDictionary = anDictionary;
[self.eocDictionary setValue:@"a" forKey:self];
//这里直接报错 [EOCClass copyWithZone:]: unrecognized selector sent to //instance 0x6080000015b0
//该错误表明,对象所属的类不支持NSCopying协议,因为“copyWithZone:”方法未实现。
}
return self;
}
在CoreFoundation层面创建collection时,可以指定许多回调函数,这些函数表示collection应如何处理其元素。然后,可运用无缝桥接技术,将其转换成具备特殊内存管理语义的Objective-C collection。
精简initialize与load的实现代码
initialize与load方法要保持精简,原因是:对于某个类来说,任何线程都可能成为初次用到它的线程,如果这个线程恰好是UI线程,那么初始化期间就会一直阻塞,导致应用程序无响应;减少进入“依赖环”的几率。
load方法:
对于加入运行期系统的每个类和分类,必定会调用此方法,而且仅调用一次。在iOS平台程序启动时候,就会执行此方法。如果分类和其所属的类都定义了load方法,则先调用类里,再调用分类。需要注意的是:load方法不遵从继承规则。,如果某个类本身没有实现load方法,那么不管其超类是否实现,系统都不会调用。
缺点:
一,load方法中使用其他类是不安全的。
#import "EOCClassB.h"
#import "EOCClassA.h"
@implementation EOCClassB
+ (void)load{
NSLog(@"Loading EOCClassB");
EOCClassA *eOCClassA = [EOCClassA new];
}
@end
调用NSLog没有问题,因为Foundation肯定在运行load之前就已经载入系统了。但是使用EOCClassA却不太安全,不能确定EOCClassA此时是否已经加载好了。
二,应用程序必须阻塞并等待所有类的load方法执行完,才能继续。
用途:
调试程序,比如可以在分类里编写此方法,用来判断该分类是否已经正确载入系统。
initialize方法
特点:
1,惰性调用,只有当程序用到了相关类,才会调用。
2,运行期系统确保initialize方法一定在线程安全的环境中执行。只有执行initialize哪个线程可以操作类或类实例。其他线程都要先阻塞,等待initialize执行完。
3,initialize和其他消息一样,如果某个类未实现它,而其超类实现了,就会运行超类实现代码。
作用:
只应该来设置内部数据,初始化全局状态,如果还有其他事情要做,可以专门创建一个方法来执行这些操作,并要求该类的使用者必须在使用本类之前调用此方法。
static const int KInterval = 10;
NSMutableArray *kSomeArray ;
@implementation EOCClassA
+ (void)initialize{
if (self == [EOCClassA class]) {
[EOCClassB doSomeThingB];
kSomeArray = [NSMutableArray array];
}
}
+ (void)doSomeThingA{
}
@end
整数在编译器可以定义,然而可变数组不行,因为它是个Objective-C对象,所以创建实例之前必须先激活运行期系统。注意某些Objective-C对象也可以在编译器创建,例如NSString实例。
网友评论