NSMutableArrays в Singleton не содержит значений - PullRequest
0 голосов
/ 13 июня 2011

Буду признателен за любую помощь в выяснении этого.У меня есть 3 NSMutableArrays в синглтоне (Game Manager), и только один из них будет работать, когда я получу доступ к данным, которые пытаюсь сохранить, даже если я все равно их использую.их в моем GameManager.h

- (GameManager *)init
{
    if ((self = [super init]))
    {
        gameTexts = [[NSMutableArray alloc] init];
        gameDrawings = [[NSMutableArray alloc] init];
        playerNames = [[NSMutableArray alloc] init];
    }
    return self;
}

Вот куда я иду, чтобы хранить значения в них.Кажется, именно в этом и заключается проблема с NSLogs, которые я вставил.

[[GameManager sharedManager].playerNames addObject:[NSString stringWithFormat:@"%@",nextPlayerName.text]];
[[GameManager sharedManager].gameTexts addObject:[NSString stringWithFormat:@"%@",inputText.text]];
[[GameManager sharedManager].gameDrawings addObject:([[GameManager sharedManager] previousDrawing])];

Здесь я пытаюсь получить данные.

names.text = [[GameManager sharedManager].playerNames objectAtIndex:(nameIndex)];
texts.text = [[GameManager sharedManager].gameTexts objectAtIndex:(textIndex)];
drawings.image = [[GameManager sharedManager].gameDrawings objectAtIndex:(drawingIndex)];

Только массив playerNames будет работать правильно.Два других дают мне ошибки доступа, когда я пытаюсь использовать NSLog, чтобы увидеть, что в них.Любые идеи приветствуются, так как я застрял на этом некоторое время.Спасибо

Ответы [ 3 ]

1 голос
/ 13 июня 2011

Звучит так, будто gameTexts и gameDrawings где-то переизбыточны.Это почти всегда причина EXC_BAD_ACCESS.

1 голос
/ 13 июня 2011

Вы должны инкапсулировать свои массивы и создавать средства доступа для функций, которые вы хотите предоставить, то есть иметь метод - (void)addPlayerName:(NSString *)name и т. Д.

Это сделает ваш код более читабельным, а также позволит вашему менеджеру делать вещисо значениями, такими как отклонение их, проверка на дубликаты и т. д.

0 голосов
/ 11 октября 2011

Я только что работал над набором макросов для обработки NSMutableArrays из нескольких потоков и решил сделать несколько поисков по переполнению стека, чтобы увидеть, были ли проблемы, которые привели меня к написанию этого кода, обычными, и в результате ваши возник вопрос.

Это не может быть "искомый дроид", поэтому не стесняйтесь "двигаться вперед", если он не отвечает на заданный вами вопрос: -)

здесь 2 заголовочных файла (с комментариями о том, как их использовать) для нескольких версий одной и той же концепции - простая однострочная для автоматической генерации некоторых методов класса / экземпляра, которые выполняют несколько хитрые аспекты поточно-ориентированных манипуляций с массивами .

StaticMutableArrays.h - это глобальная концепция массива для всего класса, где каждый экземпляр класса совместно использует общий массив - где многопоточная операция, скорее всего, будет проблемой.

Существует еще один вариант (ThreadsafeMutableArrays.h), который предназначен для создания массивов, в которых каждый экземпляр класса имеет свой собственный массив, который отличается от любых других экземпляров (как обычно, если вы просто создали массив в своем коде). Так как ваш вопрос касался проблем с доступом к массивам внутри синглетонов, я пока просто опубликую StaticMutableArrays.h. Они работают так же, но оборачиваются вокруг стандартного массива ivar.

Я выпускаю этот код как общественное достояние и буду рад любым комментариям о том, как его можно улучшить, или о каких-либо явных проблемах, которые кто-либо видит.

StaticMutableArrays.h

//
//  StaticMutableArrays.h
//
//  Created by Jonathan M Annett on 10/10/11.
//   The author has placed this work in the Public Domain, thereby relinquishing all copyrights. Everyone is free to use, modify, republish, sell or give away this work without prior consent from anybody.

//   This documentation is provided on an “as is” basis, without warranty of any kind. Use at your own risk! Under no circumstances shall the author(s) or contributor(s) be liable for damages resulting directly or indirectly from the use or non-use of this documentation.
//



