• IOS5 ARC(自动引用计数器) 一些属性介绍


    自从引进了iOS5,iPhone,iPad,iPodTouch编程改变了很多。整个runtime和objective-c的编码方式发生了戏剧性的改变。ARC(Automatic Reference Counting - 自动引用计数)技术现在引入到LLVM编译器(LLVM Compiler)中,在某些方面,使程序更加灵活,另一方面使runtime更加脆弱。 在这章中,我们将谈论,在objective-c runtime ARC下,如何使用对象,以及怎么管理它们。
    从手工引用计数到自动引用计数(ARC)
              Problem         
            学习Automatic Reference Counting(自动引用计数器),苹果的新的编译器解决了令人头痛的问题:在Objective-C中,当使用对象的时候,处理对象,以及管理内存。
             
              Solution
              由最新的LLVM编译器引进的新的属性存储技术:strong, weak,和unsafe_unretained

              Discussion
              在最新的LLVM编译器中,是使用了Automatic Reference Counting(ARC),我们需要处理strong,weak,或者unsafe和unretained存储技术。在ARC模式之下的对象,是由 :
        strong:这种类型的对象在run-time的时候自动增加引用计数,并在它的范围结束之前有效,之外就会被自动释放【will be valid until the end of its scope, where it will automatically be released】 。熟悉Objective-C传统的内存管理办法,这个关键字和retain关键字相似。

        weak:这是 “归零弱引用 - zeroing weak referencing”。如果变量使用这个关键字定义,当对象(变量指向的内存)被销毁了,这个变量被设置为nil。例如,你有一个strong的string属性和一个weak的string属性,并将weak属性的值设置为strongoing属性的值,当strong属性被销毁了,那么weak属性就会被设置为nil。
     
        unsafe_unretained:简单的将一个变量指向另一个变量。设置对象的新数值,不会增加它的引用计数,它只是简单的为变量分配了一个对象。
     
              默认,所有的局部变量(local variables)是strong变量。相反,属性必须明确指定它们的存储属性( storage attribute )。换句话,除了存储属性(a storage attribute,默认的是 strong properties),编译器不会承担所有属性的管理。所以,你需要确定将你的属性指定为存储属性(a storage attribute)。让我们看一看strong属性的例子。
                        #import <UIKit/UIKit.h>
                        @interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate : UIResponder <UIApplicationDelegate>
                        @property (strong, nonatomic) UIWindow *window;
                        @property (nonatomic, strong) NSString *string1;
                        @property (nonatomic, strong) NSString *string2;
                        @end
     
                        如果使用@"String 1"初始化string1,并将string1的属性值分配给string2,那么有着存储属性(storage attribute)的string2属性将会保存string1的值,即使在string1销毁后,string2仍然保存@"String 1"值。
                        #import "Moving_from_Manual_Reference_Counting_to_ARCAppDelegate.h"
                         @implementation Moving_from_Manual_Reference_Counting_to_ARCAppDelegate
                        @synthesize window = _window;
                        @synthesize string1;
                        @synthesize string2;
                        - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
                                       self.string1 = @"String 1";
                                       self.string2 = self.string1; self.string1 = nil;
                                       NSLog(@"String 2 = %@", self.string2);
                                       self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
                                       self.window.backgroundColor = [UIColor whiteColor];
                                       [self.window makeKeyAndVisible];
                                       return YES;
                        }

              输出为:String 2 = String 1

              在声明属性(declaring properties)的时候strong, weak, unsafe_unretained会经常被用到。即使在声明局部变量的时候你也可以使用这些存储说明符(storage specifiers),但是你需要对说明符做一点修改。strong等效的嵌入说明符为__strong,weak等效的嵌入说明符为__weak,unsafe_unretained等效的嵌入说明符为__unsafe_unretained。(记住这些关键字是以2个下划线开头),例子如下:
                        - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
                        /* All local variables are by default strong so just emphasis that.
                              We really don't have to mention __strong for the first variable but to make it clear,
                              we will set it. No harm in doing so. */
                        __strong NSString *yourString = @"Your String";
                        __weak NSString *myString = yourString;
                        yourString = nil;
                        __unsafe_unretained NSString *theirString = myString;

                        /* All pointers will be nil at this time */
                        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
                        self.window.backgroundColor = [UIColor whiteColor];
                        [self.window makeKeyAndVisible];
                        return YES;
                        }

               当程序员声明的object被销毁了,这时候要解决很多问题,但是,ARC中的“归零弱引用 - zeroing weak referencing”很有用,帮助程序员解决这种情况下的很多问题。weak引用:当指针指向的对象被销毁了,那么weak引用的对象(a zeroing reference's object)会被设置为nil。所有指向被回收对象的weak引用(the weak referencing pointers),都会被设置为nil。
              unsafe_unretained存储说明符是真正安全的,就像它的名字寓意。它安全的原因是:当unsafe_unretained变量指向的对象(object)被回收销毁了,这个变量会被设置为nil,并且将会指向内存中的a dangling location。访问这个地址可能引起程序的崩溃。为了避免这种情况,你应该使用“归零弱引用存储说明符 - zeroing weak referencing storage specifier”,weak或者内嵌(inline equivalent)说明符。
              我们看一下“归零弱引用存储说明符 - zeroing weak referencing storage specifier”的例子:
                        #import <UIKit/UIKit.h>
                        @interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate : UIResponder <UIApplicationDelegate>
                        @property (strong, nonatomic) UIWindow *window;
                        @property (nonatomic, strong) NSString *string1;
                        @property (nonatomic, weak) NSString *string2;
                        @end

              如果程序第一次运行,我们将会初始化strong类的string1属性,然后将string1分配给string2.然后我们将string1的值设为nil。然后我们等待,这绝对是至关重要的。如果我们在将string1设置为nil之后立即答应string2的值,可能你会得到不正确的结果,而不是nil。所以,你需要确保你的应用程序的runloop除去所有无效的对象。为了实现这个目标,我们在应用程序将要进入后台的时候,我们将打印string2的值。(这是在用户将另一个应用程序移到前台时,你的应用程序进入后台)。一旦程序在后台运行,我们知道runloop已经除去了内存中所有无效对象,并且我们得到的结果将是正确的:
                        /* 3 */
                        - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    self.string1 = [[NSString alloc] initWithUTF8String:"String 1"];
                                  self.string2 = self.string1; self.string1 = nil;

                                  /* All pointers will be nil at this time */
                                  self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
                                  self.window.backgroundColor = [UIColor whiteColor];
                                  [self.window makeKeyAndVisible];
                                  return YES;
                        }
                        - (void)applicationDidEnterBackground:(UIApplication *)application{
                                                      NSLog(@"String 2 = %@", self.string2);
                        }
              现在运行程序,等到1-2秒钟后,点击Home按钮。你将会注意到接下来的结果将会被打印到控制台中:
                                  String 2 = (null)

              这很容易证明ARC的“归零弱引用 - zeroing weak referencing”,实现的很完美。现在检查unsafe_unretained存储说明符是多么的危险,代码例子:
                        #import <UIKit/UIKit.h>
                        @interface Moving_from_Manual_Reference_Counting_to_ARCAppDelegate : UIResponder <UIApplicationDelegate>
                        @property (strong, nonatomic) UIWindow *window;
                        @property (nonatomic, strong) NSString *string1;
                        @property (nonatomic, unsafe_unretained) NSString *string2;
                        @end
             AppDelegate的实现部分,就像我们之前实现的一样,在程序进入后台的时候打印string2的值,并重复打开和最小化程序的操作,程序和crash!这就意味但我们的应用程序被送到后台的时候,我们试图打印内存中无效对象(string2指向的对象)的内容。既然,string2属性不安全并且 unretained(没有retain),所以string2不知道它指向的对象(in string1),当string1被设置为nil,已经被回收销毁。
              除了,上述的3个存储说明符(storage specifiers),我们还可以使用__autoreleasing说明符。当我们想要向方法传递对象的引用的时候,这个存储说明符(storage specifiers)是最方便的。例如,如果方法需要向调用方法(the caller method),传递一个NSError的error对象,方法将会向调用方法(the caller method)传递一个未初始化并且未分配的NSError实例对象。这就意味着调用者不需要分配这个error对象,所以,我们的方法就应该这样做。为了实现这个,error参数需要是自动释放对象,在合适的时间点的时候,通过runtime释放。
                        - (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{ //the caller method,声明参数的类型是自动释放的类型
                                  NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
                                  NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
                                  NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
                                  *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
                        }

                        - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
                                  NSError *error = nil;
                                  [self generateErrorInVariable:&error];
                                  NSLog(@"Error = %@", error);
                                  self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
                                  self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
                                  return YES;
                        }
     
  • 相关阅读:
    idea配置tomcat运行按钮置灰,下拉没有自定义的tomcat选项
    配置多版本jdk,自由切换jdk版本
    五年经验程序员告诉你,如何确定自己是否适合做程序员
    你的编程能力从什么时候开始突飞猛进?
    10 个提升效率的Linux小技巧
    8 种经常被忽视的 SQL 错误用法,你有没有踩过坑?
    十大优秀编程项目,让你的简历金光闪闪
    一文掌握 Lambda 表达式
    一文详解微服务架构(一)
    Java的参数传递是「按值传递」还是「按引用传递」?
  • 原文地址:https://www.cnblogs.com/hopeanCom/p/2747608.html
Copyright © 2020-2023  润新知