黑马程序员_第九天视频学习 类和对象的内存管理

---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效

一、引用计数器

每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”

每个OC对象内部专门有4个字节的存储空间来存储引用计数器

1、  引用计数器的作用

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

b当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收

2、引用计数器的操作

a、retain、release方法的基本使用

1> retain:计数器+1;会返回对象本身

2> release:计数器-1

3> retainCount:获取当前的计数器

4> dealloc:当一个对象要被回收的时候,就会调用

        一定要调用[super dealloc],而且这句调用一定要放在最后面

b、概念

1> 僵尸对象: 所占用内存已经被回收的对象,僵尸对象不能再使用

2> 野指针: 指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错:EXC_BAD_ACCESS

3> 空指针: 没有指向任何东西的指针 (存储的东西是nil、NULL、0),给空指针发送消息不会报错

2、  对象的销毁

a当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收

b当一个对象被销毁时,系统会自动向对象发送一条dealloc消息

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

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

e不要直接调用dealloc方法

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

#import <Foundation/Foundation.h>

 

@interface Person : NSObject

 

@property int age;

@end

@implementation Person

 

// 当一个Person对象呗回收的时候,就会自动调用这个方法

- (void)dealloc

{

    NSLog(@"Person对象被回收");

   

    // superdealloc一定要调用;而且放在最后面

    [super dealloc];

}

 

@end

int main(int argc,const char * argv[])

{

 

    @autoreleasepool {

        Person *p = [[Personalloc] init];

       

        NSUInteger c = [p retainCount];

       

        NSLog(@"计数器:%ld",c);

       

        // retain方法返回的是对象本身

       

        // 2

        [p retain];

       

        // 引用计数器减1

        // 1

        [p release];

       

        // 0

        [p release];

       

        // message sent todeallocated instance 0x100109a10

        // 给已经释放的对象发送了一条-setAge:消息

        // p.age = 10;

       

        // p指向的是僵尸对象,应清空指针,指针p变成空指针

        p = nil ;

       

        // OC不存在空指针错误,给空指针发送消息不报错

        [nil release];

       

       

        // EXC_BAD_ACCESS :访问了一块坏的内存(已经被回收、已经不可用的内存)

        // 野指针错误

        [p release];

        [p release];

        [p release];

        [p release];

       

    }

    return 0;

}

 

二、内存管理原则

1、只要还有人在用某个对象,那么这个对象就不会被回收

2只要你想用这个对象,就让对象的计数器+1

3当你不再使用这个对象时,就让对象的计数器-1

4、如果你通过alloc、new、或者[mutable]copy来创建对象,那么你必须调用release或autorelease

5、只要你调用了retain,你就用调用release

 

三、set方法的内存管理

    (一)、内存管理代码规范

1、只要调用了alloc,必须有release(autorelease)

2、set方法的代码规范

    1> 基本数据类型:直接复制

        -(void)setAge:(int)age

        {

            _age = age;

        }

    2> OC对象类型

        -(void)setCar:(Car *)car

        {

            // 1、判断是否是新传进来的对象

            if (_car !=car) {

 

                // 2、对旧对象做一次release操作

                [_carrelease];

 

                // 3、对新对象做一次retain操作

                _car =[car retain];

            }

        }

3、dealloc方法的代码规范

    1> 一定要写[super dealloc],而且放到最后面

    2> 对当前对象(self)所拥有的其他对象做一次release操作

    (二)、@property参数

1、set方法内存管理相关的参数

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

    assign : 直接赋值(默认,适用于非OC对象类型)

    copy   : release旧值,copy新值

 2、是否要生成set方法

    readwrite :同时生成setter和getter的声明、实现 (默认)

    readonly  :只会生成getter的声明和实现

 3、多线程管理

    nonatomic :性能高 (一般就用这个)

    atomic    :性能低 (默认)

 4、setter和getter方法的名称

    setter : 决定了set方法的名称,一定要有冒号:

    getter : 决定了get方法的名称 (一般用在BOOL类型)

 

@interface Person : NSObject

 

@property (assign,readwrite,atomic)int age;

 

@property (retain,nonatomic)NSString *name;

 

@property (readonly)int height;

 

// 返回BOOL类型的方法名一般以is开头

@property (getter = isRich,setter = setRich:)BOOL rich;

 

@end

 

四、循环引用

1、@class的作用:仅仅告诉编译器,某个名称是一个类 

@class Person; // 仅仅告诉编译器,Person是一个类

2、开发中引用一个类的规范

1> 在.h文件中用@class来声明类

2> 在.m文件中用#import来包含类的所有东西

除了父类,引用其他类的时候,用@class,不用#import,提高性能

3、两端循环引用解决方案

