I have a flow in my app that inserts Response objects into an array, This is the response object:
#interface AppResponse : NSObject
#property (nonatomic, assign) MotSDKState state;
#property (strong, nonatomic) NSError * _Nullable error;
#property (nonatomic, assign) NSInteger errorCode;
#property (strong, nonatomic) NSDictionary * _Nullable response;
#end
I want to check at the end of the flow if the array is correct, so I create a test:
var err = AppResponse()
err.errorCode = 1000
err.state = .failed
let expectedArray = [err]
XCTAssertEqual(self.responsesArray, expectedArray)
I also tried with Nimble:
expect(self.responsesArray).to(equal(expectedArray))
And I get for both of them this unit test failed error:
error: -[****.**** *****] : failed - expected to equal <[<Appesponse: 0x600003288240>]>, got <[<Appesponse: 0x6000032948d0>]>
Any idea what is the problem? how I can compare two arrays of objects?
Caveat
I'm assuming we're seeing most of the AppResponse code, and that you haven't implemented an [AppResponse isEqual:] method. If you have:
this answer doesn't apply
you possibly should include it with the question
Answer
Consider this Swift code (which maybe should have been the first thing you tried):
let a = AppResponse()
let b = AppResponse()
print(a == b) // prints "false"
print(a == a) // prints "true"
Behind the scenes, these last two lines are equivalent to:
print(a.isEqual(b))
print(a.isEqual(a))
which is the Objective-C equality method, [NSObject isEqual:] (see here).
The default implementation of that method just compares the pointers to the objects, not their contents. As we showed above, it will always be false unless you compare literally the same object.
If you want these comparisons to work, you need an [AppResponse isEqual:] method comparing AppResponse objects by their properties. Because you are using these in arrays, I think that also implies needing a compliant [AppResponse hash] method. There are many guides to help you with that, e.g. this one.
Related
This is my first time trying to use both ARC and Core Data. I can't seem to figure out why my code is crashing.
in the .h I have:
#interface Foo : NSObject {
}
#property (nonatomic,strong) NSString *name;
#property (nonatomic,strong) NSString *email;
#property (nonatomic) BOOL myBool;
#property (nonatomic) float myFloat;
in the .m
#implementation User
#dynamic name;
#dynamic email;
#dynamic myBool;
#dynamic myFloat;
User *user = (User *)[NSEntityDescription insertNewObjectForEntityForName:#"User" inManagedObjectContext:[appDelegate managedObjectContext]];
[user setName:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"name"]];
[user setEmail:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"email"]];
[user setAuthorisation_token:[[[dictionary objectForKey:#"user"] objectForKey:#"user"]objectForKey:#"authentication_token"]];
[user setMyFloat:5]; <------ crash when set to anything other than 0 (eg,setting to FALSE will crash it).
[user setMyBool:FALSE]; <---- crash when set this to anything other than 0.
Basically whenever I try to use a type other than a string I am getting EXEC crash on that particular line. When I use strings for everything it is fine. In my.xcdatamodeld file I have myFloat set to FLOAT and myBool set to BOOLEAN
kill
error while killing target (killing anyway): warning: error on line 2184 of "/SourceCache/gdb/gdb-1708/src/gdb/macosx/macosx-nat-inferior.c" in function "void macosx_kill_inferior_safe()": (os/kern) failure (0x5x)
quit
Program ended with exit code: 0
You can't use #dynamic for primitives (like float and BOOL) because Core Data won't create implementations for them.
So the reason why your code is crashing is because when you use #dynamic you are telling the compiler "I promise that an implementation for these getters and setters will be available at runtime". But since Core Data doesn't create them then your code tries to call methods that doesn't exist.
Instead there are two things you could do: Use an NSNumber for both the BOOL and the float or implement your own getters and setters.
Using NSNumber:
Core Data only uses objects and not primitives but you can specify boolean or float in the Model. When you call [user myFloat] you will actually get an NSNumber back with the float value inside it. To access the primitive you then call float f = [[user myFloat] floatValue];. The same thing goes for the boolean, it also gets stored in an NSNumber. So when you try to access it you will get back an NSNumber that you need to call BOOL b = [[user isMyBool] boolValue]; to get the primitive back.
The same thing goes the other way around, when setting myFloat and myBool, you need to store them inside an NSNumber, e.g. [user setMyFloat:[NSNumber numberWithFloat:f]]; and [user setMyBool:[NSNumber numberWithBool:b]];.
To use this approach you would have to change your last two properties to
#property (nonatomic, strong) NSNumber *myBool;
#property (nonatomic, strong) NSNubmer *myFloat;
but you can keep the #dynamic for both of them.
Implementing you own getters and setters:
For your convenience, you may want your "user" object to get and set the primitive types, float and BOOL, directly. In that case you should keep the properties as float and bool and in your implementation file (.m) remove the #dynamic lines for myFloat and myBool.
To implement the getter and setter you need to know a little about KVC/KVO and Core Data. In short: you need to tell the system when you are about to access or change a property and when yo u are done accessing or changing it, since Core Data won't do it for you. Between the "will access/change" and "did access/change" you are free to retrieve or modify the properties. One more caveat is that Core Data still cannot save the BOOL and float directly, so they need to be packaged into and unpackaged from NSNumbers when getting and setting.
Further, you can't call [self setValue:ForKey:]; or [self valueForKey:#""]; because that would cause the method you are in to call itself and throw you into an infinite loop. Core Data solves this use-case by allowing you to get and set the value without hitting your own implementation by calling [self setPrimitiveValue:ForKey:] and [self primiveValueForKey:]. Note: primiteValueForKey has nothing to do with primitive types (int, float, BOOL) but is just the name of the methods you use to get and set values in Core Data directly.
The implementation for your float and BOOL would look something like this:
- (float)myFloat
{
[self willAccessValueForKey:#"myFloat"];
float f = [[self primitiveValueForKey:#"myFloat"] floatValue];
[self didAccessValueForKey:#"myFloat"];
return f;
}
- (void)setMyFloat:(float)f
{
[self willChangeValueForKey:#"myFloat"];
[[self setPrimitiveValue:[NSNumber numberWithFloat:f] forKey:#"myFloat"];
[self didChangeValueForKey:#"myFloat"];
}
- (BOOL)isMyBool
{
[self willAccessValueForKey:#"myBool"];
BOOL b = [[self primitiveValueForKey:#"myBool"] boolValue];
[self didAccessValueForKey:#"myBool"];
return b;
}
- (void)setMyBool:(BOOL)b
{
[self willChangeValueForKey:#"myBool"];
[[self setPrimitiveValue:[NSNumber numberWithBool:b] forKey:#"myBool"];
[self didChangeValueForKey:#"myBool"];
}
Trying pass the recheck of ARC conversion, but I am not sure how to fix this issue. The method and the property are at odds and I am not sure what to do here:
- (void)getObjects:(id *)objects andKeys:(id *)keys {
return [self.items getObjects:objects andKeys:keys];
}
#interface SoapArray : SoapObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration> {
NSMutableArray* items;
}
#property (nonatomic, retain) NSMutableArray* items;
ERRORS WITH:
Sending '__autoreleasing id *' to parameter of type '__unsafe_unretained id *' changes retain/release properties of pointer
You need to update your signature to match the new ARC-compatible getObjects:andKeys:
- (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys;
The default memory semantic here would be __autoreleasing, but this method returns __unsafe_unretained objects, so you need to as well.
Keep in mind that these are __unsafe_unretained. When you get back your array of id, they have no memory management applied to them. So if self goes away, these objects probably will, too (and they won't zero the pointers like a weak property). That's usually ok and what you want, but keep it in mind.
Icon is set as #property (nonatomic, retain) AHGridIcon *icon;
Usually i just do:
-(void)setIcon:(AHGridIcon *)iconLocal {
icon = iconLocal;
}
But i read a guide to getters setters and properties online which has lead me to believe that instead, this is right:
-(void)setIcon:(AHGridIcon *)iconLocal {
if (iconLocal != self.icon)
{
NSLog(#"local: %#", iconLocal);
NSLog(#"self.icon 1: %#", self.icon);
[iconLocal retain];
[icon release];
icon = iconLocal;
NSLog(#"self.icon 2: %#", self.icon);
}
}
The problem is, the original icon is staying put, it's not being replaced with the new icon. What am i doing wrong? Should i just revert to the usual way i do it?
You should use '#synthesize' unless you really need custom setter behavior.
like I posted in my comment:
the best way is to use #synthesize which will create a getter and a setter to with respect to the properties you wrote in your property (nonatomic, retain) => not threadsafe but fast getter and setter and a retaining (and also releasing) setter. If you dont need sophisticating stuff to do in your setter then you should not override the setter.
.h:
#property (nonatomic, retain) AHGridIcon *icon;
.m:
#implementation Something
#synthesize icon;
...
#end
The code you posted in your setter is nearly the same as the compiler would produce when only using synthesize.
Your usual way is not really nice because in your header is defined (in your property) that the setter is retaining but in your implementation you are overriding that correct setter which doesn't retain. It is nearly the same as the compiler would produce with an (nonatomic, assign) property.
But if you want to override your setter then it should look like the same as you wrote. For me it is working fine.
first retaining the new object
then releasing the old one
then assigning the local pointer to your new object
you can even omit your if but then it is really important that you first retain the new and then release the old objects (like you did - just want to mention that).
For solving your problem with an overriten setter: Your setter looks ok in my eyes. Have you also overriten the getter? If yes then post it here (you use it by calling self.icon in your log-call).
I've done a small test-program
#synthesize str;
- (void)setStr:(NSString *)localStr
{
if(str != localStr)
{
NSLog(#"old : %#", self.str);
NSLog(#"new1: %#", localStr);
[localStr retain];
[str release];
str = localStr;
NSLog(#"new2: %#", self.str);
}
}
and the output is fine:
old : (null)
new1: Hello
new2: Hello
old : Hello
new1: World
new2: World
I have an NSMutableArray which is storing a list of other arrays. And when i run the code.
NSLog(#"%#",[[appDelegate teamRoster]objectAtIndex:[indexPath.row]class])
It returns and tells me that i am looking at an Array,
however when i try to do the following
[selectedRowerView tempArray] = [[appDelegate teamRoster]objectAtIndex:[indexPath.row]];
The program errors out. Anyone have any ideas why this might be happening?
You have to understand that [selectedRowerView tempArray] is actually a command / message that is being sent. In C++ equivalent, you are calling selectedRowerView->tempArray() = .... Which doesn't make logical sense because you cannot make an assignment to a function.
What you're trying to do is set the tempArray. If you have the proper setters/getters set-up, you can just run: selectedRowerView.tempArray = ...;
Just make sure that tempArray has a #property and is #synthesize'd.
How about this?
selectedRowerView.tempArray = [[appDelegate teamRoster]objectAtIndex:[indexPath.row]];
…assuming that tempArray is a synthesized property à la
#property (nonatomic, readwrite, retain) NSArray *tempArray;
#synthesize tempArray;
Clarification:
selectedRowerView.tempArray = …;
gets internally processed to
[selectedRowerView setTempArray:…];
which is a setter method.
While
selectedRowerView.tempArray;
gets internally processed to
[selectedRowerView tempArray];
which is a getter method.
Subtle but important difference.
The meaning of foo.bar depends on the very context (enclosing expression) it is used in.
So, I thought consulting you guys about my design, cause I sense there might be a better way of doing it.
I need to implement game bonuses mechanism in my app.
Currently there are 9 bonuses available, each one is based of different param of the MainGame Object.
What I had in mind was at app startup to initialize 9 objects of GameBonus while each one will have different SEL (shouldBonus) which will be responsible for checking if the bonus is valid.
So, every end of game I will just run over the bonuses array and call the isBonusValid() function with the MainGame object(which is different after every game).
How's that sound ?
The only issue I have currently, is that I need to make sure that if some bonuses are accepted some other won't (inner stuff)... any advice how to do that and still maintain generic implementation ?
#interface GameBonus : NSObject {
int bonusId;
NSString* name;
NSString* description;
UIImage* img;
SEL shouldBonus;
}
#implementation GameBonus
-(BOOL) isBonusValid(MainGame*)mainGame
{
[self shouldBonus:mainGame];
}
#end
Sounds ok, the only change I would consider is perhaps removing a bonus from the array should it be acepted. That way it is not checked in the future. This would also work for bonus that for other reason should no longer be available.
Whether or not the player can obtain a particular bonus according to the rules of your game isn't something the individual bonuses would know about. This is something the game itself would know. For example, you may have one game that allows bonuses A and B together, but another game that wouldn't.
So the logic to grant or deny a bonus should be in the MainGame object. I would organize it so that GameBonus is a plain bit bucket class and the logic is all in the MainGame. MainGame would be a superclass of any other custom games that might want to override the bonus logic.
A starting point:
typedef enum {
BonusTypeA, BonusTypeB, BonusTypeC
} BonusId;
#interface GameBonus : NSObject {
BonusId bonusId;
NSString *name;
NSString *description;
UIImage *img;
}
#property (nonatomic,assign) BonusId bonusId;
#property (nonatomic,retain) NSString *name;
#property (nonatomic,retain) NSString *description;
#property (nonatomic,retain) NSString *img;
#end
#interface MainGame : NSObject {
NSMutableSet *activeBonuses;
}
-(BOOL) tryToSetBonus:(BonusId)bonus; // tries to set, returns YES if successful.
-(BOOL) isBonusValid:(BonusId)bonus; // has no side effect, just check validity.
#end