Итак, как правильно объявить глобальные переменные? Под глобальными мы понимаем переменные, объявленные на внешнем уровне (не static) и имеющие глобальное время жизни.
Если мы объявим переменную в одном из заголовочных файлов, например в файле MyClass.h таким, казалось бы логичным, образом:
NSString *myGlobalVariable;
а затем импортируем файл MyClass.h с помощью директивы #import в несколько файлов, например, MyClass1.h, MyClass2.h, MyClass3.h и MyClass4.h, то в результате с большой вероятностью получим примерно такое сообщение об ошибке:
duplicate symbol _myGlobalVariable in:
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass.o
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass1.o
duplicate symbol _myGlobalVariable in:
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass.o
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass2.o
duplicate symbol _myGlobalVariable in:
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass.o
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass3.o
duplicate symbol _myGlobalVariable in:
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass.o
/Users/L/Library/Developer/Xcode/DerivedData/…/MyClass4.o
ld: 4 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Произойдет это по тому, что в результате импорта переменная myGlobalVariable будет объявлена в каждом из подключенных файлов.
Можно объявить глобальную переменную используя «волшебное» слово extern. В этом случае, скорее всего, мы избежим сообщения об ошибке, но для переменных с глобальным временем жизни и областью видимости — все пространство приложения, существует опасность попытки одновременного изменения ее значения из разных потоков, от чего extern не может дать гарантированной защиты. К сожалению, неопытные программисты не учитывают этого важного факта. Extern можно безопасно использовать при объявлении глобальных констант, в этом случае проблем не возникает.
Нам надо объявить глобальную переменную таким образом, чтобы она не дублировалась ни при каких обстоятельствах. Для решения этой нехитрой задачи достаточно вспомнить что же может существовать в программе только в единственном экземпляре? Правильно, это — синглтон. Глобальную переменную можно задать с помощью класса синглтон (singleton), а точнее как его свойство (property). О достоинствах такого подхода написано много литературы и все интересующиеся могут почитать об этом, а также о том в каких случаях эти достоинства проявляются в полной мере. Наша задача — показать как это делается.
Паттерн Синглтон: Проверяет, что есть только один экземпляр класса и обеспечивает единую точку доступа к нему.(«Design Patterns» GoF (Addison-Wesley, 1994)).
Ниже пример кода — создаем класс сингтон, например, GlobalVariable и добавляем ему свойство myGlobalVariable, которое и будет нашей глобальной переменной:
Заголовочный файл, GlobalVariable.h
#import <Foundation/Foundation.h>;
@interface GlobalVariable : NSObject
@property(nonatomic, strong) NSString *myGlobalVariable;
+(instancetype)sharedGlobalVariable;
@end
Файл GlobalVariable.m
#import "GlobalVariable.h"
@implementation GlobalVariable
@synthesize myGlobalVariable;
+(instancetype)sharedGlobalVariable {
static id _singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_singletonInstance = [[super allocWithZone:NULL] init];
});
return_singletonInstance;
}
+(id) allocWithZone:(NSZone*)zone {
return [self sharedGlobalVariable];
}
-(id) copyWithZone:(NSZone*)zone {
return self;
}
Обращение к переменной выглядит следующим образом:
[GlobalVariable sharedGlobalVariable].myGlobalVariable;
Для удобства можно использовать макрос:
#define MY_GLOBAL_VARIABLE [GlobalVariable sharedGlobalVariable].myGlobalVariable;
Функция dispatch_once_t отвечает за потокобезопасность данной реализации кода.?
Как видите вопрос решается всего в несколько простых строк, а при использовании библиотеки MSLibrary for iOS — объем кода уменьшится в несколько раз.
Надеемся, что материал был для вас полезен, команда MSLibrary for iOS
Другие статьи:
Захват и верификация телефонных номеров с помощью регулярных выражений, для iOS и не только… Часть 1
Захват и верификация телефонных номеров с помощью регулярных выражений, для iOS и не только… Часть 2
Реализация множественного выбора условий с помощью битовых масок, для iOS и не только…
ПРОСТО: удаляем из строки ненужные символы, используя регулярные выражения, для iOS и не только…
Создание и компиляция кроссплатформенных (универсальных) библиотек в Xcode
Комментарии (7)
GarryC
22.04.2016 10:21Использование синглтона гарантирует уникальность и, что важно, потокобезопасность переменной
и при чем тут это? Напоминает «То их на хвост нанижет, то их понюхает, то их полижет — Очки не действуют никак»MSLibrary
22.04.2016 10:37По литературе — 5, но цитаты из учебника для младшей школы не проясняют вашу мысль (если она есть, разумеется). Имейте уважение к чтателям и объясняйте все для конкретного использования. А негативные эмоции можно сливать в другом месте, Хабр не помойная яма…
MANIAK_dobrii
22.04.2016 17:55Да тут целый букет антипаттернов. Супер, давайте значит, будем пропагандировать shared mutable state, да ещё и запихнем его в синглтон. Но нет, мы на этом не остановимся, давайте ещё прикольнее сделаем, давайте обернем это всё ещё и в #define.
Естественно, я тут не пишу, что это всё вообще запрещено, в любом случае принципы проектирования прийдется нарушить, вопрос только в какой степени. Но советовать такое без предложения альтернатив как-то некультурно даже. Кстати, вы даже не написали как ваше решение с синглтоном позволяет исключить «опасность попытки одновременного изменения ее значения из разных потоков». Всё таки целевая аудитория у такого решения — юные неокрепшие умы, они просто скопируют ваше решение и всё.
bobermaniac
Жутковато.
Если вам очень нужно (в 2016 году очень нужно?) объявить глобальную переменную — просто объявите ее в заголовочном файле как extern. После этого в реализации объявляете эту переменную и работаете с ней сколько угодно.