1> 一端用retain

2> 一端用assign

4、@class和 #import的区别

a、 #import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器这是类的声明,具体这个类里的信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看类中的信息

b、如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,而相对来 讲,使用@class方式就不会出现这种问题了

c、在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,@class还需要使用#import方式引入被引用类

 

五、autorelease

1、autorelease的基本用法

1> 会将对象放到一个自动释放池中

2> 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

3> 会返回对象本身

4> 调用完autorelease方法后,对象的计数器不变 

 2、autorelease的好处

1> 不用再关心对象的释放时间

2> 不用再关心什么时候调用release

 3、autorelease的使用注意

1> 占用内存较大的对象不要随便使用autorelease

2> 占用内存较小的对象使用autorelease,没有太大影响

 4、错误写法

1> alloc 之后调用了autorelease,又调用了release

        @autoreleasepool {

            // 1

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

 

            // 0 会发生野指针错误

            [p release];

        }

2> 连续调用多次autorelease

        @autoreleasepool {

            // 释放池销毁时会进行两次release操作

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

        }

 5、自动释放池

1> 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在(先进后出)

2> 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

 6、自动释放池的创建方式

1> IOS 5.0

        NSAutoreleasePool*pool = [[NSAutoreleasePool alloc] init];

 

        Person *p2 =[[[Person alloc] init] autorelease];

 

        [pool release]; //[pool drain];

2> IOS 5.0 开始

        @autoreleasepool

        {

           

        }

 

#import <Foundation/Foundation.h>

 

@interface Person : NSObject

 

@property (nonatomic,assign) int age;

 

@end

@implementation Person

- (void)dealloc

{

    NSLog(@"Person--- dealloc");

    [super dealloc];

}

@end

int main(int argc,const char * argv[])

{

    NSAutoreleasePool *pool = [[NSAutoreleasePoolalloc] init];

   

    Person *p2 = [[[Personalloc] init]autorelease];

   

    [pool release]; // [pooldrain];

   

    @autoreleasepool

    { // {开始代表创建了释放池

       

        // autorelease方法会返回对象本身

        // 调用完autorelease方法后,对象的计数器不变

        // autorelease会将对象放到一个自动释放池中

        // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

        Person *p = [[[Personalloc] init]autorelease];

       

        p.age = 10;

       

        @autoreleasepool {

           

            Person *p2 = [[[Personalloc] init]autorelease];

           

            p2.age =20;

        }

       

    } // }结束代表销毁释放池

   

    return 0;

}

 

7、autorelease 应用

a、系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的

b、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

    创建对象时不要直接用类名,一般用self

        +(id)person

        {

            return[[[self alloc] init] autorelease];

        }

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic,assign) int age;

 

+ (id) person;

+ (id) personWithAge:(int)age;

 

@end

@implementation Person

 

+ (id)person

{

    return [[[selfalloc] init]autorelease];

}

- (void)dealloc

{

    NSLog(@"%d岁的人被销毁了",_age);

    [super dealloc];

}

+(id)personWithAge:(int)age

{

    // Person *p = [[[Personalloc] init] autorelease];

    Person *p = [selfperson];

    p.age = age;

    return p;

}

 

@end

@interface GoodPerson : Person

 

@property (nonatomic,assign) int money;

 

@end

@implementation GoodPerson

@end

int main(int argc,const char * argv[])

{

    Person *p = [[Personalloc] init];

    p.age =19;

    [p release];

 

    @autoreleasepool {

       

        Person *p1 = [Personperson];

       

        p1.age =20;

       

        Person *p2 = [PersonpersonWithAge:30];

       

         NSString *str = @"sf23"; // 默认autorelease

        

        NSString *str2 = [NSStringstringWithFormat:@"age is %d",10];

       

        NSNumber *num = [[NSNumberalloc] initWithInt:10];

        [num release];

       

        NSNumber *num2 = [NSNumbernumberWithInt:100];

       

        GoodPerson *m = [GoodPersonpersonWithAge:340];

        m.money = 100;

       

    }

    return 0;

}

 

六、ARC

自动引用技术,是编译器特性

ARC判断准则: 只要没有强指针指向对象,就会释放对象。

1、ARC特点

1> 不允许调用release、retain、retainCount

2> 允许重写dealloc,但不允许调用[super dealloc]

3> @property的参数

a、strong: 成员变量是强指针 (适用于OC对象类型)

b、weak  : 成员变量是弱指针 (适用于OC对象类型)

c、assign: 适用于非OC对象类型

4> 以前的retain改为用strong,其他一切不变

2、指针分2种:

1> 强指针:默认情况下,所以指针都是强指针

2> 弱指针:__weak

