【Objective-C学习记录】第二十天

几天没写博客了,这几天在忙论文的事情,今天看OC的时候还没缓过来,重新看了看OC的知识- -关于内存还有初始化的一些概念吧。

内存

内存分区按照内存地址从高到低的顺序分为栈区、堆区、静态区、常量区、代码区

1.栈区:存放所有的局部变量,包括函数的形参

   特点:栈区的内存是由系统自动开辟和回收的,采用先进后出的管理原则管理局部变量,栈区变量只要函数执行完毕,就会被系统回收,所以通常不会返回栈区地址,因为没有意义。

2.堆区:是系统提供给开发者使用的内存。

   特点:这块内存由开发者操作,系统不做任何干预。如果只开辟不回收,那么堆区可用内存就会越来越少,当可用内存为0时,程序就会崩溃。如果开辟回收之后再次访问,也会crash,即野指针。所以堆区必须遵守有开辟必须有释放,释放后不允许再访问的原则。

3.静态区:也叫全局区。主要用来存储静态变量,以及全局变量。

   特点:由系统为变量分配内存空间,开发者不能控制,并且空间的回收只有程序退出时才会执行。静态区的变量,有一个特点,初始化只有一次,而且不会随着函数的执行完毕被回收。

4.常量区:存放系统内部所有的常量。

   特点:通常不会对常量区进行修改或者操作,一旦修改了常量区的内容,程序会立即崩溃。常量区的内容只有一份,并且是只读的。

5.代码区:代码区存放的不是源代码,函数都存放在栈区,代码区存放的是源代码编译后形成的可执行文件,也叫做二进制流文件,或者CPU指令。

堆区分配释放函数:malloc/free

比较少见的两个:calloc(unsigned n, unsigned size)/realloc(void *, unsigned newSize)

重置函数:memset()

内存比较函数:memcpm(),比较的不是内存大小,而是内存中存储的值的大小

类和对象

创建对象分两步:

1.开辟空间,在堆区开辟一块空间,来存放对象,并且将开辟好的堆区首地址返回给外界,但是此时,实例变量并没有初值。

2.初始化,将开辟好的堆区上的对象中的实例变量,赋初值。

接下来通过自己创建一个类来复习这些东西。

定义一个英雄类Hero,那么这个英雄应该有名字、攻速、攻击力、移速、血量、蓝量、魔抗、护甲。

 1 @interface Hero : NSObject
 2 {
 3     NSString *_name;
 4     NSInteger _hp;
 5     NSInteger _mana;
 6     NSInteger _atk;
 7     NSInteger _atkSpeed;
 8     NSInteger _moveSpeed;
 9     NSInteger _phyArmor;
10     NSInteger _magicArmor;
11 }
12 @end

OC中的属性一般以下划线开头,并且默认访问级别是protected,如果需要访问可设置为public,通过对象名->实例变量名来访问,但是一般不这么做,而是通过setter和getter来实现对外界提供访问的方式。

setter和getter

 1 - (void)setName:(NSString *)name;
 2 - (NSString *)name;
 3 
 4 - (void)setHp:(NSInteger)hp;
 5 - (NSInteger)hp;
 6 
 7 - (void)setMana:(NSInteger)mana;
 8 - (NSInteger)mana;
 9 
10 - (void)setAtk:(NSInteger)atk;
11 - (NSInteger)atk;
12 
13 - (void)setAtkSpeed:(NSInteger)atkSpeed;
14 - (NSInteger)atkSpeed;
15 
16 - (void)setMoveSpeed:(NSInteger)moveSpeed;
17 - (NSInteger)moveSpeed;
18 
19 - (void)setPhyArmor:(NSInteger)phyArmor;
20 - (NSInteger)phyArmor;
21 
22 - (void)setMagicArmor:(NSInteger)magicArmor;
23 - (NSInteger)magicArmor;

setter方法(设置器)的命名一般是 set + 除去下划线的属性名

getter方法(访问器)的命名一般是 除去下划线的属性名

写好了setter和getter的声明,为了方便我们进行初始化,所以再写一个初始化方法

初始化方法

1 - (instancetype)initWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor;

老实说,以前接触过C++ C#第一次看到这个初始化方法,不禁冒出一句话:这T.M.到底是个啥?- -我真的是第一次看到这么长的方法声明,不过OC里就是这样的:返回值 参数形容词:参数类型 参数名 参数形容词:参数类型...

好了,为了不让这么长方法吓到我们的用户,我们还是写一个便利构造器吧,便利构造器封装了对象的创建过程,是一种类方法,起到了让代码更简洁的作用

便利构造器

1 + (Hero *)heroWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor;

可是依然很长啊怎么办,手动挠头

写完了声明开始写方法的实现,跳至.m文件

初始化方法的实现

 1 - (instancetype)initWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor
 2 {
 3     self = [super init];
 4     if (self)
 5     {
 6         [self setName:name];
 7         [self setHp:hp];
 8         [self setMana:mana];
 9         [self setAtk:atk];
10         [self setAtkSpeed:atkSpeed];
11         [self setMoveSpeed:moveSpeed];
12         [self setPhyArmor:phyArmor];
13         [self setMagicArmor:magicArmor];
14     }
15     return self;
16 }

第三行 self = [super init];super指父类,self指进行该初始化的对象,这很容易理解,那么这到底是怎样的一个过程呢?首先看到DemonHunter这个类继承自NSObject,所以super init消息发送的是NSObject里的方法,在NSObject里有一个隐藏的属性,叫做isa,这个isa类似于OC里的id和instancetype,是一个万能的指针,它给该对象分配了空间。所以这个方法的过程就是通过NSObject里的init方法来为该对象分配内存空间。