/*

 StaticMutableArrays are thread safe arrays that are associated with a particular class of object 

 they are accessed via class methods in the class in which they are defined.
 each instance of the class sees the same physical array. 

 they are generated by insert a macro in the interface and implementation seconds of the class:

@interface myObject : NSObject {

 }

    staticMutableArray_interface(arrayName);


 @end


 @implementation myObject

    staticMutableArray_implementation(arrayName);

@end


 these arrays are syntactially identical to those generated by  ThreadsafeMutableArrays.h

 you can then access the array in your code as follows (using "arrayName" as the example)


 NSMutableArray *tempArray = [self.class arrayName_];

 note the trailing underscore - this indicates it's an autoreleased threadsafe mutable copy of the underlying array, that was "correct" at the time you called the method. you can safely iterate the contents, knowing the array you are holding won't be mutated by another thread whilst you have it - the underlying array may have been mutated, so if you need exclusive access to the array knowing it can't be changed use [self.class arrayName] instead. note that even if the underlying array is mutated, and some elements are removed, and as long as this object is around (ie the pool is not drained) they will be quite safe as this (copied) array has them retained. this array is an NSMutableArray, so you can chop and change it as you want (eg whilst processing it), and it won't affect the underlying array at all. be sure to call [array removeAllObjects] when you are done to explicitly release the retains it has for each member. you don't need to release the array itself as it is already autoreleased.  


 NSArray *tempArray = [self.class copyOf_arrayName]

 This is the same as [self.class arrayName_], however the returned copy is immutable, and a retained copy.
 you can (for example) iterate the contents, and then release it, knowing that no matter what another thread does to the array while you are iterating it, they won't mess with your copy of the array. again - if you need exclusive access to the array knowing it can't be changed use [self.class arrayName] instead. 



 [self.class arrayName]; 

 returns the raw underlying array - warning: if you call this directly only, you can only USE the returned value safely inside an @synchronized() block for the array itself. the easiest way to do this is something like:

 NSMutableArray *array;
 @synchronized(array = [self.class arrayName]){ 

 // do something with "array"

 }

 if you study the macros you will see a variant of this throughout. (the method wrapper arrayName omitted to save an extra message being sent, but for readability of your code it's suggested you use the above construct.


 [self.class addObjectTo_arrayName:object];

 this is a thread safe way of adding to the array, that you can use from anywhere (EXCEPT from inside and @synchronized block for the array! in that case just add the object as you would normally in a single threaded environment) 

[self.class removeObjectFrom_arrayName:object];

 this is a thread safe way of removing an object from an array, that you can use from anywhere (EXCEPT from inside and @synchronized block for the array! in that case just add the object as you would normally in a single threaded environment) 

if ([self.class objectExistsIn_arrayName:object]) {...}

 this is a thread safe way of testing if an object is in an array, that you can use from anywhere (EXCEPT from inside and @synchronized block for the array! in that case just add the object as you would normally in a single threaded environment) 


 now the clever stuff:

 @synchronzed exclusive unconditional iteration of each element, blocking all other threads.

 [self.class iterate_arrayName_withBlock:NSArrayIterateBlock {

    // do something with: element 

 }];

 in this code construct you get 3 variables defined. for convenience a macro - NSArrayIterateBlock is defined to save you having to type long hand the block header:

    ^void(id element, NSInteger index,NSInteger ofCount)

    element  - this is the item you are iterating. you just typecast it to whatever you expect it to be (or test it to see what it is, the choice is yours)
    index & count - for simple accounting purposes you can tell at any stage in the loop how many you have processed (index == 0 means this is first), or how many you have to go (index==count-1) means this is the  last. you get the idea.




 @synchronzed exclusive conditional iteration of each element, blocking all other threads.

 [self.class conditionallyIterate_arrayName_withBlock:NSArrayConditionallyIterateBlock {

 // do something with: element and..

    return NO;  // we have done looping.

 // or 

    return YES;  // we want to keep looping.

 }];

 in this code construct you get 3 variables defined. for convenience a macro - NSArrayConditionallyIterateBlock is defined to save you having to type long hand the block header:

 ^BOOL(id element, NSInteger index,NSInteger ofCount)

 element  - this is the item you are iterating. you just typecast it to whatever you expect it to be (or test it to see what it is, the choice is yours)
 index & count - for simple accounting purposes you can tell at any stage in the loop how many you have processed (index == 0 means this is first), or how many you have to go (index==count-1) means this is the  last. you get the idea.

 the BOOL return value from the block indicates if you want to keep iterating the array (YES) or not (NO)



 and the final Pièce de résistance - conditionally delete elements from an array, whilst blocking access to the array by other threads.


 [self.class conditionallyDeleteFrom_arrayName_withBlock:
         NSMutableConditionallyDeleteArrayBlock {


        if (<element is something i want to delete>) {
            return YES;
        }

    return NO;
 }]



internal method that holds the static variable for the singleton array:

 [self.class arrayNameAlloc:YES];   // creates/returns the array - you can call this in your +(void) load {} for the class to ensure it is created. if you don't do this however, it will automatically be called the first time you do it. 
 [self.class arrayNameAlloc:NO];    // dumps the array - once you do this, you can't re alloc, so only do it once!



*/


