zoukankan      html  css  js  c++  java
  • OC-ARC,类扩展,block

    总结

    标号主题内容
    autorelease autorelease基本概念/自动释放池/autorelease基本使用
    autorelease注意事项 注意点/应用场景
    ARC 什么是ARC/ARC的注意点和优点/ARC的判断原则/ARC机制判断/ARC快速使用
    ARC下的内存管理 ARC下单对象内存管理/多对象内存管理/循环引用问题/下@property参数
    ARC和MRC兼容和转换 ARC模式下如何兼容非ARC的类/如何将MRC转换为ARC
    Category 什么是Category/Category的格式/注意事项
     
    类扩展 什么是类扩展/类扩展书写格式
    **Block 什么是Block/block的格式/应用场景/注意事项
    typedef和Block 函数指针回顾/bl

    一.autorelease

    • autorelease其实就是将release延迟
    1.autorelease基本概念
    • autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作

      • 注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该对象依然不会被释放。

    • autorelease方法会返回对象本身

    Person *p = [Person new];
    p = [p autorelease];
    //只要调用autorelease就不用调用release了
    • 调用完autorelease方法后,对象的计数器不变
    Person *p = [Person new];
    p = [p autorelease];
    NSLog(@"count = %lu", [p retainCount]); // 1
    • autorelease的好处

      • 不用再关心对象释放的时间
      • 不用再关心什么时候调用release
    • autorelease的原理

      • autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。
    2.自动释放池
    • 创建自动释放池格式:
    • iOS 5.0前
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // 创建自动释放池
    [pool release]; // [pool drain]; 销毁自动释放池
    • iOS 5.0 开始
    @autoreleasepool
    { //开始代表创建自动释放池
    
    } //结束代表销毁自动释放池
    
    • 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

    • 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

    3.autorelease基本使用
    NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
    
    Person *p = [[[Person alloc] init] autorelease];
    
    [autoreleasePool drain];
    
    @autoreleasepool
    { // 创建一个自动释放池
            Person *p = [[Person new] autorelease];
    } // 销毁自动释放池(会给池子中所有对象发送一条release消息)
    4.注意点
    • 一定要在自动释放池中调用autorelease,才会将对象放入自动释放池中
    • 在自动释放池中创建了对象,一定要调用autorelease,才会将对象放入自动释放池中
    • 只要在自动释放池中调用autorelease就会将对象放入自动释放池中 -

    二.autorelease注意事项

    1.autorelease使用注意
    • 并不是放到自动释放池代码中,都会自动加入到自动释放池
     @autoreleasepool {
        // 因为没有调用 autorelease 方法,所以对象没有加入到自动释放池
        Person *p = [[Person alloc] init];
        [p run];
    }
    • 在自动释放池的外部发送autorelease 不会被加入到自动释放池中
      • autorelease是一个方法,只有在自动释 放池中调用才有效。
     @autoreleasepool {
     }
     // 没有与之对应的自动释放池, 只有在自动释放池中调用autorelease才会放到释放池
     Person *p = [[[Person alloc] init] autorelease];
     [p run];
    
     // 正确写法
      @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
     }
    
     // 正确写法
     Person *p = [[Person alloc] init];
      @autoreleasepool {
        [p autorelease];
     }
    • 自动释放池的嵌套使用

      • 自动释放池是以栈的形式存在
      • 由于栈只有一个入口, 所以调用autorelease会将对象放到栈顶的自动释放池
      • 栈顶就是离调用autorelease方法最近的自动释放池

        @autoreleasepool { // 栈底自动释放池
          @autoreleasepool {
              @autoreleasepool { // 栈顶自动释放池
                  Person *p = [[[Person alloc] init] autorelease];
              }
              Person *p = [[[Person alloc] init] autorelease];
          }
        }
    • 自动释放池中不适宜放占用内存比较大的对象

      • 尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
      • 不要把大量循环操作放到同一个 @autoreleasepool之间,这样会造成内存峰值的上升
    // 内存暴涨
        @autoreleasepool {
            for (int i = 0; i < 99999; ++i) {
                Person *p = [[[Person alloc] init] autorelease];
            }
        }
    
    // 内存不会暴涨
     for (int i = 0; i < 99999; ++i) {
            @autoreleasepool {
                Person *p = [[[Person alloc] init] autorelease];
            }
        }
    • 一个程序中可以创建N个自动释放池,并且自动释放池可以嵌套
      • 自动释放池是以栈的形式存储的,栈先进后出
      • 给一个对象方法一条@autorelease,永远会将对象放入栈顶的自动释放池中
    @autoreleasepool {
     @autoreleasepool {
      @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
       }//在此处p就销毁了
      }
     }
    2.autorelease错误用法
    • 不要连续调用autorelease
      • 一个alloc/new对应一个autorelease或者release
     @autoreleasepool {
     // 错误写法, 过度释放
        Person *p = [[[[Person alloc] init] autorelease] autorelease];
     }
    • 调用autorelease后又调用release
     @autoreleasepool {
        Person *p = [[[Person alloc] init] autorelease];
        [p release]; // 错误写法, 过度释放
     }
    3.应用场景(面试)
    • 创建类工厂方法时,要创建一个类方法,并且加上autorelease
    • 注意
      • Foundation框架的类,但凡是通过类工厂方法创建对象时都是autorelease的

    三.ARC

    1.什么是ARC
    • Automatic Reference Counting,自动引用计数

      • 手动管理内存, 可以简称MRC (Manual Reference Counting)

    • 在工程中永远不写retain,release和autorelease三个关键字就好~这是ARC的基本原则。

    • 当ARC开启时,编译器将自动在代码合适的地方插入retain, release和autorelease

    2.ARC的注意点和优点
    • ARC的注意点

      • ARC是编译器特性,而不是运行时特性(Xcode的功能)
      • ARC不是其它语言中的垃圾回收,有着本质区别,不是定时查看,而是运行程序
      • 不能在重写dealloc中调用[super dealloc]
      • 不能写autorelease/release/retain
    • ARC的优点

      • 完全消除了手动管理内存的烦琐
      • 基本上能够避免内存泄露
      • 有时还能更加快速,因为编译器还可以执行某些优化
    3.ARC的判断原则(没有引用计数)
    • ARC的判断原则

      • 只要还有一个强指针变量指向对象,对象就会保持在内存中
    • 强指针

      • 默认情况下,所有指针变量都是强指针
      • 被__strong修饰的指针
    int main
    {
        {
            Person *p = [ [Person alloc] init];
    
        }//在此处被释放
    }
    
    int main
    {
        {
            Person *p = [ [Person alloc] init];
            p = nil;//在此处被释放
        }
    }
    
    int main
    {
        {
            __strong Person *p = [ [Person alloc] init];
            __weak  Person *p2 = p;
    
        }//在此处被释放
    }
    
    int main
    {
        {
            __strong Person *p = [ [Person alloc] init];
            __weak  Person *p2 = p;
            p = nil;//在此处被释放
        }
    }
    
    - 强指针
     Person *p1 = [[Person alloc] init];
     __strong  Person *p2 = [[Person alloc] init];
    • 弱指针
      • 被__weak修饰的指针
      • 如果用一个弱指针保存刚创建的对象,就会立即释放
    __weak  Person *p = [[Person alloc] init];//立马被释放掉
    
    • 注意:当使用ARC的时候,暂时忘记“引用计数器”,因为判断标准变了。

    4. 单个对象的内存管理
    • 不用时就赋值为nil,保存时用strong

    ARC快速入门

    1.ARC机制判断
    • ARC机制下有几个明显的标志:
      • 不允许调用对象的 release方法
      • 不允许调用 autorelease方法
      • 再重写父类的dealloc方法时,不能再调用 [super dealloc];
    2.ARC快速使用
    int main(int argc, const char * argv[]) {
        // 不用写release, main函数执行完毕后p会被自动释放
        Person *p = [[Person alloc] init];
        return 0;
    }

    ARC下的内存管理

    • 在ARC中保存一个对象用strong
    1.ARC下单对象内存管理
    • 局部变量释放对象随之被释放
    int main(int argc, const char * argv[]) {
       @autoreleasepool {
            Person *p = [[Person alloc] init];
        } // 执行到这一行局部变量p释放
        // 由于没有强指针指向对象, 所以对象也释放
        return 0;
    }
    • 清空指针对象随之被释放
    int main(int argc, const char * argv[]) {
       @autoreleasepool {
            Person *p = [[Person alloc] init];
            p = nil; // 执行到这一行, 由于没有强指针指向对象, 所以对象被释放
        }
        return 0;
    }
    • 默认清空所有指针都是强指针
    int main(int argc, const char * argv[]) {
       @autoreleasepool {
            // p1和p2都是强指针
            Person *p1 = [[Person alloc] init];
            __strong Person *p2 = [[Person alloc] init];
        }
        return 0;
    }
    • 弱指针需要明确说明
      • 注意: 千万不要使用弱指针保存新创建的对象
    int main(int argc, const char * argv[]) {
       @autoreleasepool {
            // p是弱指针, 对象会被立即释放
            __weak Person *p1 = [[Person alloc] init];
        }
        return 0;
    }
    2.ARC下多对象内存管理
    • ARC和MRC一样,想拥有某个对象必须用强指针保存对象,但是不需要在dealloc方法中release
    @interface Person : NSObject
    
    // MRC写法
    //@property (nonatomic, retain) Dog *dog;
    
    // ARC写法
    @property (nonatomic, strong) Dog *dog;
    
    @end
    3.ARC下循环引用问题
    • ARC和MRC一样, 如果A拥有B, B也拥有A,那么必须一方使用弱指针
    • 在ARC中如果保存对象不要用assign,而是用weak
      • assign是专门用于保存基本数据类型的
    @interface Person : NSObject
    
    //@property (nonatomic, retain) Dog *dog;
    @property (nonatomic, strong) Dog *dog;
    
    @end
    
    @interface Dog : NSObject
    
    // 错误写法, 循环引用会导致内存泄露
    //@property (nonatomic, strong) Person *owner;
    
    // 正确写法, 当如果保存对象建议使用weak
    //@property (nonatomic, assign) Person *owner;
    @property (nonatomic, weak) Person *owner;
    @end
    4.ARC下@property参数
    • strong : 用于OC对象,相当于MRC中的retain
    • weak : 用于OC对象,相当于MRC中的assign
    • assign : 用于基本数据类型, 跟MRC中的assign一样

    ARC和MRC兼容和转换

    1.ARC模式下如何兼容非ARC的类

    在Build Phases中

    • 转变为非ARC -fno-objc-arc
    • 转变为ARC的, -f-objc-arc (不常用)
    2.如何将MRC转换为ARC
    • 先将项目转换成MRC
    • Edit-->Convert--> To Object-C ARC --> Check-->Next-->Save
    • 转换大项目时时常会出错

    Category

    1.什么是Category
    • Category有很多种翻译: 分类 类别 类目 (一般叫分类)

    • Category是OC特有的语法, 其他语言没有的语法

    • Category的作用

      • 可以在不修改原来类的基础上, 为这个类扩充一些方法(还有继承的方法来实现)
      • 一个庞大的类可以分模块开发,有利于管理多人开发
    2.Category的格式
    • 在.h文件中声明类别

      • 1)新添加的方法必须写在 @interface 与 @end之间
      • 2)ClassName 现有类的类名(要为哪个类扩展方法)
      • 3)CategoryName 待声明的类别名称
      • 4)NewMethod 新添加的方法
        //分类的声明
        @interface ClassName (CategoryName)
        NewMethod; //在类别中添加方法
        //不允许在类别中添加变量
        @end
        
        • 注意: 1)不允许在声明类别的时候定义变量
    • 在.m文件中实现类别:

      • 1)新方法的实现必须写在@ implementation与@end之间
      • 2)ClassName 现有类的类名
      • 3)CategoryName 待声明的类别名称
      • 4)NewMethod 新添加的方法的实现
    //分类的实现
    @implementation ClassName(CategoryName)
    
    NewMethod
    ... ...
    @end
    
    • 使用Xcode创建分类
    选择:Object - C File
    file Type: Category
    Class:Person
    

    Category注意事项

    1.分类的使用注意事项
    • 分类只能增加方法, 不能增加成员变量
    @interface Person (NJ)
    {
    //    错误写法
    //    int _age;
    }
    - (void)eat;
    @end
    • 分类中写property只会生成setter/getter方法声明
    @interface Person (NJ)
    // 只会生成getter/setter方法的声明, 不会生成实现和私有成员变量
    @property (nonatomic, assign) int age;
    @end
    • 分类可以访问原来类中的成员变量
    @interface Person : NSObject
    {
        int _no;
    }
    @end
    
    @implementation Person (NJ)
    - (void)say
    {
        NSLog(@"%s", __func__);
        // 可以访问原有类中得成员变量
        NSLog(@"no = %i", _no);
    }
    @end
    • 如果分类和原有类出现同名的方法, 优先调用分类中的方法, 原有类中的方法会被忽略(在开发中不要这样做)
    @implementation Person
    
    - (void)sleep
    {
        NSLog(@"%s", __func__);
    }
    @end
    
    @implementation Person (NJ)
    - (void)sleep
    {
        NSLog(@"%s", __func__);
    }
    @end
    
    int main(int argc, const char * argv[]) {
        Person *p = [[Person alloc] init];
        [p sleep];
        return 0;
    }
    
    输出结果:
    -[Person(NJ) sleep]
    2.分类的编译的顺序
    • 多个分类中与原有类有同名方法,则执行最后编译的文件方法(注意开发中千万不要这么干)
    @implementation Person
    
    - (void)sleep
    {
        NSLog(@"%s", __func__);
    }
    @end
    
    @implementation Person (NJ)
    - (void)sleep
    {
        NSLog(@"%s", __func__);
    }
    @end
    
    @implementation Person (MJ)
    - (void)sleep
    {
        NSLog(@"%s", __func__);
    }
    @end
    
    int main(int argc, const char * argv[]) {
        Person *p = [[Person alloc] init];
        [p sleep];
        return 0;
    }
    
    输出结果:
    -[Person(MJ) sleep]
    • 方法调用的优先级(从高到低)
      • 分类(最后参与编译的分类优先)
      • 原有类
      • 父类

    七.类扩展(匿名分类)

    1.什么是类扩展
    • 延展类别又称为扩展(Extendsion),Extension是Category的一个特例

    • 可以为某个类扩充一些私有的成员变量和方法

      • 写在.m文件
      • 英文名是Class Extension
    2.类扩展书写格式
    @interface 类名 ()
    @end
    
      • 对比分类, 就少了一个分类名称,因此也有人称它为”匿名分类”

    八.Block

    1.什么是Block
    • Block是iOS中一种比较特殊的数据类型(定义变量/作为形参/作为返回值类型)
    • 初始化方式
      • 定义的同时初始化
      • 先定义后初始化
    • Block应用场景比较广泛

      • 动画
      • 多线程
      • 集合遍历
      • 网络请求回调
    • Block的作用

      • 用来保存某一段代码,可以在恰当的时间再取出来调用
      • 功能类似于函数和方法
    • 注意

      • 如果没有形参,那么^后面的(形参列表)可以不写
    2.block的格式
    • Block的定义格式
    返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
    
    };
    
    • block最简单形式
    void (^block名)() = ^{代码块;}
    
    例如:
    void (^myBlock)() = ^{ NSLog(@"贺梦洁"); };
    
    • block带有参数的block的定义和使用
    void (^block名称)(参数列表)
    = ^ (参数列表) { // 代码实现; }
    
    例如:
    void (^myBlock)(int) = ^(int num){ NSLog(@"num = %i", num); };
    
    • 带有参数和返回值的block
    返回类型 (^block名称)(参数列表)
    = ^ (参数列表) { // 代码实现; }
    
    例如:
    int (^myBlock)(int, int) = ^(int num1, int num2){ return num1 + num2; };
    
    • 调用Block保存的代码,必须调用block才会执行
    block变量名(实参);
    
    3.Block应用场景
    • 当发现代码的前面和后面都是一样,只是中间部分不一样的时候,就要用Block

      • Block可以包含一段或者多段代码,所以用Block
    • Block调用:Block回调

    4.Block注意事项
    • 在block内部可以访问block外部的变量
    int  a = 10;
    void (^myBlock)() = ^{
        NSLog(@"a = %i", a);
        }
    myBlock();
    输出结果: 10
    • block内部也可以定义和block外部同名的变量(局部变量),此时局部变量会暂时屏蔽Block外部的同名变量
    int  a = 10;
    void (^myBlock)() = ^{
        int a = 50;
        NSLog(@"a = %i", a);
        }
    myBlock();
    输出结果: 50
    • 默认情况下, Block内部不能修改外面的局部变量
      • 因为Block中的变量和外界的变量并不是同一个变量
      • 如果Block访问外界的变量,Block会将外界的变量拷贝一份到堆内存中
    int b = 5;
    int a = 10;
    void (^myBlock)() = ^{
        b = 20; // 报错
        NSLog(@"b = %i", b);
        };
    a = 20;
    myBlock();
    //结果:10
    //因为Block中使用的外界的bl是拷贝的,所以在调用之前修改外界变量的值,不会影响Block中的copy的值
    • Block内部可以使用__block修改的外界变量的值
      • 如果在Block中修改了外界变量的值,会影响到外界变量的值
        __block int b = 5;
        void (^myBlock)() = ^{
        b = 20;
        NSLog(@"b = %i", b);
        };
        myBlock();
        输出结果: 20
    • 为什么不加就不能修改?加了就可以在Block中修改外界变量的值?
      • 不加__block是值传递,所以不能修改外界变量的值.
      • 加__block是地址传递,所以可以在block中修改外界变量的值.
    C++文件是.cpp
    
    • 面试中block是存储在堆中还是栈中?
      • block可以存储在堆中也可以在栈中
      • 默认是栈中,如果对block进行一个copy操作,block会转移到
      • 如果block在栈中,block中访问了外界的对象,那么不会对对象进行retain操作
      • 但如果block在中,block中访问了外界的对象,那么会对对象进行一次retain操作
      • 如果在block中访问了外界变量,一定要给对象加上__block,只要加上__block,那么就不会对外界变量进行retain操作
      • 如果在ARC中需要在前面加上__weak
        • Person *p = [ [Person alloc] init];
        • __weak Person *weaP = p;
      • 如果在iOS开发时,在ARC中不这样就容易导致循环引用

    九. typedef和Block

    1.函数指针回顾
    • 函数指针使用
    //加法
    int sum(int value1, int value2)
    {
        return value1 + value2;
    }
    //减法
    int minus(int value1, int value2)
    {
        return value1 - value2;
    }
    //主函数
    int main(int argc, const char * argv[]) {
        //函数指针
        int (*sumP) (int, int) = sum;
        int res = sumP(10, 20);
        NSLog(@"sum = %i", res);
    
        //函数指针
        int (*minusP) (int , int) = minus;
        NSLog(@"minus = %i", minusP(10, 20));
        return 0;
    }
    • 函数指针别名
    //起别名
    typedef int (*calculate) (int, int);
    //主函数
    int main(int argc, const char * argv[]) {
        //函数指针
        calculate sumP = sum;
        int res = sumP(10, 20);
        NSLog(@"res = %i", res);
        //函数指针
        calculate minusP = minus;
        NSLog(@"res = %i", minusP(10, 20));
        return 0;
    }
    2.block和typedef
    • block使用

      int main(int argc, const char * argv[]) {
        //Block
        //定义的同时进行初始化
        int (^sumBlock) (int, int) = ^(int value1, int value2){
            return value1 + value2;
        };
        int res = sumBlock(10 , 20);
        NSLog(@"sum = %i", res);
      
        //先定义后初始化
        int (^minusBlock) (int, int) ;
        minusBlock = ^(int value1, int value2){
            return value1 - value2;
        };
        NSLog(@"minus = %i", minusBlock(10 , 20));
      
        return 0;
      }
    • block别名

    //给Block起别名
    typedef int (^calculateBlock) (int, int);
    //主函数
    int main(int argc, const char * argv[])
    {
        //加法
        calculateBlock sumBlock = ^(int value1, int value2)
        {
            return value1 + value2;
        };
        NSLog(@"sum = %i", sumBlock(10, 20));
        //减法
        calculateBlock minusBlock = ^(int value1, int value2)
        {
            return value1 - value2;
        };
        res = minusBlock(10, 20);
        NSLog(@"res = %i", res);
    
        return 0;
    }
     
  • 相关阅读:
    从句分析
    artDialog ( v 6.0.2 ) content 参数引入页面 html 内容
    Java实现 LeetCode 13 罗马数字转整数
    Java实现 LeetCode 13 罗马数字转整数
    Java实现 LeetCode 13 罗马数字转整数
    Java实现 LeetCode 12 整数转罗马数字
    Java实现 LeetCode 12 整数转罗马数字
    Java实现 LeetCode 12 整数转罗马数字
    Java实现 LeetCode 11 盛最多水的容器
    Java实现 LeetCode 11 盛最多水的容器
  • 原文地址:https://www.cnblogs.com/HMJ-29/p/4697041.html
Copyright © 2011-2022 走看看