zoukankan      html  css  js  c++  java
  • 【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里实现看看了,别忘了导入头文件喔

  • 相关阅读:
    编程思想之正则表达式
    SQL查询顺序
    hibernate inverse属性的作用
    介绍一下Hibernate的二级缓存
    JSON数据
    你没玩过的全新版本!Win10这些骚操作你知多少
    VSCode 小鸡汤 第01期
    Editor REST Client
    k8s常用命令
    【项目3-2】多肉植物网站
  • 原文地址:https://www.cnblogs.com/shvier/p/5064914.html
Copyright © 2011-2022 走看看