c语言中包括的基本数据类型(int,short,long,float,double等)都不是对象,OC提供了NSValue,NSNumber来封装C语言的基本类型。
定义NSInteger和NSUInteger
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
NSValue是NSNumber的父类,NSValue是一个通用的包装类,它可用于包装单个short、int、long、float、char、指针、对象id等数据项,然后可以把其加到NSArray、NSSet等集合(这些集合要求它们的元素必须是对象)中。
NSNumber的三类方法:
例如:
//调用类方法将int类型的值包装成NSNumber对象
NSNumber* num = [NSNumber numberWithInt:20];
//调用类方法将float类型的值包装成NSNumber对象
NSNumber *dou = [NSNumber numberWithDouble:3.4];
//调用类方法将char类型的值包装成NSNumber对象
NSNumber *c = [NSNumber numberWithChar:'J'];
NSLog(@"%d",[num intValue]);
NSLog(@"%g",[dou doubleValue]);
NSLog(@"%c",[c charValue]);
//先创建NSNumber对象,在调用initWithXxx方法执行初始化
NSNumber* ch = [[NSNumber alloc]initWithChar:'j'];
NSLog(@"%@",ch);
description方法是NSObject类的一个实例方法,所有的Objective-C类都是NSObject类的子类,它是一个“自我描述”方法,该方法通常用于事先这样一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。
例如:
打印会出现
//创建一个FKPerson的对象,将之赋给p变量
FKPerson* p = [[FKPerson alloc]initWithName:@"孙悟空"];
//打印p指向的FKPerson对象
NSLog(@"%@",[p description]);//
int it = 65;
float f1 = 65.0f;
NSLog(@"65和65.0f是否相等?%d",it == f1);//1
char ch = 'A';
NSLog(@"65和'A'是否相等?%d",it == ch);//1
NSString* str1 = [NSString stringWithFormat:@"hello"];
NSString* str2 = [NSString stringWithFormat:@"hello"];
NSLog(@"str1和str2是否相等?%d",str1 == str2);//0
NSLog(@"str1是否isEqual str2?%d",[str1 isEqual:str2]);//1
NSString* s1 = @"hello";
NSString* s2 = @"hello";
NSLog(@"%p--%p",s1,s2);
NSLog(@"s1和s2是否相等?%d",s1 == s2);
//让s3指向新生的对象
NSString* s3 = [NSString stringWithFormat:@"hello"];
NSLog(@"s3的地址:%p",s3);
NSLog(@"s1和s3是否相等?%d",s1 == s3);
注意:上述中的s1和s2是在常量池中取得字符串,故它们指向同一个元素,地址相同,内容也相同,所以相等。运用stringWithFormat:类方法创建的字符串对象是运行时创建出来的,它被保存在运行时内存区域(堆内存),所以他的指针变量保存的地址和s1指针变量保存的地址不同。
(2)、isEqual::是NSObject类提供的一个实例方法,要求两个指针变量指向同一个对象才会返回真,和==没有区别。但是需要注意的是:NSString重写了NSObject的isEqual:方法,NSString判断两个字符串的标准是:只要要个字符串所包含的字符序列仙童,通过isEqual:比较返回真;否则返回假。
正确重写isEqual:方法应该卯足下列条件:
⑤、对任何不适nil的x,[x isEqual:nil]一定返回假。
另外需要指出的是:NSObject默认提供的isEqual:只是比较对象的地址。
提示:NSString定义了一个isEqualToString:方法,专门用于判断当前字符串与另一个字符串的字符序列是否相等。
类簇:定义一个父类,并以该父类派生多个子类,其他程序使用这些类时,总是面向父类编程,当程序调用父类的初始化方法、静态方法来返回对象时,实际上返回的是子类的实例。
Objective-C的动态特征允许使用类别为现有的类添加新方法,并且不需要创建子类,不需要访问原有类的源代码。
类别同样接口和实现部分组成,接口部分的语法如下:
@interface已有类 (类别名)
//方法定义
。。。
@end
定义类别和定义类的区别:
例如:
为NSNumber增加一个类别,类别接口部分如下
#import
@interface NSNumber (FK)
//在类别中定义四个方法
- (NSNumber *)add:(double)num2;
- (NSNumber *)substract:(double)num2;
- (NSNumber *)multiply:(double)num2;
- (NSNumber *)divide:(double)num2;
@end
类别的实现部分
#import "NSNumber+FK.h"
//类别提供实现部分
@implementation NSNumber (FK)
- (NSNumber *)add:(double)num2{
return [NSNumber numberWithDouble:([self doubleValue] + num2)];
}
- (NSNumber *)substract:(double)num2{
return [NSNumber numberWithDouble:([self doubleValue] - num2)];
}
- (NSNumber *)multiply:(double)num2{
return [NSNumber numberWithDouble:([self doubleValue]*num2)];
}
- (NSNumber *)divide:(double)num2{
return [NSNumber numberWithDouble:([self doubleValue]/num2)];
}
@end
main函数的调用部分
#import "NSNumber+FK.h"
NSNumber* myNum = [NSNumber numberWithInt:3];
//测试add:方法
NSNumber* add = [myNum add:2.4];
NSLog(@"add = %@",add);
注意:
- ①、通过类别为指定类添加新方法之后,这个新方法不仅会影响NSNumber类,还会影响NSNumber类的所有子类,每个子类都会获取类别扩展的方法;
- ②、可根据需要一个类定义多个类别,不同的类别都可对原有的类增加方法定义;
用法
①、利用类别对类进行模块化设计;
②、利用类别来调用私有方法;
③、使用类别来实现非正式协议。
通过类别将类方法按照模块分到不同的.m文件中,从而提高项目后期的可维护性。
没有在接口部分定义而是在类实现部分定义的方法相当于是私有方法。
怎么调用那些类似的私有方法:
例如:
FKItem的接口口部分
#import
@interface FKItem : NSObject
@property (nonatomic ,assign)double price;
- (void)info;
@end
FKItem的实现部分
#import "FKItem.h"
//FKItem提供实现部分
@implementation FKItem
@synthesize price;
//实现接口部分定义的方法
- (void)info{
NSLog(@"这是一个普通的方法");
}
//额外新增的方法
- (double)calDiscount:(double)discount{
NSLog(@"我运行了");
return self.price*discount;
}
@end
FKItem添加类别
//为FKItem增加一个类别
@interface FKItem (FK)
//在类别的前面声明calDiscount:方法,告诉编译器,FKItem类中可以包含calDiscount方法
- (double)calDiscount:(double)discount;
@end
main函数的调用部分
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKItem* item = [[FKItem alloc]init];
item.price = 90;
[item info];
NSLog(@"物品嘉泽的价格为:%g",[item calDiscount:0.75]);
}
return 0;
}
扩展与类别类似,扩展相当于匿名类别,用于临时对某个类的接口进行扩展,类实现部分同时实现类接口部分定义的方法方法和扩展部分定义的方法,定义扩展的语法格式如下:
@interface 已有类()
{
实例变量
}
//定义方法。。。
@end
例如:
在FKCar中的扩展中接口部分
#import "FKCar.h"
@interface FKCar ()
{
NSString* name;
}
@property(nonatomic, copy) NSString* color;
- (void)drive:(NSString *)owner;
@end
在FKCar的接口部分
#import
@interface FKCar : NSObject
@property (nonatomic ,copy)NSString* brand;
@property (nonatomic ,copy)NSString* model;
- (void)drive;
@end
在FKCar的实现部分
#import "FKCar.h"
#import "FKCar_drive.h"
@implementation FKCar
@synthesize brand;
@synthesize model;
@synthesize color;
- (void)drive{
NSLog(@"%@汽车正在路上奔跑",self);
}
- (void)drive:(NSString *)owner{
NSLog(@"%@正驾驶%@汽车在路上奔跑",owner,self);
}
- (NSString *)description{
return [NSString stringWithFormat:@"%@---%@---%@",self.brand,self.model,name];
}
@end
main函数的调用实现部分
#import "FKCar_drive.h"
#import "FKCar.h"
FKCar* car = [[FKCar alloc]init];
car.brand = @"宝马";
car.model = @"X5";
car.color = @"黑色";
[car drive];
[car drive:@"孙悟空"];
NSLog(@"%@",[car description]);
协议定义了一种规范,协议定义某一批类多需要遵守的规范,它不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规范这批类中必须提供某些犯法,协议不提供任何实现,协议体现的是规范和实现分离的设计哲学。
例如:
为NSObject的Eatable类别中定义一个taste:方法
#import
//以NSObject为基础定义Eatable类别
@interface NSObject (Eatable)
- (void)taste;
@end
提示:所有继承NSObject类的子类都会自动带有该方法,而且NSObject的子类可以根据需要,自行决定是否要实现该方法。
FKApple的接口部分
#import
#import "NSObject+Eatable.h"
//定义类的接口部分
@interface FKApple : NSObject
@end
FKApple的实现部分
#import "FKApple.h"
//为FKApple提供实现部分
@implementation FKApple
- (void)taste{
NSLog(@"苹果营养丰富,口味很好!");
}
@end
main函数的调用部分实现
FKApple* apple = [[FKApple alloc]init];
[apple taste];//苹果营养丰富,口味很好!
注意:对于实现非正式协议的类而言,OC并不强制实现该协议中的所有方法,也就是说,上面的FKApple类也可以不实现taste方法,但是FKApple如果不实现该协议中的方法,当调用的时候会出现unrecognized selector的错误。
定义正式协议的基本语法如下:
@protocol 协议名<父协议1,父协议2>
{
零个到多个方法定义
}
语法的详细说明:
例如:
定义一个FKOutput的协议
#import
@protocol FKOutput <NSObject>
//定义协议中的方法
- (void)output;
- (void)addData(String msg);
@end
定义一个FKProductable 的协议
#import
@protocol FKProductable <NSObject>
//定义协议的方法
- (NSDate *)getProduceTime;
@end
定义一个FKPrintable 的协议,父协议为FKOutput和FKProductable
#import
#import "FKOutput"
#import "FKProductable"
@protocol FKPrintable <NSObject,FKOutput,FKProductable>
//定义协议中的方法
- (NSString *)printColor;
@end
在类定义的接口部分可指定该类继承的父类,以及遵守的协议,语法如下:
@interface 类名 : 父类 <协议1,协议2...>
例如:
定义类FKPrinter遵守FKPrintable协议
#import
#import "FKPrintable.h"
//定义类的接口部分,继承NSObject,遵守FKPrintable协议
@interface FKPrinter : NSObject<FKPrintable>
@end
FKPrinter实现协议的部分
#import "FKPrinter.h"
#define MAX_CACHE_LINE 10
@implementation FKPrinter
{
NSString* printData[MAX_CACHE_LINE];//使用数组记录所有需要缓存的打印数据
int dataNum;//打印当前需打印的作业数
}
- (void)output{
//只要还有作业,继续打印
while (dataNum > 0) {
NSLog(@"%@%@",self.printColor,printData[0]);
//将剩下的作业数减1
dataNum--;
//把作业对列整数前移一位
for (int i = 0; i < dataNum; i ++) {
printData[i] = printData[i + 1];
}
}
}
- (void)addData:(NSString *)msg{
if (dataNum >= MAX_CACHE_LINE) {
NSLog(@"输出对列已满,添加失败");
}else{
//把打印数据添加到对列中,已保存数据的数量加1
printData[dataNum++] = msg;
}
}
- (NSDate *)getProduceTime{
return [[NSDate alloc]init];
}
- (NSString *)printColor{
return @"红色";
}
@end
main函数的调用实现部分
FKPrinter* printer = [[FKPrinter alloc]init];
[printer addData:@"疯狂iOS讲义"];
[printer addData:@"疯狂XML讲义"];
[printer output];
//创建一个FKPrinter对象,当成FKProductable使用
NSObject* p = [[FKPrinter alloc]init];
//调用FKProductable协议中定义的方法
NSLog(@"%@",[p getProduceTime]);
//创建一个FKPrinter对象,当成FKOutput使用
id myOut = [[FKPrinter alloc]init];
//调用FKOutput协议中定义的方法
[myOut addData:@"孙悟空"];
[myOut addData:@"猪八戒"];
[myOut output];
如果程序需要使用协议来定义变量,有如下两种语法:
①、NSObject<协议1,协议2...>* 变量;
②、id<协议1,协议2...> 变量;
正式协议和非正式协议之间的区别:
协议的关键字:@optional、@required两个关键字
协议体现的是一种规范,定义协议的类可以吧协议定义的方法委托给实现协议的类,这样可以让类定义具有更好地通用性质,因为具体的动作交给协议的实现类去完成。
定义协议:
@protocol SecondViewControllerDelegate <NSObject]] >
- (void)addArray:(NSArray *)array;
@end
设置代理:
@property (nonatomic ,strong)iddelegate;
遵守协议
navc.controller = self;
实现代理协议
[self.delegate addArray:self.selectArray];
指定协议
#import
@protocol FKEatable <NSObject>
@optional
- (void)taste;
@end
遵守协议
#import
#import "FKEatable.h"
//定义类的接口部分
@interface FKApple : NSObject<FKEatable]] >
@end
实现协议
FKApple* apple = [[FKApple alloc]init];
[apple taste];
当设置协议的时候,制定了方法,但是遵守协议的类没有实现该方法,当要求自己的代理去完成方法时就会出现系统崩溃,为了避免程序引发的异常,程序可使用OC提供的异常机制进行处理。
开发者可以将可能引发异常的代码放在@try后的代码内,当程序引发异常时,该异常可以使用catch来捕捉,进行处理,最后使用@finally块回收资源。下面是OC异常处理机制的语法:
@try{
//业务实现代码...
}
@catch(异常1 ex){
//异常处理代码...
}
@catch(异常2 ex){
//异常处理代码...
}
//可能很多@catch块
@finally{
}
例如:
FKApple* apple = [[FKApple alloc]init];
@try {
[apple taste];
}
@catch (NSException *exception) {
NSLog(@"==捕获异常==");
NSLog(@"捕获异常:%@,%@",exception.name,exception.reason);//捕获异常:NSInvalidArgumentException,-[FKApple taste]: unrecognized selector sent to instance 0x1002029b0
}
@finally {
NSLog(@"资源回收!");//此处可进行资源回收等操作
}
NSLog(@"程序执行完成");
提示:NSException类是OC所有异类的根类,其他异类都应该通过该类进行派生。
所有的异常对象包含的常用方法:
不管@try块中的代码是否出现异常,也不管哪一个@catch块被执行,甚至在@try块或@catch块中执行了return语句,@finally都会执行。
注意:通常情况下,不要在@finally块中使用如return或@throw等导致方法终止的语句,一旦在@finally块中使用了return或@throw语句,将会导致@try块以及@catch块中的return、@throw失效。
例如:
BOOL test(){
@try {
return YES;
}
@finally {
return NO;
}
}
然后调用时输出:
BOOL a = test();
NSLog(@"A = %d",a);//输出代表NO的0;
当程序执行@try块、@catch块时遇到了return语句或@catch语句,这两个语句都会导致该方法立即结束,但是系统执行这两个语句并不会结束该方法,而是去寻找该异常处理流程中是否包含@finally块,如果没有@finally块,程序立即执行return语句或@throw语句,方法终止。如果有@finally块,系统立即开始执行@finally块,只有当@finally块执行完成后,系统才会再次跳回来执行@try块、@catch块里的return或@throw语句,如果@finally块里也使用了return或@throw等导致方法终止的语句,则@finally块已经终止了方法系统将不会跳回去执行@try块,@catch块里的任何语句。
在某些时候,某些数据于业务规则不相匹配,系统无法抛出这种异常,如果需要在程序中自行抛出异常,应使用@throw语句,@throw语句可以单独使用,@throw语句抛出的不适异常类,而是一个异常实例,而且每次只能抛出一个异常实例。语法格式如下:
@throw ExceptionInstance
用户自定义的异常都应该继承NSException基类,
例如:
自定义FKMyException
#import
@interface FKMyException : NSException
@end
定义FKDog类接口部分
#import
@interface FKDog : NSObject
@property (nonatomic ,assign)int age;
@end
定义FKDog类实现部分
#import "FKDog.h"
#import "FKMyException.h"
//为FKDog提供实现部分
@implementation FKDog
@synthesize age = _age;
- (void)setAge:(int)age{
if (self.age != age) {
//检查年龄是否在0~50之间
if (age > 50 || age < 0) {
//手动抛出异常
@throw [[FKMyException alloc]initWithName:@"IllegalArgumentException" reason:@"狗的年龄必须在0~50之间" userInfo:nil];
}
_age = age;
}
}
@end
OC提供了三种编程方式与运行环境交互
(2)、通过NSObject类中定义的代码进行动态编程;
例如:
isKindOfClass:、isMemberOfClass:方法用于判断该类所属的类;
respondsToSelector:用于判断该实例是够可调用指定的方法;
conformsToProtocol:可用于判断该对象是否遵守指定协议;
methodForSelector:可用于返回指定方法实现的指针。
每个类都有一个对应的Class,OC程序中获得Class通常有三种方式
NSLog(@"%@",NSClassFromString(@"FKDog"));//FKDog
NSLog(@"%@",[FKDog class]);//FKDog
NSLog(@"%@",[dog class]);//FKDog
相比之下第二种方法有两种优势:
(1)、代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
(2)、程序性能更高,因为这种方式无须调用方法所以性能更好。
//通过字符串来获取Class
Class clazz = NSClassFromString(@"NSDate");
NSLog(@"%@",clazz);//NSDate
//直接使用Class来创建对象
NSDate* date = [[NSDate alloc]init];
NSLog(@"%@",date);//2014-11-15 08:17:38 +0000
//通过对象来获取Class
NSLog(@"%@",[date class]);//__NSDate
//通过类来获取Class
NSLog(@"%@",[NSDate class]);//NSDate
NSLog(@"%d",clazz == NSDate.class);//1
提示:通过字符串,类名获取的Class对象是相等的,但是通过NSDate对象获取的Class则是__NSDate,原因是:OC的很多设计都采用类簇的设计,NSDate只是这个类簇的前端,当程序调用[[NSDate alloc]init];创建对象时,程序返回的只是NSDate的子类(__NSDate)的实例,而不是NSDate的实例。因此,程序直接调用date对象的class方法来获取Class时返回__NSDate。
如果程序只是需要确定一个类的继承关系,比如,判断它是否为某个类的实例或是否为某个类及其子类的实例,可以调用NSObject提供的如下几个方法进行判断:
第三种方法需要传入一个Protocol参数,为了在程序中获取Protocol,可通过如下两种方式来完成:
(1)、OC提供的@Protocol指令来实现;
(2)、可调用Protocol* NSProtocolFromString(NSString *namestr);方法根据协议名字符串获取对应的协议。
例如:
FKApple* apple = [[FKApple alloc]init];
//通过对象来判断该对象的Class
NSLog(@"%@",[apple class]);
//判断对象是否为某个类的实例
NSLog(@"apple是否为FKApple的实例:%d",[apple isMemberOfClass:[FKApple class]]);//1
NSLog(@"apple是否为NSObject的实例:%d",[apple isMemberOfClass:[NSObject class]]);//0
//判断对象是否为某个类及其子类的实例
NSLog(@"apple是否为FKApple及其子类的实例:%d",[apple isKindOfClass:[FKApple class]]);//1
NSLog(@"apple是否为NSObject及其子类的实例:%d",[apple isKindOfClass:[FKApple class]]);//1
//判断对象是否实现了指定协议
NSLog(@"apple是否实现FKEatable协议:%d",[apple conformsToProtocol:@protocol(FKEatable)]);//1
如果程序需要动态调用对象的setter、getter方法,可通过OC提供的KVC机制来实现;如果程序需要访问对象的实例变量的值,不管这个实例变量是否在类的接口部分定义,也不管该变量使用哪种访问控制符修饰,或者是否在类的实例部分定义,程序都可通过KVC机制来设置、访问实例变量的值。
如果程序需要判读某个对象是否可调用方法,可通过NSObject的如下方法进行判断:
respondsToSelector:该方法需要传入一个SEL参数,也就是说没需要传入一个SEL对象作为参数,为了在程序中动态获取SEL对象,OC提供如下方法:
如果程序需要动态的调用对象的普通方法,可通过如下两种方式来实现:
注意:为了在程序中使用objc_msgSend()函数,应该使用#import显示导入
头文件。除此之外,objc_msgSend()还有两个版本,即objc_msgSend_fpret()、objc_msgSend_stret(),点那个目标方法返回浮点数时使用objc_msgSend_fpret()函数;当目标方法返回结构体数据时,使用objc_msgSend_stret()函数。//貌似不能用了
对于有效的内存管理,通常认为包括两方面的内容
(2)、内存回收:当程序不在需要对象时,系统必须及时回收这些对象所占用的内存,以便程序可以再次使用这些内存。
典型的内存回收策略有两种:
总结起来:OC程序员可用的内存回收机制有如下几种:
正常情况下,当一段代码需要访问某个对象,该对象的引用计数加1;当这段代码不在访问该对象时,该对象的引用计数减1,白哦是这段代码不在访问该对象。当对象的引用计数为0时,表明程序已经不在需要该对象,系统就会回收该对象所占用的内存。系统在销毁对象之前,会自动调用该对象的dealloc方法来执行一些回收操作。
手动引用计数回收内存的标准是:当一个对象的引用计数为0时,就表明程序不在需要该对象,从而通知系统回收该对象所占用的内存。
在手动引用计数中,改变对象的引用计数的方式如下:
NSObject中提供了有关引用计数的如下方法:
例如:在FKItem的实现部分重写dealloc方法
#import "FKItem.h"
//FKItem提供实现部分
@implementation FKItem
//实现接口部分定义的方法
- (id)init{
if (self = [super init]) {
NSLog(@"init方法中,引用计数为:%ld",self.retainCount);
}
return self;
}
- (void)dealloc{
NSLog(@"系统开始销毁我了,再见了");
[super dealloc];
}
@end
main函数中调用方法实现
//调用new方法创建对象,该对象的引用计数为1;
FKItem* item = [FKItem new];
NSLog(@"%ld",item.retainCount);//1
[item retain];//引用计数加1,retainCount为2
NSLog(@"%ld",item.retainCount);//2
[item retain];//引用计数加1,retainCount为3
NSLog(@"%ld",item.retainCount);//3
[item release];//引用计数减1,retainCount为2
NSLog(@"%ld",item.retainCount);//2
[item release];//引用计数减1,retainCount为1
NSLog(@"%ld",item.retainCount);//1
[item release];//引用计数减1,retainCount为0
//系统会自动调用该对象的dealloc方法来销毁它
//后面代码不要调用item指针的方法,调用item方法看到得都是假象,甚至可能导致崩溃。
例如:在FKUser的借口部分持有FKItem的对象
#import
#import "FKItem.h"
@interface FKUser : NSObject
{
FKItem* _item;
}
- (void)setItem:(FKItem *)item;
- (FKItem *)item;
@end
在FKUser的实现部分,对item就行持有
#import "FKUser.h"
@implementation FKUser
- (void)setItem:(FKItem *)item{
if (_item != item) {
//让item的引用计数加1
[item retain];
_item = item;
}
}
- (FKItem *)item{
return _item;
}
- (void)dealloc{
//让_item的引用计数减1
NSLog(@"%ld",_item.retainCount);
[_item release];
NSLog(@"系统开始销毁FKUser对象");
[super dealloc];
}
@end
在main函数中调用
//调用new方法创建对象,该对象的引用计数为1;
FKItem* item = [FKItem new];
NSLog(@"创建出来的应用计数为:%ld",item.retainCount);//1
FKUser* user = [[FKUser alloc]init];
[user setItem:item];
NSLog(@"被FKUser持有后的引用计数为:%ld",item.retainCount);//2
//main方法将item的引用计数减1,item的引用计数为1;
[item release];
NSLog(@"调用release之后的引用计数为:%ld",item.retainCount);//1
//user的引用计数减1,user的引用计数为0
//系统调用user的dealloc方法,调用dealloc方法时将会让FKItem的计数减1
//这样item的引用计数为0,系统就会执行item的dealloc方法进行销毁
[user release];
上述中还有问题,应该改成:
在FKUser的实现部分
- (void)setItem:(FKItem *)item{
if (_item != item) {
//让item的引用计数加1
[_item release];
_item = [item retain];
}
}
然后在main函数调用实现部分
//调用new方法创建对象,该对象的引用计数为1;
FKItem* item = [FKItem new];
NSLog(@"创建出来item的引用计数为:%ld",item.retainCount);//1
FKUser* user = [[FKUser alloc]init];
[user setItem:item];
NSLog(@"被FKUser持有后的引用计数为:%ld",item.retainCount);//2
//重新创建一个对象item2
FKItem* item2 = [[FKItem alloc]init];
//将item2作为参数赋传给setItem:方法
[user setItem:item2];
NSLog(@"重新赋值后item的引用计数为:%ld",item.retainCount);//1
NSLog(@"重新赋值后item2的引用计数为:%ld",item2.retainCount);//2
//main方法将item的引用计数减1,item的引用计数为0;
//这样item的引用计数为0,系统就会执行item的dealloc方法进行销毁
[item release];
//main方法将item2的引用计数减1,item的引用计数为1;
[item2 release];
NSLog(@"调用release之后的引用计数为:%ld",item2.retainCount);//1
//user的引用计数减1,user的引用计数为0
//系统调用user的dealloc方法,调用dealloc方法时将会让FKItem的计数减1
//这样item2的引用计数为0,系统就会执行item2的dealloc方法进行销毁
[user release];
为了保证函数、方法返回的对象不会再被返回之前就被销毁,我们需要保证被函数、方法返回的对象能被延迟销毁,为了实现这种延迟销毁,有如下两种方法:
(2)、使用自动释放池进行延迟销毁。
- (id)autorelease:该方法不会修改对象的引用计数,只是将该对象添加到自动释放池中,该方法将会返回调用该方法的对象本身。由于autorelease方法预设了一条在将来某个时间将要调用release方法,该方法会把对象的引用计数减1,当程序在自动释放池上下文中调用某个对象的autorelease方法时,该方法只是将该对象添加到自动释放池中,当该自动释放池释放时,自动释放池会让池中的所有的对象执行release方法。
创建和销毁自动释放池的代码模块如下:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
[pool release];
//解释:当程序创建NSAutoreleasePool对象之后,该对象的引用计数为1,接下来当程序调用该对象的release方法后,该对象的引用计数变为0,系统准备调用NSAutoreleasePool的dealloc方法来回收该对象,系统会在调用NSAutoreleasePool的dealloc方法时回收该池中的所有的对象。
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
FKItem* item = [[FKItem alloc]init];
//接下来可以调用item的方法
NSLog(@"%ld",item.retainCount);//1
//创建一个FKUser对象 并将它添加到自动释放池中
FKUser* user = [[[FKUser alloc]init]autorelease];
//接下来可以调用user方法
NSLog(@"%ld",user.retainCount);//1
//系统将因为池的引用计数为0而回收自动释放池
//回收自动释放池时会调用池中所有对象的release方法
[pool release];//释放之后user的计数为0
NSMutableArray* array = [NSMutableArray arrayWithCapacity:20];
事件循环步骤:
如果程序需要在事件处理结束后依然可以保留临时对象,可采用如下几种方式:
注意:assign、week、unsafe_unretain指示符的属性除外。
第一种:
[array retain];
第二种:
在MyView.h中的代码如下
#import
@interface MyView : UIView
@property(nonatomic ,retain)NSMutableArray* data;
@end
在MyView.m中的代码如下:
- (void)setData:(NSMutableArray *)data{
if (_data == data) {
[_data release];
_data = [data retain];
}
}
- (void)dealloc{
[_data release];
[super dealloc];
}
打开ARC
打开关闭某个文件的ARC:
(1)、 打开:-fobjc-arc表明该程序使用了ARC机制,程序员不允许在程序中调用对象的retain、release方法改变对象的引用计数。
(2)、禁用:-fno-objc-arc表明关闭了某个文件的ARC机制。
mian函数中的@autoreleasePool{}块,定义自动释放池上下文,任何在该上下文创建的对象昂都有ARC来自动释放,并在自动释放块结束时销毁这些对象。
注意:如果程序在执行过程中创建了大量的临时对象(比如在循环中创建对象,这是一种非常差的做法),程序可能需要多次释放这些临时对象,此时程序可以考虑将@autoreleasePool块放在循环体中。
例如:
for(int i = 0;i < 100;i ++){
@autoreleasepool {
//创建临时对象
//调用临时对象的费方法
//...
}
}