3、当两端循环引用的时候,解决方案

1> ARC

        一端用strong,一端用weak

2> 非ARC

        一端用retain,一端用assign

Person.h

#import <Foundation/Foundation.h>

@class Dog;

 

@interface Person : NSObject

@property (nonatomic,strong) Dog *dog;

@property (nonatomic,strong) NSString *name;

@property (nonatomic,assign) int age;

 

@end

Person.m

@implementation Person

 

- (void)dealloc

{

    NSLog(@"Personid dealloc");

}

 

@end

Dog.h

@class Person;

@interface Dog : NSObject

@property (nonatomic,weak) Person *person;

@end

Dog.m

@implementation Dog

 

- (void) dealloc

{

    NSLog(@"Dog-- dealloc");

}

 

@end

main.m

#import <Foundation/Foundation.h>

#import "Person.h"

#import "Dog.h"

 

int main(int argc,const char * argv[])

{

    Person *p = [[Personalloc] init];

   

    Dog *d = [[Dogalloc] init];

    p.dog = d;

    d.person = p;

    return 0;

}

 

void test()

{

    Person *p = [[Personalloc] init];

   

    // Person *p2 = p; // 有这个代码,上面的对象就只有在程序结束的时候才会释放

   

    // 创建新对象,释放上面的对象

    p = [[Person alloc] init];

   

    // 销毁p对象

    // p = nil;

   

    NSLog(@"---");

}

void test2()

{

   

    // __weak Person *p = [[Person alloc]init]; //没有强指针指向,直接被释放,p变成空指针

   

    Person *p = [[Personalloc] init];

   

    // 弱指针 p2

    __weak Person *p2 = p;

   

    // 对象在此处被释放

    p = nil;

   

    p2 = nil;

   

    NSLog(@"---");

}

void test3()

{

    Person *p = [[Personalloc] init];

   

    Dog *d = [[Dogalloc] init];

    p.dog = d;

   

    Dog *d2 = [[Dogalloc] init];

    p.dog = d2;

   

    NSLog(@"---");

   

}


七、Block

1、 如何定义block变量

int (^sumBlock)(int, int);

void (^myBlock) ();

2、 如何利用block封装代码

 ^(inta, int b){

 returna + b;

 };

 ^(){

 NSLog(@"-------");

 };

 ^{

 NSLog(@"-------");

 };

3、 block访问外面变量

 1>block内部可以访问外面的变量

 2> 默认情况下,block内部不能修改外面的局部变量

 3> 给局部变量加上_block关键字, 这个局部变量就可以在block内部修改

4、 利用typedef定义block类型

 typedef int (^MyBlock)(int, int);

 // 以后就可以利用MyBlock这种类型来定义block变量

   MyBlock b1,b2;

    b1= ^(int a, int b){

       return a - b;

    };

   MyBlock sumBlock = ^(int a, int b){

       return a + b;

};

 

#import <Foundation/Foundation.h>

 

typedef int (^MyBlock)(int, int);

 

int main(int argc, const char * argv[])

{

    MyBlock sumBlock = ^(int a, int b){

        return a + b;

    };

   

    MyBlock minusBlock = ^(int a, int b){

        return a - b;

    };

   

    MyBlock multiplyBlock = ^(int a, int b){

        return a * b;

    };

   

    NSLog(@"%d - %d- %d",sumBlock(12,6), minusBlock(15,3), multiplyBlock(15, 3));

   

    return 0;

}

 

// 没有返回值,没有形参的block

void test()

{

    // block用来保存一段代码

    // block的标志 ^

    /*

     block跟函数很像:

     1、可以保存代码

     2、有返回值

     3、有形参

     4、调用方式一样

     */

    // 定义block变量

   

    // 如果block没有形参,可以省略后面的()

    void (^myBlock) () = ^{

        NSLog(@"-------");

    };

   

    // 判断block变量调用block内部的代码

    myBlock();

}

 

// 有返回值、有形参的block

void test2()

{

    int (^sumBlock)(int, int) = ^(int a, int b){

        return a + b;

    };

   

    int c = sumBlock(4, 5);

   

    NSLog(@"%d",c);

   

    // 用一个block输出N条横线

    void (^lineBlock)(int) = ^(int n){

        for (int i = 0; i < n; i++) {

            NSLog(@"------");

        }

    };

    lineBlock(5);

}

 

void test3()

{

    int a =10;

    __block int b = 20;

   

    void (^block)();

   

    block = ^{

        // block内部可以访问外面的变量

        NSLog(@"a = %d",a);

       

        // 默认情况下,block内部不能修改外面的局部变量

        // a = 20;

       

        // 给局部变量加上_block关键字,这个局部变量就可以在block内部修改

        b = 25;

    };

   

    block();

}

 

