自动内存管理(ARC)

来源:互联网 发布:买口红热 知乎 编辑:程序博客网 时间:2024/06/01 07:52


/*

  1.自动释放池的原理.

 

    存入到自动释放池中的对象,在自动释放池被销毁的时候.会自动调用存储在该自动释放池中的所有对象的release方法.

 

    可以解决的问题:

    将创建的对象,存入到自动释放池之中.就不再需要手动的relase这个对象了.

    因为池子销毁的时候就会自动的调用池中所有的对象的relase

 

 

  2.如何创建自动释放池.

 

     @autoreleasepool

     {

 

     }

 

    这对大括弧代表这个自动释放池的范围.

 

  3. 如何将对象存储到自动释放池之中

    

    在自动释放池之中调用对象的autorelease方法.就会将这个对象存入到当前自动释放池之中.

 

    这个autorealse方法返回的是对象本身.所以,我们可以这么写

       

     @autoreleasepool

     {

        Person *p1 = [[[Person alloc] init]autorelease];

     }

 

    这个时候,当这个自动释放池执行完毕之后,就会立即为这个自动释放池中的对象发送1release消息.

 

    目前为止,我们感受到得autorelase的好处:

    创建对象,调用对象的autorelase方法将这个对象存入到当前的自动释放池之中.

 

    我们就不需要再去relase因为自动释放池销毁的时候就会自动的调研池中所有对象的relase

 

  4. 使用注意

 

     1).只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池之中.

        如果只是将对象的创建代码写在自动释放之中,而没有调用对象的autorelease方法.是不会将这个对象存储到这个自动释放池之中的.

 

     2).对象的创建可以在自动释放池的外面,在自动释放池之中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池之中.

 

     3).如果对象的autorelease方法的调用放在自动释放池的外面,是无法将其存储的这个自动释放池之中的.

         autorelease的调用只有放在自动释放池之中才可以讲其存储到自动释放池之中,对象的创建可以在外面

 

 

     4).当自动释放池结束的时候.仅仅是对存储在自动释放池中的对象发送1release消息而不是销毁对象.

 

     5).如果在自动释放池中,调用同1个对象的autorelease方法多次.就会将对象存储多次到自动释放池之中.

        在自动释放池结束的时候.会为对象发送多条release消息.

        所以,1个自动释放池之中,autorelease1,只将这个对象放1,否则就会出现野指针错误.

 

     6).如果在自动释放池中,调用了存储到自动释放中的对象的release方法.

        在自动释放池结束的时候,还会再调用对象的release方法.

        这个时候就有有可能会造成野指针操作.

 

     7).将对象存储到自动释放池,并不会使对象的引用计数器+1所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release.

 

     8).自动释放池可以嵌套.

         调用对象的autorelease方法,会讲对象加入到当前自动释放池之中

         只有在当前自动释放池结束的时候才会像对象发送release消息.

 

 5. autorelease的应用场景.

     0).创建对象,将对象存储到自动释放池之中.就不需要再去手动的realse

 

   

     1).我们一般情况下,会为我们的类写1个类方法,用来让外界调用类方法来快速的得到1个对象.

        规范:使用类方法得到的对象,要求这个对象就已经被autorelease过了.

       

        提供1个类方法来快速的得到1个对象.

        规范

         a.这个类方法以类名开头.如果没有参数就直接是类名如果有参数就是类名WithXX:

         b.使用类方法得到的对象,要求这个对象就已经被autorelease过了.

 

         + (Person *)person

         {

            return [[[Person alloc] init]autorelease];

         }

 

        这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.

         @autoreleasepool

         {

             Person *p1 = [Person person];

             //这个p1对象已经被autorelase过了.不需要再调用autorelase

             //这个p1对象就被存储到当前自动释放池之中.

         }//当自动释放池结束.就会为存储在其中的p1对象发送release消息.

 

 

     2).完善类方法.

        Person拥有1个子类Student.这个Student子类也继承了父类的person类方法.

        但是.通过子类Student调用person类方法能创建1个子类Student对象吗?

        答案是否定的,因为person方法写死了,我们返回的就是1Person对象.

 

        我们的需求:

        通过父类Person调用这个类方法,返回的是Person对象,通过子类Student调用这个person类方法返回的是Student对象.

         a.类方法person的返回值肯定不能写Person *.我们首先想到了使用万能指针id.

         b.类方法的内部代码:  

            return [[[Person alloc] init]autorelease];

           就不能这样写了,因为这样仍然是创建Person对象.

 

           改成.

            return [[[self alloc] init]autorelease];

           在类方法中self代表当前类.通过那1个类调用这个方法self就是指的那1个类.

           所以,这个时候,通过Person类调用person方法 self就指Person.创建的就是Person对象.

           通过Student来调用person方法,self就指Student创建的就是Student对象.

           多爽啊!

 

        代码改成如下:

         + (id)person

         {

            return [[[self alloc] init]autorelease];

         }

 

        这样貌似就可以了完美.

 

     3).继续完善类方法.

 

        上面写的方法,其实已经可以实现我们的需求了.

        但是存在1个小瑕疵.

       

        类方法person返回的类型是1id类型.

         id类型是1个无类型的指针.其优点在于可以指向任何对象,编译器都不会报错.并且编译器不会做编译检查.

 

        所以,我们在创建对象的时候:

         Person *p1 = [Personperson];这样是没有问题的因为创建的是Person对象赋值给1Person指针.

         Student *s1 =[Student person];这样也是没有问题因为创建的是Student对象,赋值给1Student指针.

 

        但是,各位,下面这样写:

         NSString *str = [Person person];

        你会发现这样是不出错的.编译器根本都不会警告.实际也是可以运行的.

        原因在于person类方法返回的是1个无类型的指针.

 

        所以这样明显是不允许的,但是编译器却步报错,甚至警告都不报.

        为了解决这个问题.方法的返回值我们使用instancetype

 

         instancetype只能作为方法的返回值.代表返回的是当前类的对象.

        这是1个有类型的指针.

        所以当你将其赋值给1个类型不同的指针变量时编译器就会报警告.

       

         + (instancetype)person

         {

            return [[[self alloc] init]autorelease];

         }

 

 6.实际上Apple的框架中的类也是遵守这个规范的.

 

   通过类方法创建的对象都是已经被autorelease过的了.

 

   所以,我们也要遵守这个规范.类方法返回的对象也要被autorealse.

 

   以后,我们凡事创建对象是调用类方法创建的对象这个对象已经是被autorelease过的了.

 

 

 */

 

