Day07-Object-C ARC与分类

1. 自动释放池

1. 自动释放池的原理
    存入到自动释放池中的对象, 在自动释放池被销毁的时候, 会自动调用存储在该自动释放池中的所有对象的release方法.
    可以解决的问题:
    将创建的对象, 存入到自动释放池中, 就不再需要手动的release这个对象了
    因为池子销毁的时候, 就会自动的调用池中所有的对象的release
    自动释放池的好处:将创建的对象存储到自动释放池中, 不需要再写release了
2. 如何创建自动释放池
    @autoreleasepool{
    }
    这对大括号代表这个自动释放池的范围
3. 如何将对象存储到自动释放池之中
    在自动释放池之中调用对象的autorelease方法, 就会将这个对象存入到当前自动释放池之中, 这个autorealease方法返回的是对象本身, 所以, 我们可以这么写
    @autoreleasepool
    {
     Person *p1 = [[[Person alloc]init]autorelease];
    }
    这个时候, 当这个自动释放池执行完毕之后, 就会立即为这个自动释放池中的对象发送1条release消息
    目前为止, 我们感受到了autorelease的好处
    创建对象, 调用对象的autorelease方法, 将这个对象存入到当前的自动释放池中,我们就不需要再去release, 因为自动释放池销毁的时候, 就会自动的调用池中所有对象的release
4. 使用注意
    1).只有在自动释放池中调用了对象的autorelease方法, 这个对象才会被存储到这个自动释放池之中,
        如果只是将对象的创建代码写在自动释放之中, 而没有调用对象的autorelease方法,是不会将这个对象存储到这个自动释放池之中的.
    2). 对象的创建可以在自动释放池的外面, 在自动释放池之中, 调用对象的autorelease方法, 就可以将这个对象存储到这个自动释放池之中.
    3). 当自动释放池结束的时候, 仅仅是对存储在自动释放池中的对象发送1条release消息, 而不是销毁对象.
    4). 如果在自动释放池中, 调用同1个对象的autorelease方法多次,就会将对象存储多次到自动释放池之中,在自动释放池结束的时候, 会为对象发送多条release消息, 那么这个就可能出现僵尸对象错误,所以, 1个自动释放池之中,只autorelease1次, 只将这个对象释放1次,否则就会出现野指针错误
    5).如果在自动释放池中, 调用了存储到自动释放池中的对象的release方法,在自动释放池结束的时候,还会再调用对象的release方法, 这个时候就有可能会造成野指针操作
        也可以调用存储在自动释放池中的对象的retain方法
    6).将对象存储到自动释放池, 并不会使对象的引用计数器+1
    所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了
    7).自动释放池可以嵌套
    调用对象的autorelease方法,会将对象加入到当前自动释放池之中
    只有在当前自动释放池结束的时候才会将对象发送release消息
5. autorelease的规范
    0). 创建对象, 将对象存储到自动释放池之中,就不需要再去手动的release
    1).类方法的第1个规范:
    一般情况下, 要求提供与自定义构造方法相同功能的类方法,这样可以快速的创建1个对象
    2).我们一般情况下,写1个类,会为我们的类写1个同名的类方法,用来让外界调用类方法来快速的得到1个对象
    规范: 使用类方法创建的对象,要求这个对象在方法中就已经被autorelease过了,
    这样,我们只要在自动释放池中,调用类方法来创建对象, 那么创建的对象就会被自动的加入到自动释放池中
    提供1个类方法来快速的得到1个对象
    规范:
        a.这个类方法以类名开头, 如果没有参数就直接是类名, 如果有参数就是 类名WithXX:
        b.使用类方法得到的对象,要求这个对象就已经被autorelease过了
        +(instancetype)person
        {
        return [[[self alloc]init]autorelease];
        }
        这样,我们直接调用类方法,就可以得到1个已经被autorelease过的对象
        @autoreleasepool
        {
            Person *p1 = [Person person];
            //这个p1对象已经被autorelease过了,不需要再调用autorelease了
            //这个p1对象就被存储到当前自动释放池之中
        }// 当自动释放池结束,就会为存储在其中的p1对象发送release消息
