Remove object from an array stored in a singleton - objective-c

Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?

That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)

Related

Unit testing a method that relies on an NSMapTable to clean up objects that lack strong references

So I have the following method (it's an UIView category method to supplement nib loading, however, it has been cleaned up to be more relevant here):
+ (id) loadFromNib {
NSString* nibName = NSStringFromClass([self class]);
NSArray* elements = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
NSMutableArray* foundCustomObjects = [NSMutableArray array];
NSObject* foundViewObject = nil;
for (NSObject* anObject in elements) {
if ([anObject isKindOfClass:[self class]] && foundViewObject == nil) {
foundViewObject = anObject;
// Keep strong references to non-UIView custom objects (to prevent them from being released due to having weak-only references):
} else if (![anObject isKindOfClass:[UIView class]]) {
[foundCustomObjects addObject:anObject];
}
}
// Generate strong references to all found custom objects:
if (foundViewObject != nil) {
[foundCustomObjects enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
[customObjects setObject:foundViewObject forKey:obj];
// (Yes, I will skip objects that are strongly referenced by their view later on)
}];
}
return foundViewObject;
}
And customObjects is a static variable defined as:
+ (void) initialize {
if (customObjects == nil) {
// For each view that holds a custom object, store a strong reference to that object here, that way preventing the object from being deallocated due to weak referencing (in UICollectionView.delegate, for example):
customObjects = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
}
}
My problem is that I want to unit test the fact that deallocated views really result in deallocating the referenced "custom object". How should I do that?
This is what I've got so far (using OCMock):
- (void) test {
/*
* SETUP */
NSObject* __weak weakRefToSomeObject;
UIView* someView;
NSObject* someObject;
#autoreleasepool {
someView = [[UIView alloc] init];
someObject = [[NSObject alloc] init];
NSArray* nibElements = #[someView, someObject];
id mainBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[mainBundleMock stub] andReturn:nibElements] loadNibNamed:[OCMArg any] owner:[OCMArg any] options:[OCMArg any]];
id NSBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[NSBundleMock stub] andReturn:mainBundleMock] mainBundle];
/*
* RUN */
weakRefToSomeObject = someObject;
[UIView loadFromNib];
someObject = nil;
nibElements = nil;
[mainBundleMock stopMocking];
[NSBundleMock stopMocking];
mainBundleMock = nil;
NSBundleMock = nil;
}
/*
* VERIFY */
XCTAssertNotNil(weakRefToSomeObject); // This passes!
#autoreleasepool {
someView = nil;
}
XCTAssertNil(weakRefToSomeObject); // This does not pass - why?
}
At the last row, I expect the key-value pair (where the view was referenced weakly) to be dropped, that way dropping the last strong reference to someObject, and thus rendering weakRefToSomeObject nil.
I have also tried to add someView = nil to the first autoreleasepool (just below NSBundleMock = nil), but that didn't help.
Any ideas?
I fixed this by adding an access function:
// Allow tests to access the customObjects map:
NSMapTable* getCustomObjectsMap() {
return customObjects;
}
And then declaring it in my unit test document:
// Declare method that gives us access to the static customObjects variable:
NSMapTable* getCustomObjectsMap();
Thus the test code ended up to be:
- (void) testCustomObjectLifecycleFromStartToFinish {
/*
* ASSERT REQUIRED INITIAL STATE */
XCTAssertNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
/*
* SETUP */
UIView* someView = [[UIView alloc] init];
NSObject* someObject = [[NSObject alloc] init];
#autoreleasepool {
#autoreleasepool {
NSArray* nibElements = #[someView, someObject];
id mainBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[mainBundleMock stub] andReturn:nibElements] loadNibNamed:[OCMArg any] owner:[OCMArg any] options:[OCMArg any]];
id NSBundleMock = [OCMockObject niceMockForClass:[NSBundle class]];
[[[NSBundleMock stub] andReturn:mainBundleMock] mainBundle];
/*
* RUN */
[UIView loadFromNib];
someObject = nil;
nibElements = nil;
}
/*
* VERIFY */
XCTAssertNotNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
// Dropping last strong reference to view:
someView = nil;
}
// Without strong references to someView, the objects map should have been emptied:
XCTAssertNil([[getCustomObjectsMap() objectEnumerator] nextObject]);
}

