iOS中使用全局变量,无外乎以下几种方式:
单例
static
extern 下面我们一一介绍。
方式一:AppDelegate.h文件中设置属性充当全局变量 AppDelegate.h1 2 3 4 5 6 7 8 9 10 #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate >@property (strong , nonatomic ) NSString *currentVCName;@property (strong , nonatomic ) UIWindow *window;@end
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #import "ViewController.h" #import "AppDelegate.h" @interface ViewController ()@end @implementation ViewController - (void )viewDidLoad { [super viewDidLoad]; AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate]; myDelegate.currentVCName = NSStringFromClass ([self class]); NSLog (@"Value of global var 'currentVCName' in AppDelegate is : %@" ,myDelegate.currentVCName ); } @end
方式二:自定义单例Class的.h文件中设置属性充当全局变量 方式一、二原理都是一样的:通过单例模式的使用,达到全局变量的使用目的。
AccountManager.h
1 2 3 4 5 6 7 8 9 10 #import <Foundation/Foundation.h> @interface AccountManager : NSObject @property (strong , nonatomic ) NSString *currentAccountName;+ (instancetype)sharedInstance; @end
AccountManager.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #import "AccountManager.h" static AccountManager *_sharedInstance = nil ;@implementation AccountManager + (instancetype)sharedInstance { if (_sharedInstance == nil ) { _sharedInstance = [[self alloc] init]; } return _sharedInstance; } @end
ViewController.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #import "ViewController.h" #import "AppDelegate.h" #import "AccountManager.h" @interface ViewController ()@end @implementation ViewController - (void )viewDidLoad { [super viewDidLoad]; AccountManager *accountManager = [AccountManager sharedInstance]; accountManager.currentAccountName = @"LYL" ; NSLog (@"Value of global var 'currentVCName' in custom singleton - AccountManager is : %@" ,accountManager.currentAccountName ); } @end
补充知识:单例 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
单例模式这种特性,可以广泛应用于某些需要全局共享的资源中,比如管理类,引擎类,也可以通过单例来实现传值。UIApplication、NSUserDefaults等都是IOS中的系统单例。我们方式一、二正是利用这种特性,达到全局变量的使用目的。
上例中单例的写法没有考虑多线程问题,当多个线程同时调用sharedInstance时,可能会产生多个实例。我们可以测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (void )viewDidLoad { [super viewDidLoad]; for (NSUInteger i = 0 ; i <10 ; i++) { AccountManager *accountManager = [AccountManager sharedInstance]; NSLog (@"第%ld次获取AccountManager对象内存地址----%@" ,i+1 ,accountManager); } dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ); for (NSUInteger i = 0 ; i <10 ; i++) { dispatch_async (queue, ^{ AccountManager *accountManager = [AccountManager sharedInstance]; NSLog (@"第%ld次获取AccountManager对象内存地址----%@" ,i+1 ,accountManager); }); } }
解决办法也非常简单,加上线程锁就行了。修改AccountManager中sharedInstance方法如下:
1 2 3 4 5 6 7 8 + (id )sharedInstance { @synchronized (self ) { if (_sharedInstance == nil ) { _sharedInstance = [[self alloc] init]; } } return _sharedInstance; }
这种方法虽说没有问题,但现在大家推荐的做法是GCD的dispatch_once方法,据说更加高效:
1 void dispatch_once (dispatch_once_t *predicate, dispatch_block_t block);
我们可以看到这个函数接收一个dispatch_once_t的参数,还有一个块参数。对于一个给定的predicate来说,该函数会保证相关的块必定会执行,而且只执行一次,最重要的是——这个方法是完全线程安全的。需要注意的是,对于只需要执行一次的块来说,传入的predicate必须是完全相同的,所以predicate常常会用static或者global来修饰。
1 2 3 4 5 6 7 8 9 + (instancetype)sharedInstance { static dispatch_once_t once; dispatch_once (&once, ^{ _sharedInstance = [[self alloc] init]; }); return _sharedInstance; }
但是,这并不完整,聪明的你一定想到了,如果在项目中,其它人调用了你写的这个单例,但他可能并不知道这是个单例,他使用时可能会用init来初始化,这就会产生多个实例,这显然不是你想见到的。如何解决?我们要重载任何一个涉及到allocation的方法,这些方法包括+new、+alloc、+allocWithZone:、-copyWithZone:、-mutableCopyWithZone:。同时,由于重写了+alloc方法,因此sharedInstance方法也要修改下.
下面是完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #import "AccountManager.h" static AccountManager *_sharedInstance = nil ;@implementation AccountManager + (instancetype)sharedInstance { static dispatch_once_t once; dispatch_once (&once, ^{ _sharedInstance = [super allocWithZone:nil ]; }); return _sharedInstance; } + (id )alloc { NSLog (@"%@: use +sharedInstance instead of +alloc" , NSStringFromClass ([self class])); return nil ; } + (id )new { return [self alloc]; } + (id )allocWithZone:(NSZone *)zone { return [self alloc]; } - (id )copyWithZone:(NSZone *)zone { NSLog (@"SingletonClass: attempt to -copy may be a bug." ); return self ; } - (id )mutableCopyWithZone:(NSZone *)zone { return [self copyWithZone:zone]; } @end
方式三:static static 关键字会在声明变量的时候分配内存,所以在程序运行期间只会分配一次内存。之后 在程序中访问该变量时,实际上都是在访问原先分配的内存。
使用static修饰的变量,也称私有全局变量,只在该类中可用。和Java不同,Objective-C中声明后的static静态变量在其他类中是不能通过类名直接访问,它的作用域只能是在声明的这个.m文件中 。不过可以调用这个类的方法间接的修改这个静态变量的值。
我们声明static变量,可以放在.h文件的@interface上面:
1 2 3 4 5 static NSInteger staticInteger = 1 ;@interface SataticVarMAnager : NSObject @end
也可以放在.m文件的@implementation上面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #import "SataticVarMAnager.h" static NSString *staticStr = @"AA" ;@implementation SataticVarMAnager - (instancetype)init { if (self = [super init]) { staticStr = @"BB" ; staticInteger = 2 ; NSLog (@"%@---%ld" ,staticStr,staticInteger); [self declareStaticVarInMethord]; [self declareStaticVarInMethord]; } return self ; } - (void )declareStaticVarInMethord { static NSInteger staticInteger2 = 1 ; staticInteger2++; NSLog (@"declareStaticVarInMethord : %ld" ,staticInteger2); } + (void )useStaticVarInClassMethord { NSLog (@"useStaticVarInClassMethord : %@---%ld" ,staticStr,staticInteger); } @end
需要注意的是
static变量也可以在方法体内定义,出方法体乃至出该类的作用域其值依然保留。
类方法中可以使用static变量,不能使用实例变量。
static变量也可以在@implementation内部定义,但一般不这么做。
方式四:extern 还有一种全局变量,如下面代码中的normalInteger,和static变量十分相似,其值出该类的作用域依然保留,且也可以在类方法中调用。感觉就是编译时,自动加了static。
1 2 3 4 static NSString *staticStr = @"AA" ;NSInteger normalInteger = 0 ;@implementation SataticVarMAnager
不同的是: 这种写法不能出现在.h文件中,在其它类中也可以用,其它类中在前面加extern就能使用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #import "ViewController.h" #import "AppDelegate.h" #import "AccountManager.h" #import "SataticVarMAnager.h" extern NSInteger globalInteger;@interface ViewController ()@end @implementation ViewController - (void )viewDidLoad { [super viewDidLoad]; globalInteger = 3 ; SataticVarMAnager *sta = [[SataticVarMAnager alloc] init]; SataticVarMAnager *sta2 = [[SataticVarMAnager alloc] init]; } @end
SataticVarMAnager.h
1 2 3 4 5 6 7 #import <Foundation/Foundation.h> static NSInteger staticInteger = 1 ;@interface SataticVarMAnager : NSObject @end
SataticVarMAnager.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #import "SataticVarMAnager.h" static NSString *staticStr = @"AA" ;NSInteger globalInteger = 1 ;@implementation SataticVarMAnager - (instancetype)init { if (self = [super init]) { staticStr = @"BB" ; staticInteger = 2 ; NSLog (@"%@---%ld---%ld" ,staticStr,staticInteger,globalInteger); [self declareStaticVarInMethord]; [self declareStaticVarInMethord]; } return self ; } - (void )declareStaticVarInMethord { static NSInteger staticInteger2 = 1 ; staticInteger2++; globalInteger++; NSLog (@"declareStaticVarInMethord : %ld,----%ld" ,staticInteger2,globalInteger); } + (void )useStaticVarInClassMethord { globalInteger ++; NSLog (@"useStaticVarInClassMethord : %@---%ld" ,staticStr,staticInteger); } @end