八、protocol

1、 协议的定义

 @protocol 协议名称 <NSOject>

 // 方法的声明列表...

 @end

2、如何遵守协议

   1> 一个类遵守协议

   @interface 类名 : 父类名 <协议名称>

   @end

   2> 协议遵守协议

   @protocol 协议名称 <其他协议名称1,其他协议名称2>

   @end

3、协议中方法声明的关键字

   1> @required 要求实现,如果没有实现就会发出警告(默认)

   2> @optional 不要求实现,怎样都不会警告

4、定义一个变量的时候,限制这个变量保存的对象遵守某个协议

    类名<协议名称> *变量名;

   id<协议名称> 变量名;

   NSOject<MyProtocol> *obj;

   id<MyProtocol> obj2;

    如果没有遵守对应的协议,编译器会警告

5、@property中声明的数学也可用做一个遵守协议的限制

   @property (nonatomic, strong) 类名<协议名称> *属性名;

   @property (nonatomic, strong) id<协议名称> 属性名;

   @property(nonatomic, strong) Dog<MyProtocol> *dog;

   @property (nonatomic, strong) id<MyProtocol> dog1;

6、协议可以定义在单独.h文件中,也可以定义在某个类中

   1> 如果这个协议只用在某个类中,应该把协议定义在该类中

   2> 如果这个协议用在很多类中,就应该定义在单独文件中

7、分类可以定义在单独.h和.m文件中,也可以定义在原来类中

   1> 一般情况下,都是定义在单独文件中

   2> 定义在原来类中的分类,只要求能看懂语法

MyProtocol.h

#import <Foundation/Foundation.h>

// 定义了一个名叫MyProtocol的协议

@protocol MyProtocol <NSObject>

//@required 要求实现,不实现就会发出警告

//@optional 不要求实现

- (void)test4;

@required // 默认@required

- (void)test;

- (void)test2;

@optional

- (void)test3;

@end

MyProtocol2.h

#import <Foundation/Foundation.h>

@protocol MyProtocol2 <NSObject>

- (void)haha;

@optional

- (void)haha1;

@end

MyProtocol3.h

// 一个协议遵守了另外一个协议,就可以拥有另外一个协议的所有方法声明

@protocol MyProtocol3 <MyProtocol>

- (void)hehe;

@end

Person.h

#import <Foundation/Foundation.h>

// #import"MyProtocol3.h"

@class Hashiq;

@protocol MyProtocol2 ;

@protocol MyProtocol3 ;

// 只要一个类遵守了某一份协议,就能拥有这份协议中的所有方法声明

// : 继承

// <>遵守协议

@interface Person : NSObject <MyProtocol3, MyProtocol2>

 

@property (nonatomic, strong) id<MyProtocol2> obj;

@property (nonatomic, strong) Hashiq *hashiq;

@end

Person.m

#import "Person.h"

#import "MyProtocol2.h"

#import "MyProtocol3.h"

@implementation Person

- (void)test

{

}

- (void)test2

{

}

- (void)test4

{

}

- (void)haha

{

}

- (void)hehe

{

}

@end

Dog.h

#import <Foundation/Foundation.h>

@protocol MyProtocol2 ;

@interface Dog : NSObject <MyProtocol2>

@end

Dog.m

#import "Dog.h"

#import "MyProtocol2.h"

@implementation Dog

 

- (void)haha

{

}

@end

Hashiq.h

#import "Dog.h"

@interface Hashiq : Dog

@end

Hashiq.m

#import "Hashiq.h"

@implementation Hashiq

@end

main.m

#import <Foundation/Foundation.h>

#import "MyProtocol.h"

#import "Person.h"

#import "MyProtocol3.h"

#import "Dog.h"

#import "Hashiq.h"

 

int main(int argc, const char * argv[])

{

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

    p.obj =[[Dog alloc] init];

    p.obj =[[Hashiq alloc] init];

    return 0;

}

 

void test()

{

    NSObject *obj = [[NSObject alloc] init];

    NSObject *obj2 = @"sf23kfs";

    // 要求obj3保存的对象必须是遵守MyProtocol这个协议

    //NSObject<MyProtocol> *obj3 = [[NSObject alloc] init]; // [[NSObjectalloc] init]是没有遵守Myprototol协议的

    NSObject<MyProtocol> *obj3 = [[Person alloc] init];

    obj3 = nil;

    id<MyProtocol> obj4 = [[Person alloc] init];

    obj4 = nil;

    // 需求obj5,保存的对象必须遵守MyPrototol3、并且继承了Person

    Person<MyProtocol3> *obj5 = [[Person alloc] init];

}


 

---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值