6. 实际上Apple的框架中的类也是遵守这个规范的
    通过类方法创建的对象都是已经被autorelease过的了,
    所以,我们也要遵守这个规范,类方法返回的对象也要被autorelease过
    以后,我们凡是创建对象是调用类方法创建的对象,这个对象就已经是autorelease过的了

2. ARC机制概述

1. 什么是ARC
    Automatic Reference Counting 自动引用计数, 即ARC
    顾名思义:系统自动的帮助我们去计算对象的引用计数器的值
    可以说是WWDC2011和ios引入的最大的变革和最激动人心的变化
    ARC是新的LLVM3.0编译器的一项特性, 使用ARC, 可以说一举解决了广大ios开发者所憎恨的手动管理内存的麻烦
    在程序中使用ARC非常简单, 只需要像往常那样编写代码
    只不过永远不要写retain  release  autorelease 永远要手动的调用 dealloc这三个关键字就好, 这是ARC的最基本原则
    当ARC开启时, 编译器会自动的在合适的地方插入retain, release autorelease代码
    编译器自动为对象做引用计数, 而作为开发者, 完全不需要担心编译器会做错,
    需要特别注意的是:ARC是编译器机制, 在编译器编译代码的时候, 会在适当的位置加入retain, release 和autorelease代码
2. ARC机制下, 对象何时被释放
    本质: 对象的引用计数器为0的时候, 自动释放
    表象: 只要没有强指针指向这个对象, 这个对象就会立即被回收
3. 强指针与弱指针
    强指针: 默认情况下, 我们声明1个指针, 这个指针就是1个强指针
            我们也可以使用__strong来显示的声明这是个强指针
            Person *p1;这是个强指针, 指针默认情况下都是1个强指针
            __strong Person *p2; 这也是1个强指针, 使用 __strong来显示的声明强指针
    弱指针: 使用__weak标识的指针就叫做弱指针
    无论是强指针还是弱指针, 都是指针, 都可以用来存储地址, 这是没有区别的
    都可以通过这个指针访问对象的成员
    唯一的区别就是在ARC模式下, 他们用来作为回收对象的基准
    如果1个对象没有任何强类型的指针指向这个对象的时候, 对象就会被立即被自动释放
4. 确认程序是否开启ARC机制
    1). 默认情况下, Xcode开启ARC机制
    2). ARC机制下, 不允许调用retain release retainCount autorelease方法
    3). 在dealloc中, 不允许[super dealloc]
5. 演示1个ARC案例
    int main(int argc, const char *argv[]){
        @autoreleasepool{
            Person *p1 = [Person new];// p1是1个强指针. 每个指针变量默认情况下都是1个强指针变量
            NSLog(@"------");
        }// 当执行到这里的时候, p1指针被回收, 那么Person对象就没有任何强指针指向它了, 对象就再这被回收
        return 0;
    }

3. ARC

1. ARC下的单个对象的内存管理
    在ARC的机制下: 当1个对象没有任何的强指针指向它的时候, 这个对象就会被立即回收
    1). 当指向对象的所有的强指针被回收的时候, 对象就会被立即回收
    int main(int argc, const char * argv[])
    {
        @autoreleasepool
        {
            Person *p1 = [Person new];// p1是个强指针
            Person *p2 = p1;// p2也是个强指针, p1和p2都指向Person对象
            // 每个指针变量默认情况下都是1个强指针变量
            NSLog(@"---");
        }// 当执行到这里的时候, p1指针被回收, p2指针也被回收, 那么Person 对象就没有任何强指针指向它了, 对象就在这被回收
        return 0;
    }
    2). 将所有指向对象的强指针赋值为nil的时候, 对象就会被立即回收
    int main(int argc, const char * argv[])
    {
        @autoreleasepool
        {
            Person *p1 = [Person new];// p1是个强指针, 每个指针变量默认情况下都是强指针变量
            p1 = nil; // 当执行到这句话的时候, p1赋值为nil, p1指针不再执行Person对象.
            // Person对象没有被任何的指针所指向, 所以, Person对象在这里被释放
            NSLog(@"----");
        }
        return 0;
    }
    这两种情况就叫做没有任何强指针指向对象
    1). 指向对象的所有强指针被回收掉
    2). 指向对象的所有强指针赋值为nil
