黑马程序员--Objective-C语言基础知识--构造方法和内存管理

来源:互联网 发布:n网络优化工程师 编辑:程序博客网 时间:2024/05/18 01:15

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

构造方法

构造方法是用来初始化对象的方法,它是个对象方法,以“-”号开头。完整的创建一个可用的对象首先分配存储空间给对象此时调用类方法+alloc,之后再调用构造方法-init进行初始化。
完整的创建一个可用对象的写法:

Person *p = [[Person alloc] init];

默认情况下构造方法初始化成员变量的值为0,如果要让对象创建出来成员变量就会有特定的值就要重写构造方法。
重写构造方法要先调用父类的构造方法:[super init],再进行子类内部成员变量的初始化。这里的super关键字指当前调用者的父类或者父类对象,self关键字指当前的调用者。
示例1:

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@property NSString *name;@end@implementation Person- (Person *)initWithAge:(int)age andName:(NSString *)name/*重写构造方法设置两个参数传给对象相应的成员变量,这样我们就能按照自己的想法初始化对象,当然也可以不设置参数直接在方法内部设置成员变量的值使得每一个对象创建出来都有一样的初值*/{    if (self = [super init])/*先将父类对象初始化,继承自父类的成员变量交给父类初始化,这里没有重写父类的构造方法,父类初始化之后赋给当前的对象完成初步的初始化*/    {        _age = age;//子类初始化自己的成员变量,将参数的值赋给成员变量        _name = name;    }    return self;//初始化完成,将初始化后的对象(当前调用者自己)返回给调用者}- (NSString *)description{    //为了体现出我们初始化成功后对象的样子,重写description方法输出对象的属性值    return [NSString stringWithFormat:@"当前对象的属性是:年龄%d,姓名%@", self.age, self.name];}@endint main(){    Person *per = [[Person alloc] initWithAge:36 andName:@"LiYi"];//初始化对象    NSLog(@"%@", per);//输出初始化之后的对象    return 0;}

示例1的运行结果是:

2015-07-19 20:56:36.486 a.out[1715:513018] 当前对象的属性是:年龄36,姓名LiYi

内存管理的基本原理

移动设备的内存极其有限,每个app所能占用的内存是有限制的。当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等等。内存管理就是管理这些存放在堆中的oc对象。
一、
什么是引用计数器:
每个oc对象都有自己的引用计数器,它是一个整数,表示这个对象被引用的次数,既有多少“人”正在使用这个oc对象。oc对象内部专门有4个字节的存储空间来存储引用计数器。
二、
引用计数器的作用:
1. 当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1;
2. 当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程中,它占用的内存就不可能被回收,除非整个程序已经退出。
三、
引用计数器的操作:
1. 给对象发送一条retain消息,可以使引用计数器+1(retain方法返回对象本身);
2. 给对象发送一条release消息,可以使引用计数器值-1;
3. 给对象发送retainCount消息获得当前的引用计数器值。
四、
对象的销毁:
1. 当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收;
2. 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息;
3. 一般会重写dealloc方法,在这里面释放相关资源(该对象引用的其它对象的引用计数器减1)。重写了dealloc方法,就得在最后调用[super dealloc]。dealloc方法通常不会直接调用。
4. 对象被回收之后它所占用的内存就不可再用,坚持使用会导致程序崩溃。
四、
内存管理当中还有几个概念。
1. 僵尸对象:所占用内存已经被回收的对象,僵尸对象不能再使用;
2. 野指针:指向僵尸对象的指针,给野指针发送消息会报错;
3. 空指针:没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错。
示例2:

#import <Foundation/Foundation.h>@interface Person : NSObject@property int age;@property NSString *name;- (void)run;@end@implementation Person- (Person *)initWithAge:(int)age andName:(NSString *)name{    if (self = [super init])    {        _age = age;        _name = name;    }    return self;}- (NSString *)description{    return [NSString stringWithFormat:@"当前对象的属性是:年龄%d,姓名%@", self.age, self.name];}- (void)run{    NSLog(@"跑起来");}- (void)dealloc//重写dealloc方法{    NSLog(@"对象被回收");    [super dealloc];}@endint main(){    Person *per1 = [[Person alloc] initWithAge:36 andName:@"LiYi"];//创建并初始化一个对象,这时引用计数器默认是1    int num1 = [per1 retainCount];//获得此时引用计数器的值    id obj = per1;//定义一个万能指针指向我们创建的对象    [per1 retain];//有新的指针指向对象,引用计数器加1    int num2 = [per1 retainCount];//获得此时引用计数器的值    [per1 release];//release操作放在最后面,我们不用这个对象的时候就要将它回收,这一次的release对应alloc    [per1 release];//这次的release对应retain    per1 = nil;//对象已经被回收,那么指针再指向那块内存就变成野指针了,所以让指针清0    [per1 run];//给空指针发送一条消息是不会报错的,如果指针没有清0发送消息会报野指针错误    return 0;}

示例2展示了如何人工进行内存管理,其实oc语言的内存管理就是回收不再使用的对象。当对象被创建出来了(alloc、new、copy)那么最后要release一次,中间如果有别的指针指向它要retain一次,retain了一次之后最后要不忘在加一次release操作。
示例2的运行结果是:

2015-07-19 22:01:06.161 a.out[1797:548763] num1 = 1,num2 = 22015-07-19 22:01:06.162 a.out[1797:548763] 对象被回收

五、
内存管理原则:
1. 你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作);
2. 如果不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release操作);