CS193P assignment 2 Clear function

Given this implementation:
- (NSMutableArray *)programStack
{
if (_programStack == nil)
_programStack = [[NSMutableArray alloc]init];
return _programStack;
}
- (id)program
{
return [self.programStack copy];
}
+ (double)popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack)
[stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]]) {
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]])
{
NSString *operation = topOfStack;
// C
if ([operation isEqualToString:#"C"])
{
[stack removeAllObjects];
return 0;
}
}
}
Am I correct in assuming that the class method's call to [stack removeAllObjects] only affects a copy of a copy rather than removing all objects from the instance's _programStack ? How would you, from that class method, affect the instance's variable? Or is there a better way to do this?
Thanks.
[stack removeAllObjects]; will remove all objects from stack. If you call + (double)popOperandOffStack:(NSMutableArray *)stack from an object, passing an instance variable as stack, then popOperandOffStack: operates on that instance variable and will remove all objects:
[[self class] popOperandOffStack:self.myInstanceArray]
If, on the other hand, you call [[self class] popOperandOffStack:[self.myInstanceArray mutableCopy]] it will operate on a copy.

isMemberOfClass doesn't work as expected with ocunit [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
'isMemberOfClass' returning 'NO' when custom init
I've some trouble with the "isMemberOfClass"-Method.
I have a class, that generates and returns objects ("MyObject")
// ObjectFactory.h
...
-(MyObject*)generateMyObject;
...
// ObjectFactory.m
...
-(MyObject*)generateMyObject
{
MyObject *obj = [[MyObject alloc]init];
obj.name = #"Whatever"; // set properties of object
return obj;
}
...
And there's a unittest-class, that calls the generateMyObject-selector and checks the class of the returned object:
...
ObjectFactory *factory = [[ObjectFactory alloc]init];
MyObject *obj = [factory generateMyObject];
if (![obj isMemeberOfclass:[MyObject class]])
STFail(#"Upps, object of wrong class returned...");
else
...
I expect, that the else-part is processed...but the STFail(...) is called instead, but why?
Thx for any help!
Regards,
matrau
Ok, here is the original copy&pasted code:
//testcase
- (void)test001_setCostumeFirstCostume
{
NSString *xmlString = #"<Bricks.SetCostumeBrick><costumeData reference=\"../../../../../costumeDataList/Common.CostumeData\"/><sprite reference=\"../../../../..\"/></Bricks.SetCostumeBrick>";
NSError *error;
NSData *xmlData = [xmlString dataUsingEncoding:NSASCIIStringEncoding];
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData
options:0 error:&error];
SetCostumeBrick *newBrick = [self.parser loadSetCostumeBrick:doc.rootElement];
if (![newBrick isMemberOfClass:[SetCostumeBrick class]])
STFail(#"Wrong class-member");
}
// "MyObject"
#implementation SetCostumeBrick
#synthesize indexOfCostumeInArray = _indexOfCostumeInArray;
- (void)performOnSprite:(Sprite *)sprite fromScript:(Script*)script
{
NSLog(#"Performing: %#", self.description);
[sprite performSelectorOnMainThread:#selector(changeCostume:) withObject:self.indexOfCostumeInArray waitUntilDone:true];
}
- (NSString*)description
{
return [NSString stringWithFormat:#"SetCostumeBrick (CostumeIndex: %d)", self.indexOfCostumeInArray.intValue];
}
#end
// superclass of SetCostumeBrick
#implementation Brick
- (NSString*)description
{
return #"Brick (NO SPECIFIC DESCRIPTION GIVEN! OVERRIDE THE DESCRIPTION METHOD!";
}
//abstract method (!!!)
- (void)performOnSprite:(Sprite *)sprite fromScript:(Script*)script
{
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:#"You must override %# in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}
#end
// the "factory" (a xml-parser)
- (SetCostumeBrick*)loadSetCostumeBrick:(GDataXMLElement*)gDataSetCostumeBrick
{
SetCostumeBrick *ret = [[SetCostumeBrick alloc] init];
NSArray *references = [gDataSetCostumeBrick elementsForName:#"costumeData"];
GDataXMLNode *temp = [(GDataXMLElement*)[references objectAtIndex:0]attributeForName:#"reference"];
NSString *referencePath = temp.stringValue;
if ([referencePath length] > 2)
{
if([referencePath hasSuffix:#"]"]) //index found
{
NSString *indexString = [referencePath substringWithRange:NSMakeRange([referencePath length]-2, 1)];
ret.indexOfCostumeInArray = [NSNumber numberWithInt:indexString.intValue-1];
}
else
{
ret.indexOfCostumeInArray = [NSNumber numberWithInt:0];
}
}
else
{
ret.indexOfCostumeInArray = nil;
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:#"Parser error! (#1)"]
userInfo:nil];
}
NSLog(#"Index: %#, Reference: %#", ret.indexOfCostumeInArray, [references objectAtIndex:0]);
return ret;
}
SOLUTION:
Eiko/jrturton gave me a link to the solution - thx: isMemberOfClass returns no when ViewController is instantiated from UIStoryboard
The problem was, that the classes were included in both targets (app and test bundle)
Thank you guys for your help :)
You generally want isKindOfClass:, not isMemberOfClass. The isKindOfClass: will return YES if the receiver is a member of a subclass of the class in question, whereas isMemberOfClass: will return NO in the same case.
if ([obj isKindOfClass:[MyObject class]])
For example,
NSArray *array = [NSArray array];
Here [array isMemberOfClass:[NSArray class]] will return NO but [array isKindOfClass:[NSArray class]] will return YES.
Ok, with different class addresses per your comment, I think I can track this down to be a duplicate of this:
isMemberOfClass returns no when ViewController is instantiated from UIStoryboard
Basically, your class is included twice.

Something is wrong with singleton...unable adding a child because it is nil

I use a singleton the first time and I don't really know how to implement it...
Ok I need to explain some things:
In Hexagon.h (which inherits from CCNode) I want to create multiple sprites (here referred to as "hexagons"). However, they are not added to the scene yet. They are being added in the HelloWorldLayer.m class by calling Hexagon *nHex = [[Hexagon alloc]init]; . Is that correct ? Is it then iterating through the for loop and creating all hexagons or only one ?
Well anyways, I have a singleton class which has to handle all the public game state information but retrieving is not possible yet.For instance I cannot retrieve the value of existingHexagons, because it returns (null) objects. Either I set the objects wrongly or I am falsely retrieving data from the singleton. Actually, I would even appreciate an answer for one of these questions. Please help me. If something is not clear, please add a comment and I'll try to clarify it.
What I have right now is the following:
GameStateSingleton.h
#import <Foundation/Foundation.h>
#interface GameStateSingleton : NSObject{
NSMutableDictionary *existingHexagons;
}
+(GameStateSingleton*)sharedMySingleton;
-(NSMutableDictionary*)getExistingHexagons;
#property (nonatomic,retain) NSMutableDictionary *existingHexagons;
#end
GameStateSingleton.m
#import "GameStateSingleton.h"
#implementation GameStateSingleton
#synthesize existingHexagons;
static GameStateSingleton* _sharedMySingleton = nil;
+(GameStateSingleton*)sharedMySingleton
{
#synchronized([GameStateSingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([GameStateSingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
}
return self;
}
#end
Hexagon.m
-(CCSprite *)init{
if( (self=[super init])) {
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *levelConfigPlistLocation = [mainPath stringByAppendingPathComponent:#"levelconfig.plist"];
NSDictionary *levelConfig = [[NSDictionary alloc] initWithContentsOfFile:levelConfigPlistLocation];
NSString *currentLevelAsString = [NSString stringWithFormat:#"level%d", 1];
NSArray *hexPositions;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
hexPositions = [[levelConfig valueForKey:currentLevelAsString] valueForKey:#"hexpositionIpad"];
}
else{
hexPositions = [[levelConfig valueForKey:currentLevelAsString] valueForKey:#"hexpositionIphone"];
}
NSString *whichType = [NSString stringWithFormat:#"glass"];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if ([whichType isEqualToString:#"stone"]){
hexagon = [CCSprite spriteWithFile:#"octagonstone.png"];
}else if([whichType isEqualToString: #"glass"]){
hexagon = [CCSprite spriteWithFile:#"octagoncolored1.png"];
}else if([whichType isEqualToString: #"metal"]){
hexagon = [CCSprite spriteWithFile:#"octagonmetal.png"];
}
NSMutableDictionary *eHexagons =[[GameStateSingleton sharedMySingleton] getExistingHexagons];
for (int i=0;i < [hexPositions count];i++){
CGPoint location = CGPointFromString([hexPositions objectAtIndex:i]);
CGPoint nLocation= ccp(screenSize.width/2 + 68 * location.x,screenSize.height/2 + 39 * location.y);
NSString *aKey = [NSString stringWithFormat:#"hexagon%d",i];
hexagon =[CCSprite spriteWithFile:#"octagoncolored1.png"];
hexagon.position = nLocation;
[eHexagons setObject:hexagon forKey:aKey];
[self addChild:[eHexagons valueForKey:aKey] z:3];
[[GameStateSingleton sharedMySingleton]setExistingHexagons:eHexagons];
}
NSLog(#"these are the existinghexagons %#", existingHexagons);
//This returns a dictionary with one (null) object
}
return hexagon;
}
HelloWorldLayer.m -> -(id)init method
Hexagon *nHex = [[Hexagon alloc]init];
First of all, it returns null because the existingHexagons array has never been initialized in the first place. Go to the init function of your singleton and add:
existingHexagons = [[NSMutableArray alloc]init];
As for your For Loop question, I did not get it. I recommend making one StackOverflow question per query instead of putting two in one.

UITableView and SearchBar problem

I'm trying to add a Search bar to my UITableView. I followed this tutorial: http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html.
I'm getting this error if I type a letter in the search box: Rooster(10787,0xa05ed4e0) malloc: *** error for object 0x3b5f160: double free
*** set a breakpoint in malloc_error_break to debug.
This error occurs here:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self handleSearchForTerm:searchString];
return YES;
}
(on the second line)
- (void)handleSearchForTerm:(NSString *)searchTerm {
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release];
}
//Empty the searchResults array
[[self searchResults] removeAllObjects];
//Check if the searchTerm doesn't equal zero...
if ([[self savedSearchTerm] length] != 0) {
//Search the whole tableList (datasource)
for (NSString *currentString in tableList) {
NSString *klasString = [[NSString alloc] init];
NSInteger i = [[leerlingNaarKlasList objectAtIndex:[tableList indexOfObject:currentString]] integerValue];
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
//Check if the string matched or the klas (group of school)
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound ||
[klasString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound) {
//Add to results
[[self searchResults] addObject:currentString];
//Save the klas (group of school). It has the same index as the result (lastname)
NSString *strI = [[NSString alloc] initWithFormat:#"%i", i];
[[self searchResultsLeerlingNaarKlas] addObject:strI];
[strI release];
}
[klasString release];
}
}
}
Can someone help me out?
Regards,
Dodo
The double free error means you have released an object more than needed. Here the suspicious object is klasString.
From your code:
NSString *klasString = [[NSString alloc] init];
...
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
...
[klasString release];
The assignment inside the if statement
loses reference to the newly allocated NSString, introducing a memory leak
makes the later release apply to the object from klassenList. When klassenList releases its elements, a double free error will occur.