2. 强指针与弱指针
    1). 强指针与弱指针的声明
        默认情况下, 所有的指针都是强类型的, 也就是说我们之前声明的指针变量都是强类类型的
        p1指针是强类型的, 因为默认情况下指针都是强类型的
        Person *p1 = [[Person alloc] init];
        不过我们可以使用__strong来显示的标识指针是强类型指针
        __strong Person *p2 = [Person new];
        这个时候p2指针类型是强指针类型的, 其实写不写__strong都是强类型的指针
        指针类型也可以是弱指针类型
        使用__weak标识指针的类型是弱类型指针
        __weak Person *p3 = p2;
        这个时候, p3指针就是1个弱类型的指针, p3弱指针也指向p2指针指向的对象
        在操作对象的时候, 通过强指针或者弱指针都可以操作, 没有任何区别
    2). ARC模式下的对象回收标准
        ARC机制下释放1个对象的标准是:没有任何强指针指向对象的时候, 对象就会被释放
        如果这个时候有弱指针指向, 也会被释放
        int main(int argc, const char * argv[]){
            @autoreleasepool
            {
                //使用__strong来标识p1指针是强类型的, 其实不写__strong也是强类型的
                __strong Person *p1 = [[Person alloc] init];
                // 使用__weak标识指针p2的类型是弱类型指针
                __weak Person *p2 = p1;
                // 这个时候, p2指针和p1指针都指向Person对象
                // 这个时候如果设置p1的值为nil
                p1 = nil;
                // 这个时候Person对象只有被1个弱指针p2指向, 没有任何强指针指向
                // 所以Person对象在这里被回收
            }
            return 0;
        }
    3). 最重要的1点: 不能创建对象用1个弱指针存储这个对象的指针
    这样的话, 刚创建出来的对象, 就没有任何强指针指向, 创建出来就会被回收
    int main(int argc, const char * argv[])
    {
        @autoreleasepool
        {
            // 创建1个对象, 将这个对象的地址赋值给1个弱指针, 后果就是创建出来的这个对象没有被任何强指针指向, 刚创建出来就会被释放
            __weak Person *p1 = [[Person alloc] init];
        }
        return 0;
    }
    4). 在ARC机制下, 当对象被回收的时候, 原来指向这个对象的弱指针会被自动设置为nil

3. ARC机制下的多个对象的内存管理

    1.ARC机制下的对象的回收的标准, 当没有任何强类型的指针指向对象的时候, 这个对象就会被立即回收
    2, 强类型指针, 弱类型指针
    3. 什么情况下叫做对象没有强指针指向
        1). 指向对象的强指针被回收
        2). 指向对象的强指针被赋值为nil
    4. 在ARC的机制下, @property参数不能使用retain
        因为retain代表生成的setter方法是MRC的标准的内存管理代码, 而我们在ARC的机制下, 不需要这些代码的
        所以, 在ARC机制下的setter方法, 什么都不要做, 直接赋值就可以了
    5. ARC机制下, 我们关注的重点
        当1个类的属性是1个OC对象的时候, 这个属性应该声明为强类型的还是弱类型的, 很明显, 应该声明为1个强类型的
        如何控制@property生成的私有属性, 是1个强类型的还是1个弱类型的呢
        使用参数, strong和weak
        @property(nonatomic, strong)Car *car
        代表生成的私有属性_car是1个强类型的
        @property(nonamic, weak)Car *car;
        代表生成的私有属性_car是1个弱类型的
        如果不写, 默认是strong
    6.使用建议
        1).在ARC机制下, 如果属性的类型是OC对象类型的, 绝大多数场景下使用strong
        2).在ARC机制下,如果属性的类型不是OC对象类型的,使用assign
        3).strong和weak都是应用在属性的类型是OC对象的时候, 属性的类型不是OC对象的时候就使用assign

        在ARC机制下,将MRC下的retain换位strong
        @property(nonamic, strong)Car *car;
        做的事情:
        1), 生成私有属性, 并且这个私有属性是strong
        2). 生成getter setter方法的声明
        3) 生成getter setter方法的声明
        setter 的实现:直接赋值 