set方法的内存管理

如果某个对象有个oc对象类型的成员变量,就必须管理这个成员变量的内存。

- (void)setBook:(Book *)book{        if(book != _book)        {           [_book release];           _book = [book retain];        }}

以上是在set方法中管理成员变量的规范代码。首先我们判断放进来的对象是不是原有的旧值,如果是旧值就不用再做引用计数器的操作了,如果不是旧值则我们先对以前的旧制release一次因为我们的对象已经不用它了它被少引用一次,然后对新值进行一次retain操作因为我们的对象引用了它一次。

- (void)dealloc{   [_book release];   [super dealloc];}

以上是在dealloc方法里面管理成员变量内存的代码规范。当我们的对象不用被销毁时,那么它之前所引用的成员变量对象也会少一个引用者故此时这个成员变量对象要进行一次release操作。
示例3:

#import <Foundation/Foundation.h>@interface Book : NSObject@end@implementation Book@end@interface Person : NSObject{    Book *_book;}@property int age;@property NSString *name;- (void)setBook:(Book *)book;- (Book *)book;@end@implementation Person- (void)setBook:(Book *)book{    if (_book != book)    {        [_book release];        _book = [book retain];    }}- (Book *)book{    return _book;}- (void)dealloc{    [_book release];    [super dealloc];}@endint main(){    Person *per = [[Person alloc] init];    Book *book = [[Book alloc] init];    per.book = book;    [per release];    [book release];    return 0;}

示例3就是一个管理成员变量对象内存的实例。

@property参数

我们知道利用@property可以直接生成set和get方法,通过在@property后面添加一些参数可以控制set和get方法。
多线程管理:
atomic:这个关键字意味着性能较低,默认情况下就是它;
nonatomic:这个关键字性能较高,我们通常使用这个参数。
控制set方法的内存管理:
retain:在set方法中自动生成管理成员变量内存的代码,release旧值,retain新值。这个参数用于oc对象成员变量;
assign:直接赋值,不做任何内存管理,默认情况下就是它,用于非oc对象的管理。
控制需不需要生成set方法:
readwrite:同时生成set方法和get方法,默认就是它;
readonly:只会生成get方法。
控制set方法和get方法的名称:
setter:设置set方法的名称,一定要有个冒号“:”;
getter:设置get方法的名称。
示例4:

#import <Foundation/Foundation.h>@interface Book : NSObject@end@implementation Book@end@interface Person : NSObject@property(nonatomic, readonly)int age;@property(nonatomic, retain)NSString *name;@property(nonatomic, retain)Book *book;@property(getter = isRich)BOOL rich;@end@implementation Person- (void)dealloc{    [_book release];    [super dealloc];}@endint main(){    Person *per = [[Person alloc] init];    Book *book = [[Book alloc] init];    per.book = book;    per.rich = YES;    BOOL is = per.isRich;    NSLog(@"%d", is);    [per release];    [book release];    return 0;}

示例4利用@property的各种参数对成员变量的set和get方法进行了各种操作,我们给其中一个成员变量的get方法改了名。示例4的运行结果是:2015-07-20 09:50:23.655 a.out[441:22302] 1

循环引用

循环引用就是“我”引用“你”,“你”引用“我”。两个类互相引用对方做自己的成员变量。

1.#import "B.h"@interface A : NSObject{    B *b;}@end2.#import "A.h"@interface B : NSObject{    A *a;}@end

以上就是两个类互相引用的情况,这样编译器会报错。
如果我们在.h文件中使用@class 类名; 就可以引用一个类。它的作用是说明跟在它后面的标识符是个类。在.m文件中当我们真的用到这个类的时候再把这个类的声明导入进来。

@class B;@interface A : NSObject{    B *b;}@end

这样写就可以避免重复拷贝的错误。
按照我们之前所学在自动生成的set方法中会release旧值retain新值,但是如果两个对象互相引用对方做自己的成员变量就会导致对象的内存该释放时释放不掉。
这种情况下我们让一端用retain,另一端用assign(直接赋值不retain一次新值)。这样两个对象的内存就都可以释放掉了。
示例5:

