几天没写博客了,这几天在忙论文的事情,今天看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,结果都是一样的,但是初始化De*nter类又怎样会返回其他类呢?这样只是个人习惯- -能写清楚的不写清楚不舒服斯基
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里实现看看了,别忘了导入头文件喔