Разработчики библиотеки MSLibrary for iOS продолжают серию очень компактных статей, посвященных тому как ПРОСТО реализовать ту или иную функцию. Никакой теории, только практика…

Итак, как правильно объявить глобальные переменные? Под глобальными мы понимаем переменные, объявленные на внешнем уровне (не 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)


  1. bobermaniac
    21.04.2016 18:17
    +2

    Жутковато.

    Если вам очень нужно (в 2016 году очень нужно?) объявить глобальную переменную — просто объявите ее в заголовочном файле как extern. После этого в реализации объявляете эту переменную и работаете с ней сколько угодно.


  1. HexGrimm
    21.04.2016 19:35

    Я в своей команде подчиненным руки отрывал за синглтоны и статику.


    1. MSLibrary
      22.04.2016 10:35

      Жалко ваших коллег, но какое отношение их судьба имеет к глобальным переменным не очень понятно… себя показать можно и в другом месте, Хабр для этого не очень подходит…


  1. GarryC
    22.04.2016 10:21

    Использование синглтона гарантирует уникальность и, что важно, потокобезопасность переменной
    и при чем тут это? Напоминает «То их на хвост нанижет, то их понюхает, то их полижет — Очки не действуют никак»


    1. MSLibrary
      22.04.2016 10:37

      По литературе — 5, но цитаты из учебника для младшей школы не проясняют вашу мысль (если она есть, разумеется). Имейте уважение к чтателям и объясняйте все для конкретного использования. А негативные эмоции можно сливать в другом месте, Хабр не помойная яма…


  1. kostyl
    22.04.2016 17:50

    Что это за говностатьи? MSLibrary? Что это вообще за ерунда…


  1. MANIAK_dobrii
    22.04.2016 17:55

    Да тут целый букет антипаттернов. Супер, давайте значит, будем пропагандировать shared mutable state, да ещё и запихнем его в синглтон. Но нет, мы на этом не остановимся, давайте ещё прикольнее сделаем, давайте обернем это всё ещё и в #define.

    Естественно, я тут не пишу, что это всё вообще запрещено, в любом случае принципы проектирования прийдется нарушить, вопрос только в какой степени. Но советовать такое без предложения альтернатив как-то некультурно даже. Кстати, вы даже не написали как ваше решение с синглтоном позволяет исключить «опасность попытки одновременного изменения ее значения из разных потоков». Всё таки целевая аудитория у такого решения — юные неокрепшие умы, они просто скопируют ваше решение и всё.