接着if语句判断self是否为空,这个判断过程是否有必要。其实这个步骤的出现是,在其他的地方可能不小心释放了该对象的存储空间,这样赋值是没有任何意义的,所以直接返回该对象。

便利构造器实现

1 + (Hero *)heroWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor
2 {
3     return [[Hero alloc] initWithName:name hp:hp mana:mana atk:atk atkSpeed:atkSpeed moveSpeed:moveSpeed phyArmor:phyArmor magicArmor:magicArmor];
4 }

很容易理解,分配一个该类型的空间,通过自身的初始化方法进行初始化。有些地方的返回类型写的是instancetype或者id,结果都是一样的,但是初始化DemoHunter类又怎样会返回其他类呢?这样只是个人习惯- -能写清楚的不写清楚不舒服斯基

setter和getter实现

 1 - (void)setName:(NSString *)name
 2 {
 3     _name = name;
 4 }
 5 - (NSString *)name
 6 {
 7     return _name;
 8 }
 9 
10 - (void)setHp:(NSInteger)hp
11 {
12     _hp = hp;
13 }
14 - (NSInteger)hp
15 {
16     return _hp;
17 }
18 
19 - (void)setMana:(NSInteger)mana
20 {
21     _mana = mana;
22 }
23 - (NSInteger)mana
24 {
25     return _mana;
26 }
27 
28 - (void)setAtk:(NSInteger)atk
29 {
30     _atk = atk;
31 }
32 - (NSInteger)atk
33 {
34     return _atk;
35 }
36 
37 - (void)setAtkSpeed:(NSInteger)atkSpeed
38 {
39     _atkSpeed = atkSpeed;
40 }
41 - (NSInteger)atkSpeed
42 {
43     return _atkSpeed;
44 }
45 
46 - (void)setMoveSpeed:(NSInteger)moveSpeed
47 {
48     _moveSpeed = moveSpeed;
49 }
50 - (NSInteger)moveSpeed
51 {
52     return _moveSpeed;
53 }
54 
55 - (void)setPhyArmor:(NSInteger)phyArmor
56 {
57     _phyArmor = phyArmor;
58 }
59 - (NSInteger)phyArmor
60 {
61     return _phyArmor;
62 }
63 
64 - (void)setMagicArmor:(NSInteger)magicArmor
65 {
66     _magicArmor = magicArmor;
67 }
68 - (NSInteger)magicArmor
69 {
70     return _magicArmor;
71 }

不难发现,方法前面的修饰符有+和-之分,+修饰的称为类方法,被类消息发送,-修饰的称为对象方法,被对象消息发送。比如在初始化对象时会这么写:[[类名 alloc] init]。这里的alloc就是类方法,init就是对象方法。

好了,我们的英雄类已经写好了,接下来我们写一个它的子类:AntiMage

新建一个类,父类选择我们刚刚写的Hero。在这里,AntiMage类继承自Hero类,将Hero类里面所有的实例变量和方法都会继承过来,需要注意的是,即使是private修饰的实例变量也会被继承,但无法被访问。所以刚刚的那些属性我们无需在AntiMage里再次定义,可以直接使用,不过我们给它重写初始化方法和便利构造器:

1 - (instancetype)initWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor
2 {
3     return [self initWithName:name hp:hp mana:mana atk:atk atkSpeed:atkSpeed moveSpeed:moveSpeed phyArmor:phyArmor magicArmor:magicArmor];
4 }
5 + (DemonHunter *)demonHunterWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor
6 {
7     return [[DemonHunter alloc] initWithName:name hp:hp mana:mana atk:atk atkSpeed:atkSpeed moveSpeed:moveSpeed phyArmor:phyArmor magicArmor:magicArmor];
8 }

然后为它添加一个Blink方法,以及添加一个Blink距离的属性:

1 @interface DemonHunter : Hero
2 {
3     NSInteger _blinkDisntance;
4 }
5 
6 - (void)blink;

所以不得不为前面的初始化和便利构造器添加一个新的参数:

 1 - (instancetype)initWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor blinkDistance:(NSInteger)blinkDistance
 2 {
 3     self = [super initWithName:name hp:hp mana:mana atk:atk atkSpeed:atkSpeed moveSpeed:moveSpeed phyArmor:phyArmor magicArmor:magicArmor];
 4     if (self)
 5     {
 6         [self setBlinkDistance:blinkDistance];
 7     }
 8     return self;
 9 }
10 + (DemonHunter *)demonHunterWithName:(NSString *)name hp:(NSInteger)hp mana:(NSInteger)mana atk:(NSInteger)atk atkSpeed:(NSInteger)atkSpeed moveSpeed:(NSInteger)moveSpeed phyArmor:(NSInteger)phyArmor magicArmor:(NSInteger)magicArmor blinkDistance:(NSInteger)blinkDistance
11 {
12     return [[DemonHunter alloc] initWithName:name hp:hp mana:mana atk:atk atkSpeed:atkSpeed moveSpeed:moveSpeed phyArmor:phyArmor magicArmor:magicArmor blinkDistance:blinkDistance];
13 }

别忘了添加blinkDistance的setter和getter:

1 - (void)setBlinkDistance:(NSInteger)blinkDistance
2 {
3     _blinkDistance = blinkDistance;
4 }
5 - (NSInteger)blinkDistance
6 {
7     return _blinkDistance;
8 }

最后就可以在main里实现看看了,别忘了导入头文件喔

原文地址:https://www.cnblogs.com/shvier/p/5064914.html