I have a project at https://github.com/niklassaers/NJSNotificationCenter with so far only two unit tests. One of them runs, one of them runs 60% of the time. The remaining 40% of the time, it will fail because my NSMutableValue contains a nil value, even though I have never put in a nil value (nor should that be possible)
The problem arises here:
- (void) addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject priority:(NSInteger)priority {
NJSNotificationKey *key = [[NJSNotificationKey alloc] initWithObserver:observer name:aName object:anObject];
NSLog(#"Key is: %p", key);
key.priority = priority;
NJSNotificationValue *value = [[NJSNotificationValue alloc] initWithSelector:aSelector];
NSAssert(value, #"Value cannot be nil!");
#synchronized(observers) {
observers[key] = value;
NSLog(#"Key: %p\tValue: %p\t%#", key, value, observers);
if(observers[key] == nil)
NSLog(#"This can't be!");
}
}
I make a key, it is not nil, I make a value, it is not nil, I add it to my dictionary and get it back from the dictionary, but now it is nil! This makes no sense to me.
I have wrapped every access to observers (a local instance variable) in a #synchronized block just in case there was any other threading going on (there isn't).
Please check out my code (BSD license) and have a look at it, and help me understand how this can be. If you'd like, I'd love to pair program on this with you, I'm #niklassaers on Twitter
You haven't implemented hash.
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Collections/Articles/Dictionaries.html#//apple_ref/doc/uid/20000134-SW8
Keys must implement the hash and isEqual: methods because a dictionary
uses a hash table to organize its storage and to quickly access contained
objects
The dictionary is copying your key object and storing that - when it tried to lookup the original key object, it does not find it because the hash values do not match.
Related
Rather than a long if statement, what is a more compact and readable way to verify if a string is contained in a collection of possible values? In other words, check if a value is within a domain?
I want to do something like this…
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
BOOL valueFoundInDomain = [domain containsObject:#"elephant"];
But I'm concerned about equality checking with NSString. I want to check the value of the text, not object identity.
The documentation for NSArray says the containsObject method uses the isEqual method. But I cannot find in the documentation for NSString an explanation for its implementation of isEqual. The presence of the isEqualToString method suggests that isEqual may be doing something else. If that something else involves interning of string objects, then experimenting myself may give misleading results, so I'd like a documented answer.
I never use -isEqualToString:, only -isEqual: and it just works as it should! (I do this for years.)
NSString is implementing -isEqual: and it returns YES if the other object is a string and it has the same contents.
In Apples Objective-C documentation, methods that are overridden from a baseclass are often not explicitely documented. But -isEqual: is one of the few methods that is implemented in all foundation classes where it makes sense.
The isEqual method does an additional type check to ensure you are comparing two objects of the same class.
IsEqualToString assumes you are sending a string and will crash if you send a nil or object of another type.
Your code looks good for its use case.
Lets Try Using This
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
NSIndexSet *indexes = [domain indexesOfObjectsWithOptions:NSEnumerationConcurrent passingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [obj isEqualToString:#"elephant"];
}];
// Where indexes contains matched indexes of array elements
Here isqualToString: Returns a Boolean value that indicates whether a given string is equal to the receiver using a literal Unicode-based comparison.isEquealTo: Returns a Boolean value that indicates whether the receiver and a given object are equal. When you know both objects are strings, isEqualToString: is a faster way to check equality than isEqual:
Being a ReactiveCocoa newbie, I'm hoping for some advice with this:
I'm trying to create a dynamic form that contains multiple Field objects parsed from an XML file. Each Field can have muliple validation rules that will run against the Field's NSString *value param.
For the RAC part of the question-
inside each Field object, I want to bind BOOL completed to a signal that checks the Field's *value param against an array of rules. So far I've gotten here with my thinking:
#implementation Field
self = [super init];
if (self) {
RAC(self, completed) = [RACObserve(self, value) filter:^BOOL(NSString *fieldValue) {
NSLog(#"%s::self.completed = %d\n", sel_getName(_cmd), self.completed); // trying to watch the values here, with no luck
NSLog(#"%s::fieldValue = %#\n", sel_getName(_cmd), fieldValue); // same here, I'd like to be able to view the `*value` here but so far no luck
return [self validateCurrentValue]; // currently this method just checks value.length > 5
}];
}
return self;
The *value param has already been bound to my view model (successfully) and it gets updated each time a textfield changes.
What I'm looking for is a basic example or best-practice, the code above crashes when run so I know I'm missing something fundamental.
Thanks all
-filter: is simply passing values from RACObserve(self, value) through unchanged, but only if the block returns YES. So that means you're trying to set completed to values of whatever type value is. That's Probably Bad®.
But the good news is that you're really close!
Instead of filtering, you want to transform. You want to take every value and map it to something other thing. Namely whether that value passes validation. To do that, we use -map::
RAC(self, completed) = [RACObserve(self, value) map:^(NSString *fieldValue) {
return #([self validateCurrentValue]);
}];
This question already has answers here:
Should you use 'isEqual' or '=='?
(2 answers)
Comparing objects in Obj-C
(4 answers)
Closed 8 years ago.
I am reading the Programming with Objective-C . In the section of Determining Equality of Objects , it says the following words:
- When dealing with objects, the == operator is used to test whether two separate pointers are pointing to the same object:
if (firstPerson == secondPerson) {
// firstPerson is the same object as secondPerson
}
- If you need to test whether two objects represent the same data, you need to call a method like isEqual:, available from NSObject:
if ([firstPerson isEqual:secondPerson]) {
// firstPerson is identical to secondPerson
}
I get confused about the differences between == and isEqual with the above explanation, does it mean firstPerson == secondPerson is an alternative of [firstPerson isEqual:secondPerson] ?
The definition of == is correct, it checks to see that they're pointing to the actual same pointer/memory address (ie. 0xffffff)
The key to understanding what you're asking is to think about what you mean by the word "equal". "equal" typically means, from the developer's point of view, that "these two objects contain the same data in the fields that I require for all practical purposes". You can have two user objects each with the same userID property but different times in lastUpdated - would you consider them equal? Depends on your use case. Most likely you would say yes because they're the same user. They were updated from the server at different times, so some fields differ, but for your implementation, they're equal.
In the case above, are they the same object? Definitely not. They point to different memory addresses. So == would be NO, whereas if you wrote your isEqual: method to check just the userID property, it would return YES
The definition of isEqual: is entirely up to the author of the class. isEqual: can be written to use == if you wanted. All you have to do, in your class, is to override the isEqual: method which is defined by the NSObject protocol.
If you have a custom object, use isEqual: to define what your definition of equal is. In the example of a user object, you might define:
- (BOOL)isEqual:(id)otherObject {
if ([otherObject isKindOfClass:[self class]]) {
MyClass *otherObjectAfterCast = (MyClass*)otherObject;
if ([otherObjectAfterCast.userID isEqualToString:self.userID])
return YES;
}
return NO;
}
Technically you'd probably want to use caseInsensitiveCompare: or something like that but you get the drift...
isEqual: can also be used to trigger other methods - in the case of NSString - calling isEqual: when both operands are strings results in a call to isEqualToString: - which is why the documentation recommends calling isEqualToString: if you know they're both strings, since it's a bit faster.
So, isEqual: is whatever you make of it, or whatever the class author has defined it to be.
This is also a pretty clear definition in the docs (for once lol): NSObject Protocol Reference
Hope this helps! Let me know if you need any further clarification.
NSString *string1 = [[NSString alloc] initWithString:#"some string"];
NSString *string2 = [[NSString alloc] initWithString:#"some string"];
NSString *string3 = string2;
BOOL equal1 = (string1 == string2); // NO
BOOL equal2 = [string1 isEqual:string2]; // YES
BOOL equal3 = (string2 == string3); // YES
BOOL equal4 = [string2 isEqualToString:string3]; // YES
The simple version is this.
== tells you if the pointers are the same object or not.
The isEqual: family of methods do something different.
They tell you if the objects at the other end of the pointers are effectively the same based on some criteria such as the properties or ivars holding equal values or whatever logic is implemented in the method used. They may or may not be the exact same object.
I'm trying to save some data into database using CoreData so I created Entity named 'Client' with some attributes. Two of them are 'city' and 'post_code', both of String type. I also created Client class extending NSManagedObjects and I wrote some methods there.
-(void) setCity: (NSString*) city
{
[self setValue:city forKey:#"city"];
}
-(NSString*) getCity
{
return [self valueForKey:#"city"];
}
-(void) setPostCode: (NSString*) postCode
{
[self setValue:postCode forKey:#"post_code"];
}
-(NSString*) getPostCode
{
return [self valueForKey:#"post_code"];
}
getPostCode and setPostCode work as I expected but calling setCity or getCity is causing problems. Appication stops and method is looping in thread as you can see on screenshot.
Full size image
This is how I call those methods
if([databaseResult count] > 0)
c = [databaseResult objectAtIndex:0];
else
c = [NSEntityDescription insertNewObjectForEntityForName:#"Client" inManagedObjectContext:context];
[c setPostCode:[jsonData valueForKey:#"post_code_invoice"]];
[c setClientType:[jsonData valueForKey:#"company_type"]];
[c setCity:[jsonData valueForKey:#"city_invoice"]];
it always stops on setCity no matter what data I pass there, even that call doesn't work
[c setCity:#"aaa"];
Did anyone had similar problem?
The setValueForKey method is calling back into the same function because it has the same name.
From the KVC documentation
Default Search Pattern for setValue:forKey:
When the default implementation of setValue:forKey: is invoked for a property the following search pattern is used:
The receiver’s class is searched for an accessor method whose name matches the pattern set:.
If no accessor is found, and the receiver’s class method accessInstanceVariablesDirectly returns YES, the receiver is searched for an instance variable whose name matches the pattern _, _is, , or is, in that order.
If a matching accessor or instance variable is located, it is used to set the value. If necessary, the value is extracted from the object as described in “Representing Non-Object Values.”
If no appropriate accessor or instance variable is found, setValue:forUndefinedKey: is invoked for the receiver.
So when you call setValue:forKey: with the key city, the implementation calls setCity: and your implementation calls setValue:forKey:, and round and round you go.
Why are you even doing it this way rather than setting the value directly?
Or better still use properties and you don't even need to write setters or getters.
The other two methods work because the key names are different. (they have underscores
)
I have a situation where troops can attack buildings. Each troop keeps a pointer to its target.
#property (nonatomic, weak) Building *target;
In an update loop, the troops periodically cause damage to their target.
if (_target)
{
if (/*enough time has passed since last attack, attack again*/)
{
[_target attack];
if (_target.health <= 0)
{
[_target removeFromParentAndCleanup:YES]; //Cocos2d
_target = nil;
}
}
}
else /* Find new target */
The problem is:
troop1 deals the blow that fells building1 and moves on to building2
troop2 was attacking building1 but waits until its next attack to determine that building1 is now nil.
I realise the problem is that troop2's pointer has not been set to nil and instead I should be checking that the value of the pointer is nil.
I tried using if (*_target) but was met with the message
Statement requires expression of scalar type
If there a way to achieve this kind of comparison in Objective-C? What other options are there for determining when a value has changed? KVO? Some extensive delegate pattern?
It is the pointer itself that is set to nil when the object it points to is deallocated. if (objectPointer == nil) is always the way to check if an object is nil in Objective-C/Cocoa. If the pointer is not nil, it means the object in question has not in fact been deallocated. If you dereference a pointer to an object, you get a struct, hence the compiler error about needing a scalar value in the if expression.
So, in your case, if if(self.target != nil) is not giving you the result you expect, you should look for remaining strong references to the target (from other objects).
More broadly, as hinted at by trojanfoe's answer, you're relying on ARC's zeroing weak reference behavior for real program logic. In theory this is OK, as (contrary to his initial statement), ARC's zeroing weak behavior is reliable/deterministic. But, it does mean that you have to ensure that targets are always deallocated when they're no longer on the playing field (or whatever). This is a bit fragile. Zeroing weak references are intended as a way to avoid retain cycles (essentially a form of memory leak), rather than as a way to implement logic the way you're doing. The gist of trojanfoe's solution, where you explicitly register and unregister targets as necessary, is probably a more robust solution.
There may be something that I have overlooked here, but to check if the target2 property is nil, just do:
if ( self.target2 == nil ) {
// Something
}
I think you are relying too heavily on the implementation of ARC in that you only know if an object has been removed if the pointer is nil. This is non-portable and can you make any guarantee between the object being released and the pointer becoming nil?
Instead, use a central dictionary of objects, mapped against their unique ID and store just this unique ID rather than the object pointer itself. In this example I'm using a NSNumber for the key using an incrementing integer, but there are probably better keys that can be used. Also Object is the base class of any object you want to store in this dictionary:
// Probably ivars in a singleton class
unsigned _uniqueId = 1;
NSMutableDictionary *_objects;
- (NSNumber *)addObject:(Object *)object
{
NSNumber *key = [NSNumber numberWithUnsignedInt:_uniqueId++];
[_objects setObject:object forKey:key];
return key;
}
- (void)removeObjectForKey:(NSNumber *)key
{
[_objects removeObjectForKey:key];
}
- (Object *)getObjectForKey:(NSNumber *)key
{
return [_objects objectForKey:key];
}
And in your target, simply store the building key:
#property (strong) NSNumber *buildingKey;
and get the building via the methods provided:
Building *building = (Building *)[objectDictionary objectForKey:buildingKey];
if (building != nil)
{
// building exists
}
else
{
// building does not exist; throw away the key
buildingKey = nil;
}
Since target is a weak reference, your code should work "as-is", assuming that [_target removeFromParentAndCleanup:YES]; removes all strong references to the target.
When the last strong reference is removed, all of the weak properties pointing to it will automatically be set to nil. If they are not automatically set to nil, then there is still a strong reference to the target somewhere.
Find and remove that reference, and this will work fine.