/*

 

 1.什么是ARC

   

    Automatic ReferenceCounting,自动引用计数.ARC.

   顾名思义:系统自动的帮助我们去计算对象的引用计数器的值,

 

   可以说是WWDC2011iOS5引入的最大的变革和最激动人心的变化.

    ARC是新的LLVM3.0编译器的一项特性,使用ARC,可以说一举解决了广大iOS开着所憎恨的手动管理内存的麻烦.

 

   在程序中使用ARC非常简单,只需要像往常那样编写代码.

   只不过永远不要写retainreleaseautorelease这三个关键字就好,这是ARC的最基本的原则.

   ARC开启时,编译器会自动的在合适的地方插入retainreleaseautorelase代码.

   编译器自动为对象做引用计数.而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用了ARC).

 

   需要特别注意的是: ARC是编译器机制.在编译器编译代码的时候,会在适时的位置加入retainreleaseautorealse代码.

 

 2. ARC机制下,对象何时被释放

 

   只要没有强指针指向这个对象,这个对象就会立即回收.

 

 3.强指针与弱指针.

 

   强指针:默认情况下,我们声明1个指针这个指针就是1个强指针.

          我们也可以使用__strong来显示的声明这是1个强指针.

           

           Person *p1;这是1个强指针.指针默认情况下都是1个强指针.

           __strong Person*p2;这也是1个强指针.使用__strong来显示的声明强指针.

 

   弱指针:使用__weak标识的指针就叫做弱指针.

 

          

   无论是强指针还是弱指针,都是指针,都可以用来存储地址,1点没有任何区别

   都可以通过这个指针访问对象的成员.

   唯一的区别就是在ARC模式下.他们用来作为回收对象的基准.

   

   如果1个对象没有任何强类型的指针指向这个对象的时候,对象就会被自动释放

 

 4.确认程序是否开启ARC机制.

 

    1).默认情况下,Xcode开启ARC机制.

    2).ARC机制下,不允许调用retainrelaseretainCountautorelease方法.

    3).dealloc不允许[superdealloc];

 

 

 5. 演示第1ARC案例

     int main(int argc, const char * argv[])

        {

            @autoreleasepool

            {

               Person *p1 = [Person new];//p11个强指针.

               //因为我们说过,1个指针变量默认情况下都是1个强指针变量.

               NSLog(@"------");

            }//当执行到这里的时候.p1指针被回收,那么Person对象就没有任何

            //强指针指向它了.对象就在这被回收.

            return 0;

     }

 

 6. ARC下的单个对象的内存管理.

 

    1).当指向对象的所有的强指针被回收的时候,对象就会被立即回收.

 

     int main(int argc, const char * argv[])

     {

         @autoreleasepool

         {

            Person *p1 = [Person new];//p11个强指针.

             Person *p2 =p1;//p2也是个强指针.p1p2都指向Person对象.

            //因为我们说过,1个指针变量默认情况下都是1个强指针变量.

            NSLog(@"------");

         }//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何

         //强指针指向它了.对象就在这被回收.

         return 0;

     }

 

    2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.

       

     int main(int argc, const char * argv[])

     {

         @autoreleasepool

         {

            Person *p1 = [Person new];//p11个强指针.

            //因为我们说过,1个指针变量默认情况下都是1个强指针变量.

             

            p1 = nil;//当执行到这句话的时候.p1赋值为nil.

            //p1指针不再执行Person对象.

            //Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.

            NSLog(@"------");

         }

         return 0;

     }

 

 

 

 7.强指针与弱指针.

   

    1).强指针与弱指针的声明.

 

    p1指针是强类型的,因为默认情况下指针都是强类型的.

    Person*p1 = [[Person alloc] init];

   不过我们可以使用__strong来显示的标识指针是强类型指针.

   __strong Person *p2 = [Person new];

   这个时候p2指针类型是强指针类型的.其实写不写__strong都是强类型的指针.

       

   指针类型也可以是弱指针类型.

   使用__weak标识指针的类型是弱类型指针.

    __weakPerson *p3 = p2;

   这个时候,p3指针就是1个弱类型的指针. p3弱指针也指向p2指针指向的对象.

 

   在操作对象的时候,通过强指针或者弱指针都可以操作,没有任何区别.

 

    2). ARC的对象回收标准

 

    ARC机制下释放1个对象的标准是:没有任何强指针指向对象的时候,对象就会被释放.

   如果这个时候有弱指针指向,也会被释放.

    int main(int argc, const char * argv[])

    {

            @autoreleasepool

            {

               //使用__strong来标识p1指针是强类型的,其实不写__strong也是强类型的.

                __strongPerson *p1 = [[Person alloc] init];

                

               //使用__weak标识指针p2的类型是弱类型指针.

                __weakPerson *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个弱指针

               //后果就是创建出来的这个对象没有被任何强指针指向.

               //刚创建出来就会被释放.

                __weakPerson *p1 = [[Person alloc] init];

                

            }

            return 0;

     }

 

 

 

 */

 

 