7. ARC机制下的循环引用

    在ARC机制下,当两个对象相互引用的时候, 如果两边都使用strong, 那么就会出现内存泄漏
    解决方案, 1端使用strong, 1端使用weak

8. @property参数总结

    1. 开发程序分为ARC和MRC
    2. 与多线程相关的参数
        atomic 默认是安全的, 但是效率低下
        nonatomic:不安全, 但是效率高
        无论在ARC还是在MRC都可以使用
        使用建议:无论是ARC还是MRC, 都使用nonatomic
    3. retain:
        只能在MRC的模式下, 代表生成的setter方法是标准的内存管理代码
        当属性的类型是OC对象的时候, 绝大多数情况下使用retain, 只有在出现了循环引用的时候1边retain 1边assign
    4. assign
        在ARC和MRC的模式下都可以使用assign
        当属性的类型是非OC对象的时候, 使用assign
    5. strong:
        只能使用在ARC机制下, 当属性的类型是OC对象类型的时候, 绝大多数情况下使用strong
        只有出现了循环引用的时候, 1端strong 1端weak
    6 weak:
        只能在使用ARC机制下, 当属性的类型是OC对象的时候, 只有出现了循环引用的时候, 1端strong, 1端weak
    7. readonly readwrite
        无论是ARC还是MRC都可以使用
    8. setter getter 无论在ARC下还是在MRC下都可以改

    在ARC机制下, 原来使用retain的用strong
    出现循环引用的时候, MRC: 1边retain, 1边assign, ARC: 1边strong, 1边weak

11. 分类

1. 分类。 类别, 类目, category
2. 如果将所有的方法都写在同1个类模板中, 当然是完全可以的,如果全都写在一个模板中, 就显得很臃肿, 后期难以维护和管理
默认情况下1个类独占1个模板, 这个是将所有的成员都写在这1个模板中, 我们的想法: 那就让1个类占多个模块, 将功能相似的方法定义在同1个模块中, 这样的好处:方便维护和管理
3. 分类:
    1).顾名思义: 将1个类分为多个模块
    2)。如何为1个类添加分类
    3).会生成1个.h和1个.m的模块
    模块的文件名: 本类名+分类名.h   本类名+分类名.m
    4).添加的分类也分为声明和实现
    @interface 本类名(分类名)
    @end
    代表不是新创建1个类,而是对已有的类添加1个分类. 小括弧中写上这个分类的名字. 因为1个类可以添加多个分类, 为了区分每个分类, 所以分类要取名字
    @implementation Student(ss)
    @end
    这是分类的实现
4. 分类的使用
    1). 如果要访问分类中定义的成员, 就要把分类的头文件引进来
5. 分类的作用:将1个类分为多个模块

使用分类注意的几个地方:
    1.分类只能增加方法, 不能增加属性
    2.在分类之中可以写@property但是不会自动生成私有属性, 也不会自动生成getter setter的实现
    只会生成getter setter的声明
    所以, 你就需要自己写getter和setter的实现, 也需要自己定义属性, 这个属性就必须在本类中
3. 在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)
    但是可以调用本类的getter setter来访问属性
    本类的@property生成的私有属性, 只可以在本类的实现中访问
    分类中不能直接访问私有属性
    分类可以使用getter setter 来访问
4. 分类中可以存在和本类同名的方法
    当分类中有和本类中同名的方法的时候, 优先调用分类的方法, 哪怕没有引入分类的头文件
    如果多个分类中有相同的方法, 优先调用最后编译的分类

什么时候需要使用分类
当1个类的方法很多很杂的时候, 当1个类很臃肿的时候
那么这个时候我们就可以使用分类, 将这个类分为多个模块, 将功能相似的方法写在同1个模块之中

14. 非正式协议

1. 分类的作用在于可以将我们写的类分为多个模块
    可以不可以为系统的类写1个分类呢
    为系统自带的类写分类, 这个就叫做非正式协议
2. 分类的第2个作用:
    为1个已经存在的类添加方法

分类的作用:
    1). 将臃肿的类分为多个管理, 方便管理
    2). 扩展1个类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值