iOS的内存管理和引用计数规则、Block的用法以及三种形式(stack、malloc、global)

学习内容

  1. iOS的内存管理和引用计数规则objective-c

    • 内存管理的思考方式express

      • 本身生成的对象本身持有
      • 非本身生成的对象本身也能持有
      • 本身持有的对象不须要时释放
      • 非本身持有的对象不能释放
    • ARC有效时,id类型和对象类型必须加上全部权修饰符,一共有四种安全

      • __strong函数

        • id和对象类型若是不加全部权修饰符那么默认为__strong类型学习

        • id obj = [[NSObject alloc]init] 
          id __strong obj = [[NSObject alloc]init] 
          //以上两种在ARC有效状况下是相同的
        • //ARARC
          {
            id __strong obj = [[NSObject alloc]init]
          }
          //ARC无效时
          {
            id obj = [[NSObject alloc]init]
            [obj release]
          }
          //ARC无效时执行release操做
        • __strong修饰符表示对对象的强引用,持有强引用的变量在超出其做用域时被废弃,它强引用的对象也会被释放指针

        • 含有__strong修饰的变量,不单单在做用域上,在赋值上也能正确管理对象的生命周期code

      • __weak对象

        • __strong全部权修饰符大部分状况下能够完美的进行内润管理,可是引用计数式内存管理方法必然会遇到循环引用,这时候就须要使用__weak来解决
        • 循环引用会形成内存泄漏,所谓内存泄漏就是应当被废弃的对象在超出其生存周期(做用域)后继续存在
        • __weak修饰的变量不持有对象,原对象在超出其做用域时会被当即释放
        • 经过检查__weak修饰符的变量是否为nil,能够判断被赋值的对象是否已经被释放
      • __unsafe_unretained生命周期

        • 这是不安全的全部权修饰符
        • 附有__unsafe_unretained的变量不属于编译器的内存管理对象
        • 若是对象已经被释放,可是__unsafe_unretained修饰的变量仍然访问了原对象的内存,那么这个变量就是个悬垂指针,错误访问,大几率会引发程序的崩溃
      • __autoreleasing内存

        • 在ARC有效时,用@autoreleasepool块代替NSAutoreleasePool类,用附有__autoreleasing修饰符的变量来代替autorelease方法
        • 编译器会自动检查方法名是否为alloc/new/copy/mutablecopy开头,若是不是的话则自动将返回值的对象注册到autoreleasepool中(init方法返回值的对象也不注册到autoreleasepool中)
    • 属性声明的属性修饰符与全部权修饰符之间的关系

      • 属性修饰符 _unsafe_unretained
        assign _unsafe_unretained
        copy _strong(指针变量指向的是新的被复制的对象)
        retain _strong
        strong _strong
        unsafe_unretained _unsafe_unretained
        weak _weak
      • 为属性添加各类修饰符就至关于给变量添加各类对应的全部权修饰符

      • @property (strong) id obj;
        --------------------------
        id __strong obj = [[NSObject alloc]init]
    • ARC的规则

      • 不能使用retain/retainCount/release/autoRelease
      • 不能使用NSAllocateObject和NSDeallocateObject
      • 遵照内存管理的方法命名规则
      • 不要显式的调用dealloc
      • 使用@autoreleasepool代替NSAutoreleasePool
      • 不能使用NSZone
      • 对象型变量不能做为c语言结构体的成员
      • 显式转换"id"和"void *"
  2. Block

    • block是什么

      • //一句话归纳,block是带有自动变量的匿名函数
        ^(int param){
          	NSLog(@"%d",param);
        }
        -------------------------------------
        //上面的为简写的,完整的block形式为
        //^返回值类型 (参数列表){表达式}
        ^void (int param){
          	NSLog(@"%d",param);
        }
        //完整形式的block语法与C语言函数定义相比,仅有两点不一样
        //1.没有函数名---没有函数名由于它是匿名函数
        //2.带有"^"符号---^是插入记号,方便查找
      • block的返回值类型能够省略,省略返回值类型时

        • 若是表达式中有return语句,那么返回值类型就和return的相同
        • 没有return,返回值类型就是void
        • 有多个return语句时,全部return的类型必须相同
      • block的参数类型也能够省略

        • ^(void)(void){expression}
          -------------------------
          ^{expression}
      • //C语言的函数指针的写法
        int func(int a){
          printf("%d",a);
        }
        int (*funcptr)(int) = &func	//将func函数的地址赋值给函数指针funcptr
          
        //block的写法
        int (^blk)(int a);	//仅仅是将c语言函数指针的"*"更换成了"^"
      • block类型变量与c语言变量的用法彻底相同,能够做为如下用途使用

        • 自动变量(局部变量)
        • 函数参数
        • 静态变量
        • 静态全局变量
        • 全局变量
      • block变量的使用

        • //将block赋值给block变量
          int (^blk)(int) = ^(int){};
          //将block变量赋值给block变量
          int (^blk1)(int) = blk;
          //两个block变量互相赋值
          int (^blk2)(int);
          blk2 = blk1;
        • //在函数参数中使用block类型变量能够向函数传递block
          void func((int)(^blk)(int));
          //在函数返回值中使用block能够将返回值指定为block
          void func(){
            return ^{return;};
          }
        • //使用typedef定义block
          typedef (int)(^blk)(int);	//定义一个block,后面该block的类型就为blk
          //经过block类型变量调用block与在c语言中一般的函数执行没什么区别
          blk func(blk block,int rate){
          	return blk(rate);
          }
        • //这里blk截获的是Array对象的实例指针,经过这个实例指针调用该对象的方法是彻底没问题的,可是若是向Array指针赋值的话就会编译错误(能够用__block解决)
          id Array = [NSMutableArray new];
          void (^blk)(void) = ^{
            id obj = [NSObject new];
            [Array addObject:obj];
          };
        • 在block中若是须要改变被截获的外部变量的值,可使用__block说明符(__block存储域类说明符)来解决

        • block代码转换为cpp代码分析(待完善)

          //block对外部变量捕获的原理,使用cpp代码查看
          //这里写一个block捕获外部变量val的值
          int val = 10;
          void (^blk)(void) = ^{
            printf("%d",val);
          };
          //block本质也是一个OC的对象,oc对象都是结构体,其中含有指向父类的isa指针
          struct __main_block_impl_0 {
            struct __block_impl impl;
            struct __main_block_desc_0* Desc;
            int val;
            __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0) : val(_val) {
              impl.isa = &_NSConcreteStackBlock;
              impl.Flags = flags;
              impl.FuncPtr = fp;
              Desc = desc;
            }
          };
          //这里使用clang -rewrite-objc将.m代码转换成.cpp代码,这里的_cself是block的自身的结构体指针,能够看到在block的函数中是将val从新建立了一个变量进行输出
          static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
            int val = __cself->val; // bound by copy
            printf("%d",val);
          }
        • 三种block的形式

          1. NSConcreteGlobalBlock(全局Block,存放在全局区[数据区])

            • 记录全局变量的地方有Block语法时
            • Block语法的表达式中不使用应截获的自动变量时
          2. NSConcreteStackBlock(栈Block)

            • 除了1.中的两种状况生成的Block,其余使用Block语法产生的Block都是栈Block
          3. NSConcreteMallocBlock(堆Block)

            • 配置在全局区的block在变量做用域外也能够经过指针安全的访问,可是配置在栈上的block一旦其做用域结束就会被系统回收

            • Block提供了将Block和__Block变量复制到堆上的方法来解决这个问题

            • 复制到堆上的block将类对象NSMallocBlock赋值给isa指针

              struct __main_block_impl_0 {
                struct __block_impl impl;
                struct __main_block_desc_0* Desc;
                __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0) : val(_val) {
                  //注意这里就是讲isa指针指向堆Block
                  impl.isa = &_NSConcreteStackBlock;
                }
              };
相关文章
相关标签/搜索