In my app I'm accessing and changing a mutable array from multiple threads. At the beginning it was crashing when I was trying to access an object with objectAtIndex, because index was out of bounds (object at that index has already been removed from array in another thread). I searched on the internet how to solve this problem, and I decided to try this solution .I made a class with NSMutableArray property, see the following code:
#interface SynchronizedArray()
#property (retain, atomic) NSMutableArray *array;
#end
#implementation SynchronizedArray
- (id)init
{
self = [super init];
if (self)
{
_array = [[NSMutableArray alloc] init];
}
return self;
}
-(id)objectAtIndex:(NSUInteger)index
{
#synchronized(_array)
{
return [_array objectAtIndex:index];
}
}
-(void)removeObject:(id)object
{
#synchronized(_array)
{
[_array removeObject:object];
}
}
-(void)removeObjectAtIndex:(NSUInteger)index
{
#synchronized(_array)
{
[_array removeObjectAtIndex:index];
}
}
-(void)addObject:(id)object
{
#synchronized(_array)
{
[_array addObject:object];
}
}
- (NSUInteger)count
{
#synchronized(_array)
{
return [_array count];
}
}
-(void)removeAllObjects
{
#synchronized(_array)
{
[_array removeAllObjects];
}
}
-(id)copy
{
#synchronized(_array)
{
return [_array copy];
}
}
and I use this class instead of old mutable array, but the app is still crashing at this line: return [_array objectAtIndex:index]; I tried also this approach with NSLock, but without a luck. What I'm doing wrong and how to fix this?
I believe this solution is poor. Consider this:
thread #1 calls count and is told there are 4 objects in the array.
array is unsynchronized.
thread #2 calls removeObjectAtIndex:2 on the array.
array is unsynchronized.
thread #1 calls objectAtIndex:3 and the error occurs.
Instead you need a locking mechanism at a higher level where the lock is around the array at both steps 1 and 5 and thread #2 cannot remove an object in between these steps.
You need to protect (with #synchronized) basically all usage of the array. Currently you only prevent multiple threads from concurrently getting objects out of the array. But you have no protection for your described scenario of concurrent modification and mutation.
Ask yourself why you're modifying the array on multiple threads - should you do it that way or just use a single thread? It may be easier to use a different array implementation or to use a wrapper class that always switches to the main thread to make the requested modification.
Related
This is an implementation section of a class named "Model". Here I recursively call call the setDictionary method upto 3 layers, which raises an exception (NSMutablearray mutated while being enumerated) this exception can be avoided if I use for loop istead of forin, I would like to understand how this error occurs...Can any one help...Please dont reply with some links that point to some definition, I already read lots of documentation and I dont understand how the exception is raised in this situation.
#implementation Model
#synthesize arraySubOptions,boolHasSub;
- (instancetype)init
{
self = [super init];
if (self) {
boolHasSub = NO;
arraySubOptions = [NSMutableArray new];
}
return self;
}
-(void)setDictionary:(NSDictionary*)dict{
boolHasSub = [[[dict objectForKey:#"key_has_sub"] nullCheck:[NSString class]] boolValue];
if (boolHasSub) {
NSArray * arrayDict = (NSArray*)[self loadDataFromDB];
if(arrayDict && (arrayDict.count>0) ){
for (NSDictionary * dict in arrayDict) {
Model * objOption = [Model new];
[objOption setDictionary:dict];
[arraySubOptions addObject:objOption]; /*This is the line that raises the exception. It raised */
}
}
}
}
#end
Your method
[self loadDataFromDB]
should be altering your arrayDict, so, when you reach
[objOption setDictionary:dict];
is entering twice in that function, modifying your arrayDict and launching this exception.
Doing it with 'for' instead of 'foreach' works because you set the limits of your loop and it doesn't reach any exception if your limit isn't out of bounds. (i > arrayDict.count)
[self loadDataFromDB] returns an array which gets deallocated when the method call ends, since the object was created inside the method. So copying the returned array did the trick.
[[self loadDataFromDB] copy]
I have a table view with a search, and search scope buttons with two possible scopes. The table is empty until a search is executed. Each scope has it's own mutable array for the table's data source, we'll say scopeA_array and scopeB_array. To simplify some methods, I'm trying to create a generic pointer reference to whichever array is the currently active scope. So I tried this:
#property (nonatomic, assign) NSMutableArray *tableDataArray;
In viewDidLoad, I assign it to the default selected scope.
_tableDataArray = _scopeA_array;
I can log the memory address of each array, they're both the same.
However, if I execute a search, _scopeA_array gets populated. Then in my numberOfRowsInSection method, I take the count of _tableDataArray but it's empty. I log the addresses again, both are different.
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
EDIT: A simplified way to test this, with the following lines of code, would like a way for tableDataArray to have the contents of testArray, even though the contents of testArray are assigned after:
NSArray *testArray = [NSArray new];
NSArray *tableDataArray = [testArray copy];
testArray = [NSArray arrayWithObjects:#"my", #"test", #"array", nil];
NSLog(#"table data array: %#", tableDataArray);
// logs empty array
I think the best approach is use a method to return conditionally the array for the current scope. So you just always use this method to populate your UITableView
- (NSMutableArray*) tableArray
{
return [self isScopeA] ? _scopeA_array : _scopeB_array;
}
How do I create an array property that just references an array, and always points to the same object in memory even if it changes?
If you want to track changes to a variable then you use a pointer to the variable rather than a pointer to a single array instance. E.g.:
#implementation MyController
{
__strong NSArray* *_currentDataPtr;
NSArray* _dataA;
NSArray* _dataB;
}
- (id)init
{
if (self = [super init])
{
_currentDataPtr = &_dataA; // Ensure _currentDataPtr is never NULL
}
return self;
}
- (void)setSearchScope:(NSInteger)searchScope
{
switch (searchScope)
{
default :
NSAssert(NO, #"");
case 0 :
_currentDataPtr = &_dataA;
break;
case 1 :
_currentDataPtr = &_dataB;
break;
}
}
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
return [*_currentDataPtr count];
}
If you want it to be a property then implement a property getter that dereferences the pointer:
#property (nonatomic, readonly) NSArray* currentData;
- (NSArray*)currentData { return *_currentDataPtr; }
Is it better to initialize an NSArray in viewDidLoad when the program first starts up, or define the NSArray only if a condition is met?
Basically, I initialized an NSArray in an IF condition of one of my methods. This method may be called multiple times, and want to know if it's better on memory if the NSArray is created and destroyed in the method, or if it's better to define it once in viewDidLoad and reference it in the method?
If I'm not clear, please let me know.
Thanks
Create any data collection only when you need it, this way you make sure that the program/screen is initially launched quickly. This is called as lazy loading, this approach should be followed whenever possible.
If you are using NSMutableArray and managing this in run time, you can do something like this for lazy loading, and clearing it from memory when not needed. Add helper methods to add and remove objects from an array, array is created automatically when needed and removed from memory when it is empty.
- (void)addObject:(NSObject *)value
{
if (value == nil) return;
if (_collection == nil) {
_collection = [[NSMutableArray alloc] init];
}
[_collection addObject:value];
}
- (void)removeObject:(NSObject *)value
{
if (value == nil) return;
[_collection removeObject:value];
if ([_collection count] == 0) {
[_collection release], _collection = nil;
}
}
So I want to have multiple nodes that are connected. Every node has outgoing and incoming connections. But the NSMutableArrays are creating leaks although i'm using ARC. How can i get the objects to be released properly? I'm already using an autoreleasepool.
The code so far is:
#interface TestObj()
#property(strong) NSMutableArray *incoming;
#property(strong) NSMutableArray *outgoing;
#end
#implementation TestObj
#synthesize incoming,outgoing;
- (id)init
{
self = [super init];
if (self) {
incoming = [NSMutableArray array];
outgoing = [NSMutableArray array];
}
return self;
}
-(void)addIncoming:(TestObj *)incomingN {
if([incoming indexOfObject:incomingN] == NSNotFound) {
[incoming addObject:incomingN];
[incomingN addOutgoing:self];
}
}
-(void)addOutgoing:(TestObj *)outgoingN {
if([outgoing indexOfObject:outgoingN] == NSNotFound) {
[outgoing addObject:outgoingN];
[outgoingN addIncoming:self];
}
}
With ARC, as with manual memory management on iOS, you need to avoid retain cycles. If you have one object that is retaining a second, and the second is retaining the first, those two will never be deallocated even if nothing else references them, so you have a memory leak.
You need to make it so that you aren't referencing them like this. NSArray and NSMutableArray keep strong references to other objects. You can do something like the following to create a weak reference that you can story in the array:
NSValue *val = [NSValue valueWithNonretainedObject:object];
If you store val in the array, it won't have a strong reference to the object, so it can be deallocated. However, you have to be careful that you aren't creating a situation where some of your objects have no strong references, or they will get deallocated before you want them to.
Well this may sound basic, but have tried setting them = to nil?
I have the following 2 classes:
EventDispatcher:
#interface EventDispatcher()
-(id)initEventDispatcher;
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType;
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType;
#end
#implementation EventDispatcher
static EventDispatcher* eventDispatcher;
// Singleton.
+(EventDispatcher*)instance
{
if (eventDispatcher == nil)
{
eventDispatcher = [[EventDispatcher alloc] initEventDispatcher];
}
return eventDispatcher;
}
-(id)initEventDispatcher
{
self = [super init];
if (self)
{
eventSubscriptions = [[NSMutableDictionary alloc] init];
}
return self;
}
// Let anyone subscribe to an event. Return the EventSubscriber so they can dispatch events if needed, and to be able to unsubscribe.
-(EventSubscriber*)subscribe:(EVENT_TYPE)eventType :(void(^)(id package))operateEvent
{
// Create the object.
EventSubscriber* eventSubscriber = [[EventSubscriber alloc] initEventSubscriber:eventType :operateEvent];
// Now get the list it belongs to (we sort subscriptions in a dictionary so that when we dispatch an event, it's fast (we don't need to iterate through all EventSubscribers to find who subscribe to an event).
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if(subscriptionsToThisEvent == nil)
{
// If the list is nil, no one has subscribed to it yet, so make that list and add it to the dictionary.
subscriptionsToThisEvent = [[NSMutableArray alloc] init];
NSNumber* key = [self getKeyToEvent:eventType];
[eventSubscriptions setObject:subscriptionsToThisEvent forKey:key];
[subscriptionsToThisEvent release];
}
// Add the EventSubscriber to the subscription list.
[subscriptionsToThisEvent addObject:eventSubscriber];
[eventSubscriber release];
return eventSubscriber;
}
-(void)unsubscribe:(EventSubscriber*)eventSubscriber
{
// Get the list it belongs to, and remove it from that list.
EVENT_TYPE eventType = [eventSubscriber getEventType];
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
if (subscriptionsToThisEvent != nil)
{
[subscriptionsToThisEvent removeObject:eventSubscriber];
}
}
-(void)dispatch:(EVENT_TYPE)eventType :(id)package
{
NSMutableArray* subscriptionsToThisEvent = [self getSubscriptionsToEvent:eventType];
// If no one has subscribed to this event, it could be nil, so do nothing.
if (subscriptionsToThisEvent != nil)
{
// Otherwise, let them all know that the event was dispatched!
for (EventSubscriber* eventSubscriber in subscriptionsToThisEvent)
[eventSubscriber dispatch:package];
}
}
// Helper methods to get stuff (lists, keys) from event types.
-(NSMutableArray*)getSubscriptionsToEvent:(EVENT_TYPE)eventType
{
NSNumber* key = [self getKeyToEvent:eventType];
NSMutableArray* subscriptionsToThisEvent = [eventSubscriptions objectForKey:key];
return subscriptionsToThisEvent;
}
-(NSNumber*)getKeyToEvent:(EVENT_TYPE)eventType
{
return [NSNumber numberWithInt:eventType];
}
-(void)dealloc
{
[eventSubscriptions release];
[super dealloc];
}
#end
EventSubscriber:
#import "EventSubscriber.h"
#implementation EventSubscriber
-(id)initEventSubscriber:(EVENT_TYPE)newEventType :(void(^)(id package))newOperateEvent
{
self = [super init];
if (self)
{
operateEvent = [newOperateEvent copy];
eventType = newEventType;
}
return self;
}
-(void)dispatch:(id)package
{
operateEvent(package);
}
-(EVENT_TYPE)getEventType
{
return eventType;
}
-(void)dealloc
{
[operateEvent release];
[super dealloc];
}
#end
Onto the big question: How do I unburden a programmer who is using this system with having to unsubscribe from an event during deallocation? When multiple classes are using this system, programmers will have to remember to unsubscribe during deallocation (if not an earlier time), or REALLY bad/weird/unexpected things could happen (I would prefer a compile-time check, or a big, obvious, debuggable crash, but more-so the former). Ideally, I'd like to restructure this system (or do anything) so that when an object is deallocated, the EventDispatcher gracefully handles it.
One quick fix is to have objects allocate EventSubscribers directly, then in the EventSubscriber constructor, it subscribes itself to EventDispatcher (but that's obviously bad, maybe make EventDispatcher's stuff static? Ugh now we're just getting worse).
Side notes:
I'm not using ARC, but, that does not matter here (at least I think it does not, if there are ARC-based solutions, I'd like to hear them).
I do plan on adding a method in EventDispatcher to be able to remove EventSubscribers by those who did the subscription (so now when subscribing, objects will have to pass 'self'). I also plan on making the enumerated EVENT_TYPE use strings, but that's a different topic altogether.
I also plan on translating a lot of my code (including these classes) to C++. So I'd appreciate a conceptual solution as opposed to Objective-C specific solutions.
So, is this possible?
Thanks a bunch!