#import <Foundation/Foundation.h>@class B;@interface A : NSObject@property(nonatomic,retain)B *b;@end@implementation A- (void)dealloc{    [_b release];    [super dealloc];    NSLog(@"A被释放");}@end@interface B : NSObject@property(nonatomic, assign)A *a;@end@implementation B- (void)dealloc{    [super dealloc];    NSLog(@"B被释放");}@endint main(){    A *a = [[A alloc] init];    B *b = [[B alloc] init];    a.b = b;    b.a = a;    [a release];    [b release];    return 0;}

示例5就是循环引用的一个示例,它的运行结果是:

2015-07-20 10:26:52.559 a.out[496:40026] A被释放2015-07-20 10:26:52.561 a.out[496:40026] B被释放

autorelease自动释放池

autorelease自动释放池会对放入里面的对象内存进行管理。
它的基本用法是:
1. 给某对象发送一条autorelease消息,那么这个对象就会加到一个自动释放池中;
2. 当自动释放池销毁时,会给池子里面的所有对象发送一条release消息;
3. 调用autorelease方法时并不会改变对象的计数器,会返回对象本身;
4. autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用release一次。

    @autoreleasepool    {        Person *p = [[[Person alloc] init] autorelease];    }

以上就是自动释放池的创建以及如何把对象放入自动释放池中。
示例6:

#import <Foundation/Foundation.h>@class B;@interface A : NSObject@property(nonatomic,retain)B *b;@end@implementation A- (void)dealloc{    [_b release];    [super dealloc];    NSLog(@"A被释放");}@end@interface B : NSObject@property(nonatomic, assign)A *a;@end@implementation B- (void)dealloc{    [super dealloc];    NSLog(@"B被释放");}@endint main(){    @autoreleasepool {        A *a = [[[A alloc] init] autorelease];        B *b = [[[B alloc] init] autorelease];        a.b = b;        b.a = a;    }//自动释放池销毁    return 0;}

示例6的运行结果是:

2015-07-20 10:44:13.178 a.out[522:51336] B被释放2015-07-20 10:44:13.179 a.out[522:51336] A被释放

我们看到后进入池子的b先被释放,这说明先对b做了一次release操作。放入自动释放池中的对象先进后release,后进先release。

ARC机制

ARC(Automatic Reference Counting)是iOS 5之后增加的新特性,完全消除了手动管理内存的繁琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切。
ARC是编译器特性,不是运行时特性,它也不是类似于其它语言中的垃圾收集器,ARC和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化。
ARC的规则:只要有一个强指针指向对象,对象就会保存在内存中。
strong:给@property添加参数strong说明这个成员变量对象被强指针指,相当于之前所学的retain,默认情况下对象是被强指针指的;
weak:给@property添加参数weak说明这个成员变量对象被弱指针指,相当于我们之前所学的assign。
示例7:

#import <Foundation/Foundation.h>@class B;@interface A : NSObject@property(nonatomic,weak)B *b;@end@implementation A- (void)dealloc{    NSLog(@"A被释放");}@end@interface B : NSObject@property(nonatomic, strong)A *a;@end@implementation B- (void)dealloc{    NSLog(@"B被释放");}@endint main(){    A *a = [[A alloc] init];    B *b = [[B alloc] init];    a.b = b;    b.a =a;    return 0;}

示例7使用了ARC机制,两个互相引用的对象先后被销毁。原先需要我们手动添加的代码releae、retain等都不再需要自己添加。
示例7的运行结果是:

2015-07-20 11:17:12.466 0[637:71993] B被释放2015-07-20 11:17:12.467 0[637:71993] A被释放
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 brp丢了怎么办回国补 被怀疑假结婚该怎么办 中考作弊被捉了怎么办 露娜注册错了怎么办 去泰国开民宿怎么办工作签证 绩点2.7想出国怎么办 香港过境想去澳门怎么办 加拿大博士资格考试没通过怎么办 外国人没有学历怎么办工作签证 澳洲语言班挂了怎么办 英国学位翻译成文学硕士怎么办 没考上好的高中怎么办 毕业证12月发放申请英国怎么办 高二迷茫成绩差怎么办 高二期末考的差怎么办 法国留学签证办不下来怎么办 澳洲留学挂科签证续签怎么办 澳洲旅游签证被拒了怎么办 学校六级不让刷分怎么办 英国留学生怎么办申根签证 挂科太多拿不到学位证怎么办 ucl语言班没过怎么办 西澳大学工程挂科怎么办 澳洲语言班没过怎么办2018 杨浦区对口公立小学太差怎么办 公立小学太差了怎么办 澳大利亚出国留学怎么办-语言课程 学校不给释放信怎么办 重修费交不起钱怎么办 不想用家里的钱怎么办 三本学费太贵怎么办 从日本往中国汇款怎么办 学费钱大一没交怎么办 初中孩子和同学相处不好怎么办 使用假护照出国被发现怎么办 酒店忘记退房了怎么办 语言课申请不上怎么办 学会计的应届生想转行怎么办 上班熬不下去了怎么办 墨大选修挂科怎么办 日本签证年收入没有10w怎么办