/*

 1. ARC下的单个对象的内存管理.

 

     1).当指向对象的所有的强指针被回收的时候,对象就会被立即回收.

 

     int main(int argc, const char * argv[])

     {

         @autoreleasepool

         {

           Person *p1 = [Person new];//p11个强指针.

            Person *p2 =p1;//p2也是个强指针.p1p2都指向Person对象.

           //因为我们说过,1个指针变量默认情况下都是1个强指针变量.

            NSLog(@"------");

         }//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何

         //强指针指向它了.对象就在这被回收.

         return 0;

     }

 

     2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.

    

     int main(int argc, const char * argv[])

     {

         @autoreleasepool

         {

            Person *p1 = [Person new];//p11个强指针.

            //因为我们说过,1个指针变量默认情况下都是1个强指针变量.

             

            p1 = nil;//当执行到这句话的时候.p1赋值为nil.

            //p1指针不再执行Person对象.

            //Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.

            NSLog(@"------");

         }

         return 0;

     }

 

 

 

 7.强指针与弱指针.

    

    1).强指针与弱指针的声明.

   

     默认情况下,所有的指针都是强类型的,也就是说我们之前声明的指针变量都是强类类型的

 

     p1指针是强类型的,因为默认情况下指针都是强类型的.

     Person *p1 = [[Person alloc]init];

 

    不过我们可以使用__strong来显示的标识指针是强类型指针.

     __strong Person *p2 = [Personnew];

    这个时候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也是强类型的.

             __strongPerson *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;

     }

 

 

 

 

 

 */

 

 

 

