黑马程序员-iOS学习日记(八)内存管理

---------------------- <a href="http://www.itheima.com"target="blank">iOS开发</a>、<a href="http://www.itheima.com"target="blank">iOS培训</a>、期待与您交流! ----------------------


一、手动内存管理

1、引用计数器

(1)每个OC对象都有自己的引用计数器,是一个整数,表示“该对象被引用的次数”,即有多少人正在引用该OC对象,引用计数器占用4个字节的空间。

(2)引用计数器的作用

a、当使用allocnew或者copy创建一个新对象时,新对象的引用计数器默认就是1

b、当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出

(3)引用计数器的操作

a、给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)

b、给对象发送一条release消息,可以使引用计数器值-1

c、可以给对象发送retainCount消息获得当前的引用计数器值

2、对象的销毁

a、当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
b、当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

c、一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言

d、一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用

e、不要直接调用dealloc方法

f、一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

3、概念

a、僵尸对象:所占用空间已经被回收的对象,僵尸对象不能再使用

b、野指针:指向僵尸对象(不可用内存)的指针

c、空指针:没有指向任何东西的指针(0或nil),给空指针发送消息不会报错

4、原则

a、谁创建,谁就release;如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease

b、谁retain,谁就release;只要你调用了retain,无论这个对象是如何生成的,你都要调用release

5、手动内存管理代码示例(多对象引用)

// Book.h
#import <Foundation/Foundation.h>
@interface Book : NSObject
{
    int _price;
}
- (void)setPrice:(int)price;
- (int) price;
@end

// Book.m
#import <Foundation/Foundation.h>
#import "Book.h"
@implementation Book
- (void)setPrice:(int)price
{
    _price = price; // C语言类型不需要手动内存管理
}
- (int) price
{
    return _price;
}
@end

// Person.h
#import <Foundation/Foundation.h>
#import "Book.h"
@interface Person : NSObject
{
    NSString *_name;
    Book *_book;
}
- (void)setName:(NSString *)name;
- (NSString *)name;

- (void)setBook:(Book *)book;
- (Book *)book;
@end

// Person.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Book.h"
@implementation Person
- (void)setName:(NSString *)name // OC对象需要手动内存管理
{
    if (_name != name)  // set的对象和当前对象内部的对象不是同一个对象 才进行set 否则会出现设置同一个对象 引用计数器多1的错误
    {
        [_name release];    // 将原来的成员变量引用计数器-1
        _name = [name retain]; // 将传入的对象引用计数器+1 并赋值给需要设置的对象
    }
}
- (NSString *)name
{
    return _name;
}

- (void)setBook:(Book *)book
{
    if (_book != book)
    {
        [_book release];
        _book = [book retain];
    }
}
- (Book *)book
{
    return _book;
}
- (void)dealloc    // 重写父类的dealloc方法 释放该对象所引用的对象
{
    [_name release];
    [_book release];
    [super dealloc];    // 调用父类的dealloc方法释放其他对象资源 必须调用 且必须写在最后
}
@end

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Book.h"
int main()
{
    Person *p1 = [[Person alloc] init]; // p1:1
    Person *p2 = [[Person alloc] init]; // p2:1
    
    Book *b1 = [[Book alloc] init]; // b1:1
    Book *b2 = [[Book alloc] init]; // b2:1
    
    p1.book = b1;   // b1:2
    p2.book = b1;   // b1:3
    
    p2.book = b2;   // b1:2 b2:2
    
    [b2 release];   // b2:1
    [b1 release];   // b1:1
    
    [p1 release];   // p1:0 b1:0
    [p2 release];   // p2:0 b2:0
    
    return 0;
}
5、 @property参数
(1)控制set方法的内存管理
a、assign:直接赋值(默认,适用于非OC对象类型或手动内存管理时循环引用的一方)

b、retain:release旧值,retain新值(适用于OC对象类型)

c、copy:release旧值,copy新值

@property(assign) int age;
@property(retain) School *school;


- (void) setAge:(int) age
{
	_age = age;
}
- (int) age
{
	return _age;
}

- (void) setSchool:(School *) school
{
	if(_school != school)
	{
		[_school release];
		_school = [school retain];
	}
}
- (School *) school
{
	return _school;
}

(2)是否要生成set方法
a、readwrite:同时生成setter和getter的声明、实现(默认)
b、readonly:只会生成getter的声明、实现

(3)多线程管理
a、nonatomic:线程不同步,性能高 (一般就用这个)
b、atomic :线程同步,性能低(默认)

(4)setter和getter方法的名称(只影响方法名 不影响变量名)

a、setter : 决定了set方法的名称(一定要有个冒号 :)
b、getter : 决定了get方法的名称(一般用在BOOL类型)

@property(setter=abc:,getter=cba) int age;

test.abc = 10;
test.age = 10;
// 以上两句作用相同

int age = test.cba;
int age = test.age;
// 以上两句作用相同

6、循环引用

// Card.h
#import <Foundation/Foundation.h>

@class Person    // 在.h文件中使用@class 表示引用一个类 而不是import 在.m中需要import该类的头文件
@interface Card : NSObject
@property (nonatomic, assign) Person *person;    // 循环引用需要在一端使用assign而不是retain 否则会造成彼此都不能被释放
@end

// Person.h
#import <Foundation/Foundation.h>

@class Card
@interface Person : NSObject
@property (nonatomic, retain) Card *card;
@end
(1)@class
a、对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类
b、与#import的区别
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息。
如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了。
在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。
(2)循环retain
a、比如A对象retain了B对象,B对象retain了A对象。导致A对象和B对象无法释放
b、当两端互相引用时,应该一端用retain、一端用assign

7、自动释放池

(1)autorelease方法返回对象本身

(2)autorelease方法会将对象放入一个自动释放池中

(3)当自动释放池被销毁时,会对池子里面所有调用autorelease方法的对象做一个release操作(不是直接销毁对象)

(4)自动释放池可以嵌套

int main()
{
    
    @autoreleasepool
    {
        Person *p = [[[Person alloc] init] autorelease];    // p:1 调用了autorelease方法后 对象的引用计数器不会发生变化
    }   // p:0 销毁释放池被管理的对象的引用计数器才会-1
    return 0;
}

二、自动内存管理

1、ARC:ARC是一个编译器特征,它提供了对OC对象自动管理内存。ARC让开发者专注于感兴趣的代码和对象的关系,而不用考虑对象的retain和release。

2、判断原则:如果没有强指针指向该对象,则销毁该对象

3、强指针和弱指针

// 强指针:默认情况下所有指针都是强指针 或用__strong修饰的指针
Person *p1 = [[[Person alloc] init];
__strong Person *p2 = [[[Person alloc] init];
    
// 弱指针:用__weak修饰的指针
// 弱指针指向的对象被回收后,弱指针会自动变为nil指针,不会引发野指针错误
__weak Person *p3 = [[[Person alloc] init];
4、@property参数
strong:修饰OC对象强指针

weak:修饰OC对象弱指针

5、使用注意

a、不能调用release、retain、autorelease、retainCount
b、可以重写dealloc,但是不能调用[super dealloc]
c、@property : 想长期拥有某个对象,应该用strong,其他对象用weak
d、其他基本数据类型依然用assign
e、两端互相引用时,一端用strong、一端用weak

---------------------- <a href="http://www.itheima.com"target="blank">iOS开发</a>、<a href="http://www.itheima.com"target="blank">iOS培训</a>、期待与您交流! ----------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值