// custom block header macros where you can specify a name and class type for the array element
// for example NSArrayIterateBlock_(personsName,NSString *)
// for example NSArrayIterateBlock_(itemInfo,NSDictionary *)
# define NSArrayIterateBlock_(element,id) ^void(id element, NSInteger index,NSInteger ofCount)
# define NSArrayConditionallyIterateBlock_(element,id) ^BOOL(id element, NSInteger index,NSInteger ofCount)
# define NSMutableConditionallyDeleteArrayBlock_(element,id) ^BOOL(id element, NSInteger originalIndex)


// generic version that just defines each element as "id element"
# define NSArrayIterateBlock NSArrayIterateBlock_(element,id)
# define NSArrayConditionallyIterateBlock NSArrayConditionallyIterateBlock_(element,id)
# define NSMutableConditionallyDeleteArrayBlock NSMutableConditionallyDeleteArrayBlock_(element,id)



#define staticMutableArray_interface(arrayName)\
+(NSMutableArray *) arrayName;\
/*+(NSMutableArray *) arrayName##Alloc:(BOOL)alloc;*/\
+(void) addObjectTo_##arrayName:(id) object;\
+(void) removeObjectFrom_##arrayName:(id) object;\
+(BOOL) objectExistsIn_##arrayName:(id) object;\
+(NSMutableArray *) arrayName##_;\
+(NSArray *) copyOf_##arrayName;\
+(void) iterate_##arrayName##_withBlock:\
(void (^)(id element,NSInteger index,NSInteger ofCount))codeBlock;\
+(void) iterateCopyOf_##arrayName##_withBlock:\
(void (^)(id element,NSInteger index,NSInteger ofCount))codeBlock ;\
+(void) conditionallyIterate_##arrayName##_withBlock:\
(BOOL (^)(id element,NSInteger index,NSInteger ofCount))codeBlock;\
+(void) conditionallyIterateCopyOf_##arrayName##_withBlock:\
(BOOL (^)(id element,NSInteger index,NSInteger ofCount))codeBlock;\
+(void) conditionallyDeleteFrom_##arrayName##_withBlock:\
    (BOOL (^)(id element,NSInteger originalIndex))codeBlock;


