美文网首页
说一说基类 NSObject(二)

说一说基类 NSObject(二)

作者: 小曼blog | 来源:发表于2020-02-22 12:39 被阅读0次

    本节我们继续学习NSObject,这个类中的很多方法都是我们常用的,所以要重点学习,花点时间也是值得的。

    一、实例对象的生成和释放相关方法
    1. alloc

    类方法alloc,声明在NSObject类中。

    +(instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");

    作用是生成消息接收类的实例对象,为实例对象开辟存储空间, 并初始化对象。之前alloc负责分配内存空间,init负责对对象进行初始化操作。通过实验我们知道,现在alloc方法就可以完成开辟空间和初始化的操作。

    实际上,现在的alloc和new方法是一个意思了。

    验证之前,我们先新建一个target,新建一个类ClassA,如下图:


    image.png

    classA.h

    @interface ClassA : NSObject
    
    @property(nonatomic, copy)NSString * name;
    
    -(void)hello;
    
    @end
    

    classA.m

    #import "ClassA.h"
    
    @implementation ClassA
    
    -(void)hello {
        NSLog(@"你好,我是A类的对象,我的名字是%@", self.name);
    }
    
    @end
    

    alloc验证代码:
    在main.m中的main方法的自动释放池中添加代码:

           ClassA * a = [ClassA alloc];
            a.name = @"小雨滴💧";
            NSLog(@"alloc a = %@, name = %@",a, a.name);
            [a hello];
            
            ClassA * a2 = [ClassA new];
            a2.name = @"小雪花❄️";
            [a2 hello];
    

    打印结果:


    image.png
    2.dealloc

    对象方法dealloc声明在NSObject类中
    - (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
    作用是,释放实例对象。我们知道,当对象的引用计数为0的时候,对象就会被释放了,对象实际释放是在
    dealloc方法中进行的。程序不允许直接调用这个方法。允许子类中重写这个方法,当对象释放的时候,
    这个方法会被调用。

    我们在ClassA.m文件中重写dealloc方法

    - (void)dealloc
    {
        NSLog(@"A的对象 %@ , %@ 被销毁了",self, self.name);
    }
    
    

    再次运行,看看打印结果

    image.png

    因为我们是把对象a和a2写在自动释放池中的,所以自动释放池走完的时候,对象a,a2就会被释放了,dealloc方法就被调用了。
    我们在自动释放池外面在定义一个对象看看结果如何。

    //
    //  main.m
    //  Lesson8_2
    //
    //  Created by wenhuanhuan on 2020/2/22.
    //  Copyright © 2020 weiman. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "ClassA.h"
    
    int main(int argc, const char * argv[]) {
        
        ClassA * outA = [ClassA alloc];
        outA.name = @"外部A";
        
        @autoreleasepool {
    
            ClassA * a = [ClassA alloc];
            a.name = @"小雨滴💧";
            NSLog(@"alloc a = %@, name = %@",a, a.name);
            [a hello];
            
            ClassA * a2 = [ClassA new];
            a2.name = @"小雪花❄️";
            [a2 hello];
        }
        [outA hello];
        
        return 0;
    }
    
    

    看看打印结果:


    image.png
    3. release、retain、autorelease、retainCount

    这几个方法都是关于内存管理的,手动内存管理(MRC)的时候用于增加引用计数,减少引用计数,返回引用计数。ARC中,这几个方法不可用。简单了解下。
    这几个方法都是声明在NSObject协议中的。


    image.png

    -(instancetype)retain OBJC_ARC_UNAVAILABLE;
    给对象的引用计数加1,同时返回对象。
    -(oneway void)release OBJC_ARC_UNAVAILABLE;
    给对象的引用计数减1。
    -(instancetype)autorelease OBJC_ARC_UNAVAILABLE;
    把对象加入到自动释放池中,同时返回对象。
    -(NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
    返回对象的引用计数。

    4.finalize

    这个方法是垃圾回收机制中使用的方法,垃圾回收器在释放对象之前会执行finalize方法,有点像dealloc方法。
    垃圾回收是指在程序运行过程中,不定时的检查是否有不再使用的对象,如果有就释放它。目前,垃圾回收内存管理方式只用在Mac OS X的软件中,Mac OS X10.7和iOS5以上系统推荐使用ARC进行内存管理。我们这里不再仔细深究。


    image.png

    从截图上我们看到,OC的垃圾回收机制已经不再支持。(我使用的Xcode版本是 Version 11.3.1 (11C504))。

    5.init

    init方法是对对象进行初始化的,实验证明,目前可以省略这个方法。子类可以重写这个方法。
    这个方法的源码如下:

        - (id)init {
             return _objc_rootInit(self);
         }
    
         id _objc_rootInit(id obj)
         {
             return obj;
         }
    

    我们可以发现,这个init方法什么都没干呀,所以是可以省略咯。

    init方法可以被重写,来实现自己想要在初始化的时候自定义的内容。
    我们在ClassA中写一下验证代码:
    ClassA.h

    @interface ClassA : NSObject
    
    @property(nonatomic, copy)NSString * name;
    @property(nonatomic, assign)int age;
    @property(nonatomic, copy)NSString * remark;
    
    -(instancetype)init;
    -(void)hello;
    -(void)printAllProperty;
    
    @end
    

    ClassA.m

    #import "ClassA.h"
    
    @implementation ClassA
    
    -(instancetype)init {
        if (self = [super init]) {
            self.name = @"无名氏";
            self.remark = @"default";
        }
        return self;
    }
    
    -(void)hello {
        NSLog(@"你好,我是A类的对象,我的名字是%@", self.name);
    }
    
    -(void)printAllProperty {
        NSLog(@"self = %@ :", self);
        NSLog(@"name = %@, age = %d, remark = %@",
                self.name, self.age, self.remark);
    }
    
    - (void)dealloc
    {
        NSLog(@"A的对象 %@ , %@ 被销毁了",self, self.name);
    }
    
    @end
    
    

    main中:

           ClassA * a3 = [[ClassA alloc] init];
            NSLog(@"赋值前 a3: ");
            [a3 printAllProperty];
            a3.name = @"小云朵☁️";
            a3.age = 20;
            a3.remark = @"白白的云";
            NSLog(@"赋值后 a3: ");
            [a3 printAllProperty];
            
            ClassA * a4 = [ClassA alloc];
            NSLog(@"只用alloc创建的对象a4: ");
            [a4 printAllProperty];
    

    打印结果:


    image.png

    从打印结果可以看出,此时,只用alloc创建的对象和使用init初始化的对象是不一样的,只用alloc创建的对象不会走重写的init方法。

    6.initialize方法
    类方法initialize,定义在NSObject类中。
    +(void)initialize;
    这个方法用于类的初始化。这个方法会在类收到第一个消息之前被自动执行。

    我们在ClassA的实现方法中,重写这个方法。

    + (void)initialize
    {
        if (self == [ClassA class]) {
            NSLog(@"ClassA 被初始化了");
        }
    }
    

    执行结果:


    image.png

    我们发现,这个方法是最先执行的。

    之前认为,这个方法不可以手动调用的,实验发现,是可以被手动调用的。


    image.png

    看看打印:


    image.png

    也是被成功调用了。

    7.new

    类方法new,定义在NSObject类中,

    • (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
      new方法被认为是alloc和init方法的组合。源码如下:
    + (id)new {
        return [callAlloc(self, false/*checkNil*/) init];
    }
    

    其实就是把alloc和init结合起来了,不过现在init什么事都不做,如果没有重写init方法的话,new和alloc方法其实是一个意思了。但是,重写了init方法,new和alloc就不一样了。

    我们在实验init方法的时候,重写了init方法,现在我们使用new再创建一个对象看看。


    image.png

    打印结果:


    image.png

    此时,我们发现,使用new创建的对象,执行了重写的init方法,这与只是用alloc创建的对象是不一样的。

    相关文章

      网友评论

          本文标题:说一说基类 NSObject(二)

          本文链接:https://www.haomeiwen.com/subject/juqlqhtx.html