任何帮助解决这个问题将不胜感激。我在一个单独的游戏管理器中有3个NSMutableArrays,当我去访问我试图存储的数据时,它们中只有一个会工作,即使我使用它们都是一样的。
这是我在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查看其中的内容时,另外两个给出了错误的访问错误。 任何想法都是受欢迎的,因为我一直坚持这一点。感谢
答案 0 :(得分:1)
您应该封装数组并为要提供的函数创建访问器,即:即有一个- (void)addPlayerName:(NSString *)name
方法等。
这将使您的代码更具可读性,并允许您的经理使用这些值来处理,例如拒绝它们,检查重复项等等。
答案 1 :(得分:1)
听起来gameTexts
和gameDrawings
在某处过度释放。这几乎总是EXC_BAD_ACCESS
的原因。
答案 2 :(得分:0)
我刚刚研究了一组用于处理来自多个线程的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];\
}