#define staticMutableArray_implementation(arrayName)\
/*quasi singleton factory method*/ \
+(NSMutableArray *) arrayName##Alloc:(BOOL)alloc {\
    static NSMutableArray *result = nil;\
    static BOOL dealloced = NO;\
    if (alloc) {\
            if (!result) {\
               if (!dealloced) {\
                  result = [[NSMutableArray alloc] init ];\
                }\
           }\
    } else {\
      if (!dealloced) {\
          if(result) {\
              @synchronized(result){ \
                 [result removeAllObjects];\
                 [result release];\
              }\
              result = nil;\
           }\
           dealloced = YES;\
       }\
    }\
    return result;\
}\
/*  add an object the arrray */ \
+(void) addObjectTo_##arrayName:(id) object   {\
     NSMutableArray *array;\
       @synchronized(array= [self.class arrayName##Alloc:YES]){ \
       [array addObject:object];\
     }\
}\
/*  add an object, if it is not already in the array */ \
+(void) includeObjectIn_##arrayName:(id) object   {\
    NSMutableArray *array;\
    @synchronized(array= [self.class arrayName##Alloc:YES]){ \
      if ([array indexOfObject:object]==NSNotFound){\
         [array addObject:object];\
     }\
   }\
}\
/*  remove an object from the array */ \
+(void) removeObjectFrom_##arrayName:(id) object   {\
   NSMutableArray *array;\
   @synchronized(array= [self.class arrayName##Alloc:YES]){ \
      [array removeObject:object];\
  }\
}\
/*  test object existance*/ \
+(BOOL) objectExistsIn_##arrayName:(id) object   {\
  NSInteger result = NSNotFound; \
  NSMutableArray *array;\
  @synchronized(array= [self.class arrayName##Alloc:YES]){ \
    result = [array indexOfObject:object];\
  }\
  return result!=NSNotFound;\
}\
/*  raw underlying access - use inside @synchronized(array= [self.class arrayName##Alloc:YES]) only*/ \
+(NSMutableArray *) arrayName { \
    return [self.class arrayName##Alloc:YES];\
}\
/*  mutable  autoreleased copy of underlying array - ie snapshot which may contain objects that have been removed since snapshot was taken - you need to call removeAllObjects when done, to expedite adjusting affected retainCounts that the arrayWithArray process implies */ \
+(NSMutableArray *) arrayName##_ { \
    NSMutableArray *result = nil;\
    NSMutableArray *array;\
    @synchronized(array= [self.class arrayName##Alloc:YES]){ \
        result = [NSMutableArray arrayWithArray:array];\
    }\
    return result ;\
}\
/*  immutable retained copy of underlying array - ie snapshot which may contain objects that have been removed since snapshot was taken - you need to call release when done, to expedite adjusting affected retainCounts that the initWithArray process implies */ \
+(NSArray *) copyOf_##arrayName { \
    NSArray *result = nil;\
    NSMutableArray *array;\
    @synchronized(array = [self.class arrayName##Alloc:YES]){ \
        result = [[NSArray alloc] initWithArray:array];\
    }\
    return result ;\
}\
/*  iteration of the array for each element, using a thread safe snapshot copy*/\
+(void) iterateCopyOf_##arrayName##_withBlock:\
(void (^)(id element,NSInteger index,NSInteger ofCount))codeBlock  {\
    NSArray *array = [self.class copyOf_##arrayName]; \
    NSInteger index = 0;\
    NSInteger count = array.count;\
        for (id element in array) {\
            codeBlock (element,index,count);\
            index++;\
        }\
   [array release];\
}\
/*  @synchronized iteration the array for each element */\
+(void) iterate_##arrayName##_withBlock:\
(void (^)(id element,NSInteger index,NSInteger ofCount))codeBlock  {\
   NSMutableArray *array;\
   @synchronized(array = [self.class arrayName##Alloc:YES]){ \
        NSInteger index = 0;\
        NSInteger count = array.count;\
        for (id element in array) {\
            codeBlock (element,index,count);\
            index++;\
        }\
    }\
}\
/* iteration of the array for each element, using a thread safe snapshot copy, with option to exit loop */ \
+(void) conditionallyIterateCopyOf_##arrayName##_withBlock:\
(BOOL (^)(id element,NSInteger index,NSInteger ofCount))codeBlock  {\
    NSArray *array = [self.class copyOf_##arrayName];\
    NSInteger index = 0;\
    NSInteger count = array.count;\
    for (id element in array) {\
        if (!codeBlock (element,index,count)) break;\
        index++;\
    }\
    [array release];\
}\
/*  @synchronized iteration the array for each element, with option to exit loop */ \
+(void) conditionallyIterate_##arrayName##_withBlock:\
(BOOL (^)(id element,NSInteger index,NSInteger ofCount))codeBlock  {\
    NSMutableArray *array;\
    @synchronized(array = [self.class arrayName##Alloc:YES]){ \
        NSInteger index = 0;\
        NSInteger count = array.count;\
        for (id element in array) {\
            if (!codeBlock (element,index,count)) break;\
            index++;\
        }\
    }\
}\
/* conditionally delete each element */ \
+(void) conditionallyDeleteFrom_##arrayName##_withBlock:\
(BOOL (^)(id element,NSInteger originalIndex))codeBlock  {\
NSArray *array = [self.class copyOf_##arrayName]; \
NSInteger originalIndex = 0;\
for (id element in array) {\
    \
    if (codeBlock (element,originalIndex)) [self.class removeObjectFrom_##arrayName:element];\
    originalIndex++;\
}\
[array release];\
}
...