iPhone开发学习-从手动计数到Automatic Reference Counting
在最新的LLVM编译器中,用ARC,我们将要面对新的存储特性: strong, weak, 和 unsafe_unretained,每个在ARC下的对象都要遵循这些新特性,这里给出他们的简单介绍。
strong
一个 strong类型的对象会在运行时自动retained,并且将会在他的整个生命周期里有效直到该对象被自动的释放。如同其他的Objective-c普通内存管理方法一样,该关键字与retain方法相似。
weak
一种简单的弱关联(zeroing weak referencing),如果一个变量被定义为weak,当该变量指针被dealloc,该变量将会被赋值nil。例如,如果你有一个strong类型的string成员和一个weak类型的string成员,同时将strong类型成员的值赋给weak类型string(指针传递,浅拷贝),当strong类型的string值dealloc时,weak类型的成员取值时会返回nil。
unsafe_unretained
这是一个简单的方法将一个变量指向其他变量。这样并不会retain当前的对象,只是简单的assign当前对象。一般默认的,所有的变量都是strong类型。作为对比,成员变量必须明确的指出他们的类型。也就是说,编译器不会多管闲事的认为所有的成员变量默认为strong属性。假设我们有两个成员变量 string1和string2:
#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,并且将其赋值给成员 string2,我们会看到strong的类型的string2在string1 dealloc后仍然能够保持他的值:
#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.windowmakeKeyAndVisible]; return YES; }
输出结果为:String 2 = String 1
strong,weak, 和 unsafe_unretained 频繁的用于声明成员属性,你可以应用这些新的关键字来声明本地成员,但是要明确的区分他们的不同。这些关键字的内联等同于“__关键字”如__strong,__weak,__unsafe_unretained.(注意这些关键字前面都有两个下划线)下面是例子:
- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* All local variables are by default strong so just emphasis that. Wereally don't have to mention __strong for the first variable but to make itclear, 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; }
在 ARC下的这种置零弱关联(zeroing weak referencing),是一种神奇的方式解决了开发人员不知他们的对象在什么时刻释放的问题。而且弱关联也使得对象能够在它所指向的对象被释放后被置为nil。这种释放也会使得与被释放对象相关联指向的对象都被置为nil。
unsafe_unretained类型如它的名字所隐含的那样,是真是的不安全。不安全的原因在于unsafe_unretained类型的变量所指向的对象一旦被释放,那么该变量不会被置为nil并且将指向一个未知的内存。操作这样的变量可能会导致程序crash。为了防止这样的情况发生,建议将成员声明成弱关联类型(即weak类型),weak或者内联型的__weak。
下面是弱关联例子,改变string2成员的为weak来替代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, weak)NSString *string2; @end
当程序开始运行,初始化strong类型的成员string1,并且将其赋值给string2。将string1赋值为nil,进行等待,等待是必须的。如果在赋值string1为nil时立即输出string2的结果的话,你将会看到输出错误而不是nil。所以,如果为了确保程序的runloop已经完全的清除了无效的对象,建议等到程序被切入后台的时候输出string2的值。一旦程序切入后台,就可以确定程序的runloop已经完全清除了内存中的无效对象,我们看到的结果将会如下:
- (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.string1 = [[NSString alloc] initWithUTF8String:"String1"]; 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的弱关联机制(weak)。下面来研究unsafe_unretained类型的危险性,依旧是上面的代码,如我们之前将strong设置为weak一样,将string2成员的类型由weak设置为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
如之前一样运行我们的程序,当把程序切入后台后,你会发现程序会crash。这意味着当程序切入后台后,我们在尝试输出一个string2指向的无效内存地址。当string2类型被设置为unsaf_unretained时,它并不知道它所指向的(string1)已经被赋值为nil释放。
为了添加提到的这些新的类型,我们可以使用__autoreleasing关键字。这个关键字用来做对象关联方法传递的处理。例如,如果一个方法需要将error或者NSError传递给调用者,调用者方法将会返回一个未初始化的NSError实例到这个方法。这意味调用者不会真正的分配error,而需要我们的方法去做。为了达到目的,你必须指定该 error 参数需要在其运行时中合适的时间被自动释放:
- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError { NSArray *objects = [[NSArrayalloc] 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:1userInfo: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.windowmakeKeyAndVisible]; return YES; }
这个例子中, application:didFinishLaunchingWithOptions: 方法没有分配一个实例NSError;用generateErrorInVariable方法来实现。但是必须让编译器知道这个error对象的存在。创建的被编译器所提及的、含有error参数的generateErrorInVariable方法,如果不再使用的话将会被被自动释放。