我剛剛在處理一組用於處理來自多線程的NSMutableArrays的宏,並決定在堆棧溢出時進行一些搜索以查看導致我編寫此代碼的問題是否司空見慣,並且作爲結果你的問題出現了。
這可能不是「你正在尋找的機器人」,可以隨意「向前走」,如果它不回答你問的問題:-)
這裏有2個文件(意見關於如何使用它們)對同一概念的幾個版本 - 一個簡單的一行代碼自動生成一些類/實例方法做線程安全操作的一些技巧方面。
StaticMutableArrays.h是全球一流的,廣泛的概念,其中類股共同陣列的每個實例 - 在多線程操作是最有可能是一個問題。
還有另一種變體(ThreadsafeMutableArrays.h),它用於創建數組,每個類的實例都有自己的數組,這與其他任何實例都不相同(如果您剛創建了一個數組,因爲你的問題是關於在單例內訪問數組的問題,我現在只需要發佈StaticMutableArrays.h,它們的工作方式是一樣的,但是圍繞一個標準的伊娃數組。 ,並歡迎任何意見如何改進,或者有任何人看到任何明顯的問題。
StaticMutableArr ays.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];\
}
我創造了我的單封裝方法,但是這並沒有使在結果的差異。它不會崩潰,但gameTexts和gameDrawings會變爲null,即使在我設置它們之後的聲明中。我做了和gameNames一樣的gameTexts和gameDrawings,但是隻有playerNames能夠一路挺過來。 – 2011-06-12 21:53:44
我正在將文本發送到要設置的單例類方法中。在該方法中,要添加的文本是正確的,我使用[gameTexts addObject:(text)];添加它。但是在我的下一個聲明中,我會使用[[GameManager] sharedManager] getGameText:(0)]打印出來;它返回null。壞訪問是當我試圖NSLog數組的數量 – 2011-06-12 21:57:12
我做了一些改變,有什麼想法? @Wilbur Vandrsmith @fzwo – 2011-06-12 22:04:14