/*

 

  0.ARC机制下,set方法的封装不需要retainrelase

    直接赋值就可以了.

 

 

 

  1.当对象的属性是另外1个属性的时候,这个属性应该是1个强指针还是弱指针?

 

    肯定需要是1个强指针.

    1个强指针指向1个对象的时候.

    说明我在用这个对象,这个对象就不能被销毁.

 

    当我被销毁的时候我的强指针也被销毁了说明我现在不用你了.

    对象再判断是否还有其他强指针指向它.以决定是否回收.

 

 

 

    说那么多的废话.

    只有1个结论.ARC机制下,如果1个对象的属性是1OC对象类型的这个属性应该声明为1个强指针.

 

 

  2.需要注意的是.如果对象没有被任何强指针指向.那么对象就会被回收.

    原来指向对象的弱指针的值会自动设置为nil

 

 

 

 

 

  3.使用@property如何让生产的属性是1个强类型或者弱类型.

 

    使用参数. strongweak

 

     //@property参数strong代表生成的私有属性_dog指针是1个强类型的指针.

 @property(nonatomic,strong)Dog *dog;

 

 //@property参数weak代表生成的私有属性_name指针是1个弱类型的指针.

 @property(nonatomic,weak)NSString *name;

 

    默认的参数是strong

 

 

  4. @property参数注意.

 

     1).ARC机制下.@property参数retain就无法再使用了.因为retain参数代表的意义是生成的set方法是标准的MRC下的内存管理代码.

       ARC,不需要引用记数.就不需要生成这些内存管理代码了.

 

     2).ARC机制下,属性的类型是OC对象类型的时候,一般使用strong.OC对象的类型使用assign

    

     3).strongweak都是针对OC对象类型的属性,OC对象不能使用strongweak

 

 

 

 

 

 

 

 */

 

 

/*

 

  1.当两个类相互引用作为对方的属性的时候.

    ARC机制下.如果两边的@property都是以strong就会出现循环引用而导致的内存泄露

 

 

  2.解决方案:

    一端使用strong一端使用weak

 

 

 

 

 */

 

/*

 

  1.程序的内存管理模式.分为两种. MRCARC

 

    

  2.与多线程相关的参数.

 

     atomic:默认值

     nonatomic

 

    无论是在ARC还是MRC模式下,都使用nonatomic

 

  3. retain:只能使用在MRC模式下.当属性的类型是1OC对象的时候,除了循环引用之外,全部都是使用retain

 

     assign:既可以用在MRC,也可以用在ARC之中,

            MRC模式中和ARC模式下.当属性的类型是1个非OC对象类型的时候.使用assign

 

    MRC模式下,如果出现循环引用.一端使用retain一端使用assign

 

 

 

   4. strong:只能使用在ARC模式下,当属性的类型是1OC对象的时候,除了循环引用之外,全部都是使用strong

      weak:只能使用在ARC模式下,当出现循环引用的时候.一端使用strong一端使用weak

 

 

   5. readwrite:可以使用在ARC模式和MRC模式下,代表同时生成属性的gettersetter

      readonly:可以使用在ARC模式和MRC模式下代表只成属性的getter

 

 

   6.修改生成的getter setter的名字:ARCMRC中都可以使用.

 

 

 ---------------------

 

 @property参数:MRC中使用retainstrong来代替就可以了.

 

 

 

 

 

 */

 

 

/*

  1想象这样1个画面:

   

 

   你要使用别人的类,但是别人的类是在MRC模式下得.而你的程序是ARC模式.

 

 

  2.ARC的程序中,有一些类使用MRC实现的.

 

    如果希望某些指定的类还是使用MRC.那么这个时候可以在

    点击项目 ->选中要改的Targer -> Bulid Phases -> ComplileSource

 

    在这里双击要修改模式的类.

    输入: -fno-objc-arc

 

    就代表这个类使用的是MRC

 

 

 

 

 */

 

/*

 

 转换的方式非常简单和粗暴:

 

   就是简单的替换:

      删除所有的 retain relase autorease [super dealloc]; retain strong

 

 

  所以是有可能会出错得.

 

 

 

 

 */

0 0
原创粉丝点击