黑马程序员_OC类的研究和内存管理初识

(一)OC类的研究:

1.类的本质:

在OC中,类其实也是一个对象。这个对象是Class类型的(Class里面有*),称作类对象。而由类对象创建出来的这个
类 类型的对象叫做实例 对象。就是说,假如创建一个Car类,并且通过方法创建Car对象:Car *c  =   [ [ Car alloc ] init ]
 ,代码实现完成后,编译器会先利用class类型  创建Car类对象,然后再用Car类对象创建Car类型的对象。

2.获取内存中得类对象:

可以通过对象方法获取内存中的类对象 class d = [ c  class ];

可以通过类方法获取内存中得类对象 class d1 = [ Car  class ];

3.运行思路:

OC程序运行时,会把类,并且只会把这个类对象加载到内存一次。而对象却是无数多的,现在内存中只有一个Car类对
象,类对象里面放着 Car类方法的生命。然后通过分配存储空间和初始化后,Car类对象可以拥有无数Car类型的对象,
这些Car类型的对象都拥有一个继承自 NSObject父类的isa指针,指针指向自己类型的类对象,也就是Car类对象。用于调用Car类对象的方法。

通过上述方法找到类对象后,d d1数值肯定就是相等的,因为内存中当且仅有一个类对象的。这时候d d1就代表这个类
对象,可以使用类方法 [ [ d  alloc ] init ],当然,类名本身也表示类对象。

4.类的加载过程:

当整个程序加载到内存中时,会先加载父类,再加载子类。哪怕是main主函数中没有用到这个类,都要进行加载。类只会被加载一次,在类被 加载的时候,会调用一个系统的+ (void)load方法,这个方法是自动加载的。

当main函数中用到哪个类时(第一次使用这个类),这时候会进行更深层的加载,会调用一个+(void)initialize方法。

下面进行总结:

当程序启动时,会加载所有类和分类,而且加载后会调用每个分类的+load方法,只会调用一次。

当第一次使用一个类时,就会调用当前类的+initialize方法。

先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法)。

先初始化父类,在初始化子类(先调用父类的+initialize方法,在调用子类的+initialize方法)。

当分类和类中都有+initialize方法时候,根据规则,则会调用分类中得+initialize方法。

通过这些方法,可以进行监听。当类第一次使用时做一些操作,就可以重写+initialize方法。

5.description方法(NSObject自带):

在NSLog输出一个%@类型的对象时,会碰到两种情况,如果%@是一个NSString *类型,则输出相应的字符串。如果%@是一个对象,或者是 一个类对象,那么就会拥有一个输出规则--自动调用description。

①输出遇到类对象: 自动调用+description。

②输出遇到对象:自动调用-description。

而description是有返回值的,它的返回值是NSString *,NSLog遇到%@输出后,会调用description,拿到返回值然后输
出在屏幕上。所以,当 我们想输出整个类的所有属性时,一个一个用NSLog访问太麻烦,万一成员变量很多就大大的降
低了效率,这时候我们可以选择重写父类NSO bject的方法description,让其return我们想要的值就可以了。

下面我们通过代码来分析一下工作原理:

Person.h

#import <Foundation/Foundation.h>

@interface Person :NSObject

@property int age;

@property NSString *name;

@end



Person.m

#import "Person.h"

@implementation Person

-(NSString *)description

{

   return [NSString stringWithFormat:@"人的年龄是%d,姓名是%@",_age,_name ];

}

+(NSString *)description

{

   return @"ABC";

}

@end



main();

#import <Foundation/Foundation.h>

#import "Person.h"

int main()

{

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

    p.age =100;

    p.name =@"dsa";

   NSLog(@"%@",p);

    

    Class c = [Person class];

   NSLog(@"%@",c);

   return 0;

}


count:

2015-04-0317:41:09.939 o[1989:508218]人的年龄是100,姓名是dsa

2015-04-0317:41:09.940 o[1989:508218] ABC


上代码可以看出,+description默认输出类对象名称。-description默认输出类名+内存地址。在重写后,他们在被NSLog调用时返回了相应的 返回值。这样就实现了一次性输出所有属性的功能。

6.打印增强
  • 指针变量地址:NSLog(@"%p",&p);

  • 对象的地址:NSLog(@"%p",p);

  • 类名:对象地址:NSLog(@"%@",p);

  • %s---__FILE__---打印文件路径(OC NSLog不允许有中文,可以使用printf)

  • %d---__LINE__---打印当前行号。

7.SEL数据类型:

我们以前在调用一个方法[p  test]-->称作发送消息机制。其实就是把要调用的方法封装成sel数据类型发送给类,发消息发得就是sel数据类型。

一个sel数据类型代表一个方法。每一个方法地址都会有一个sel数据类型的数据相对应。

#import <Foundation/Foundation.h>

#import "Person.h"

int main()

{

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

    

    [p performSelector:@selector(test2)];

    

   return 0;

}


如上为sel的发送。调用了p的performselector方法把test2封装成sel数据类型发送给Person类,调用成功。步骤是:

①把test2包装成sel类型的数据。

②根据sel数据找到对应的方法地址。

③根据方法低值调用对应方法。

字符串包装sel:

 NSString *d =@"wjds";

SEL f = NSSelectorFromString(d);

[d performSelector:f];


(二)OC内存初识:

1.内存管理

为什么要管理内存呢,因为不同的数据类型在内存中的储存方式是不一样的,一些局部变量,如基本数据类型,结构体
,枚举,指针等类型 ,都会存放在内存的栈中,而对象储存在内存的堆中。OC系统中栈数据是自动回收的。它拥有自
己的代码块作用域,当代码块执行完毕,就会 从内存中清除。但是堆空间是动态分配的,是不可能会主动回收的。此时
需要程序员人工进行内存回收。任何继承了NSObject类的对象都要管 理内存

2引用计数器:

每一个OC对象都有引用计数器(4字节),表示被用的次数。当对象分配内存后(代码表示为alloc,new),引用计数器默认值变为1.引用计 数器用三个对象方法。
  • 给对象发送retain消息,计数器+1,retain返回值返回对象本身。

  • 给对象发送release消息,计数器-1,release没有返回值。

  • 给对象发送retainCount消息,获取计数器值--->NSUInteger类型--->%ld-->当计数器为0时,就从内存中移除。对象刚诞生时,计数器为1。

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


[p retain];

[p release];

//    NSUInteger c =  [p retainCount];

[p release];

[p retain];

return 0;


当计数器为0时,系统自动向对象发送dealloc消息。一般会选择重写dealloc方法,在这里释放相应的资源。dealloc就是对象的遗言。

3.僵尸对象:

当对象计数器为0时,称这时候的对象为僵尸对象(不能再使用,所占用的内存已经被回收的对象)。

4.野指针:

指向不可用内存的指针叫做野指针。当然,指向僵尸对象的指针也是野指针。

5.空指针:

不指向任何对象的指针,指针=nil ,NULL,0.空指针在OC语言中是允许的,比关切给空指针发送消息并不会报错。

6机制分析:

当一个对象刚被创建出来时候(new alloc表示分配内存空间给对象。而init不算创建对象,只是初始化)。对象引用计数器+1,然后当对象发送 retain消息,计数器+1,发送release消息,计数器-1.当对象调用完毕,不需要的时候,需要手动清除,此时release对象使计数器变为0,然后 对象自动发送dealloc消息,告知已“死亡”,此时这个对象是僵尸对象,还储存在堆中,原来指向他的那个指针变成了野指针。

7.EXC_BAD_ACCESS----->100%内存出错。访问了一块坏内存(不可用,被回收的)。--->野指针常见错误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值