The NSOrderedSet Class Reference Overview says:
You can use ordered sets as an alternative to arrays when the order of elements is important and performance in testing whether an object is contained in the set is a consideration— testing for membership of an array is slower than testing for membership of a set.
Which methods are considered "testing for membership"? Just containsObject:? Or, will indexOfObjectPassingTest: also be faster?
I'm asking because if I just have the object's ID (from the server for example) and want to check if the ordered set contains an object with that ID, I'd use indexOfObjectPassingTest:. But, that method, since it tests every object in the collection, seems that it'd be just as slow as it is for an array. On the other hand, containsObject: seems that it'd be faster since it takes advantage of the NSObject methods hash & isEqual:. I could just create a probe object with the ID I have, and then use containsObject:. But then, if the ordered set already contains an object with that ID, I'll just discard of the probe object and update the properties on the object already in the ordered set. It seems like extra work to have to create a probe object first. In that case, is it even worth using an ordered set over an array?
Also, I would be sorting objects by their date, not their ID.
I'd use an NSMutableDictionary with object IDs mapping to objects, as St3fan suggested, but I also want to display the objects in a UITableView.
The best way to test this is by writing some small benchmarks. I don't know how many objects you are dealing with but if it is less then a few hundred then you probably won't notice much difference between containsObject:, indexOfObjectPassingText: or even just iterating over all objects manually.
It sounds like an NSMutableDictionary is more appropriate for your use case actually. Why don't you store your objects in a dictionary that is indexed by your object's ID? Then you can really quickly find them by ID and you can also iterate easily over them if needed.
You can override -isEqual: and -hash in you class. If you do, it'll work with NSOrderedSet's fast lookup. It can be as simple as:
- (BOOL)isEqual:(id)otherObject
{
return self.myID == otherObject.myID;
}
- (NSUInteger)hash
{
return self.myID;
}
Here's a full example:
#import <XCTest/XCTest.h>
#interface MyClass : NSObject
#property (nonatomic) NSInteger myID;
#property (nonatomic, strong) NSDate *date;
#end
#implementation MyClass
- (BOOL)isEqual:(MyClass*)otherObject
{
return self.myID == otherObject.myID;
}
- (NSUInteger)hash
{
return self.myID;
}
#end
#interface MyTests : XCTestCase
#end
#implementation MyTests
- (void)testExample
{
MyClass *obj1 = [[MyClass alloc] init];
obj1.myID = 1;
obj1.date = [NSDate dateWithTimeIntervalSince1970:20000];
MyClass *obj2 = [[MyClass alloc] init];
obj2.myID = 2;
obj2.date = [NSDate dateWithTimeIntervalSince1970:10000];
MyClass *obj3 = [[MyClass alloc] init];
obj3.myID = 1;
obj3.date = [NSDate dateWithTimeIntervalSince1970:30000];
MyClass *obj4 = [[MyClass alloc] init];
obj4.myID = 3;
obj4.date = [NSDate dateWithTimeIntervalSince1970:30000];
NSOrderedSet *set = [[NSOrderedSet alloc] initWithArray:#[obj1, obj2]];
XCTAssertEqualObjects(((MyClass *)[set firstObject]).date, obj1.date);
XCTAssertEqualObjects(((MyClass *)[set lastObject]).date, obj2.date);
XCTAssertTrue([set containsObject:obj1]);
XCTAssertTrue([set containsObject:obj3]);
XCTAssertFalse([set containsObject:obj4]);
}
#end
Related
I have many "model" objects whose properties are defined as "readonly" and shared among various components.
In some cases I need to create local mutable copies of the objects (using them for local mutable state)
I rather not implement NSMutableCopy protocol as the object should be immutable after it is created. The modified object could be "passed" around after copy+mutate operations.
Is there a suggested mechanism , or should I just implement a constructor receiving the "changed" parameters?
For example an object which parses a JSON to native types :
#interface ImmutableObject : NSObject
// various "readonly" properties
...
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
#property (nonatomic, readonly) MyClass1 *prop1;
#property (nonatomic, readonly) MyClass2 *prop2;
...
#property (nonatomic, readonly) NSArray<MyClass100 *> *prop100;
#end
#implementation
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
self = [super init];
[self parseDictionaryToNative:jsonDictionary];
return self;
}
#end
Somewhere in code:
ImmutableObject *mutated = [immutableObject mutableCopy]; // best way to accomplish this?
// change some values...
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // save the new object
#spinalwrap is correct, but in this case there is no reason to create the extra copy before storing it. NSMutableArray is a subclass of NSArray, so can be used anywhere an NSArray can be used (and this is very common). Same for yours. In your particular case, you'd probably do it this way:
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = mutated; // Since `state` is an immutable type,
// attempts to mutate this later will be compiler errors
This is safe because you know that this block of code is the only block that has a reference to the mutable version of the object (because you created it here).
That said, once you've created a mutable subclass, you now need to consider the possibility that any ImmutableObject you are passed might actually be a MutableObject, and so make defensive copies (just as is done with NSArray, NSString, etc.) For example:
- (void)cacheObject:(ImmutableObject *)object {
// Need to copy here because object might really be a MutableObject
[self.cache addObject:[object copy]];
}
This is made fairly efficient by implementing copy on ImmutableObject and return self, and implementing copy on MutableObject as an actual copy, usually like this:
ImmutableObject.m
- (ImmutableObject *)copy {
return self;
}
MutableObject.m
// as in spinalwrap's example
- (MutableObject *)mutableCopy {
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
// No need to duplicate code here. Just declare it immutable;
// no one else has a pointer to it
- (ImmutableObject *)copy {
return (ImmutableObject *)[self mutableCopy];
}
So the copy is almost free if the object was immutable already. I say "fairly efficient" because it still causes some unnecessary copies of mutable objects that are never mutated. Swift's copy-on-write system for value types was specifically created to deal with this problem in ObjC. But the above is the common pattern in ObjC.
note that NSMutableArray, NSMutableData etc. are different classes than their immutable counterparts. So in this case, you maybe should define a MutableObject class with the same interface as the ImmutableObject class (but with mutable properties) and use that if you want to have a mutable object.
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // creates an ImmutableObject
the implementation of ImmutableObject's mutableCopy could be something like:
- (MutableObject *) mutableCopy
{
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
and MutableObject's copy method could look like this:
- (ImmutableObject *) copy
{
ImmutableObject *instance = [ImmutableObject new];
instance.prop1 = [self.prop1 copy];
// etc...
return instance;
}
You're not forced to use the NSMutableCopy protocol formally, but you can.
i would like to add extra data to EKEvent, i tried NSDictionary (there is a lot of data to add) but it doesn't work..
sample code:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[eventStore setValue:dictionary forKey:MAIN_DICTIONARY];
any ideas?
You're using setValue:forKey: in a wrong way. That a look here. There are different options to achieve what you want: category, subclassing or create a class that contains the EKEvent and the NSMutableDictionary. It depends on how you need to use the EKEvent.
You cannot do it this way, because even with key-value coding you can only set (declared or non declared) properties known by the instance. Basically the accessors (setter, getter) are executed. But there is no property MAIN_THREAD,no setter setMAIN_THREAD: in EKEvent.
If you want to extend instances of a foreign class that are created by the system (the instances, not the class), there are to ways to add data:
You create an own class, let's say MyEvent and give them a reference to the system instance (EKEvent) as a property plus the properties you need. When you get an instance of EKEvent you look-up your list of MyEventss using the identifier. With that you have the full access to your data.
You use associated objects. But you have to take care that they are not handled by the instance, i. e. while copying.
The first solution is better by far. Simple sample code:
#interface MyEvent : NSObject
#property (readonly) EKEvent* systemEvent;
#property id customProperty;
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent;
#end
#implemenation MyEvent
// Look-Up
NSMutableDictionary *eventLookUp;
+ (void)initialize
{
if( self == [MyEvent class])
{
eventLookUp = [NSMutableDictionary new];
}
}
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent
{
return eventLookUp[systemEvent.calendarItemIdentifier];
}
// Instance creation
- (instancetype)initWithSystemEvent:(EKEvent*)systemEvent
{
// Usual initializer
…
eventLookUp[systemEvent.calendarItemIdentifier] = systemEvent;
return self;
}
+ (instancetype)newEventWithSystemEvent:(EKEvent*)systemEvent
{
return [[self alloc] initWithSystemEvent:systemEvent];
}
#end
Typped in Safari
I have a core data entity that has a structure like this:
number (integer 16),
reference (binary)
image (binary)
I have created a class for that entity.
The header of that class has these declarations for the entity property.
#property (nonatomic, retain) NSNumber * number;
#property (nonatomic, retain) NSData * reference;
#property (nonatomic, retain) NSData * image;
but in fact, these 3 properties are
number = NSInteger
reference = NSArray
image = UIImage
because I cannot store arrays and images on core data directly, I have to convert it to NSData to save but I don't care for the property being declared as NSData, because the conversion to and from NSData is something internal to the entity and should not be exposed to code outside the class.
I want these header properties to be declared like
#property (nonatomic, assign) NSInteger number;
#property (nonatomic, retain) NSArray * reference;
#property (nonatomic, retain) UIImage * image;
and I want to, for example, when I assign an array to reference that is converted to NSData internally.
I know I have to create setters and getters to do that but my problem is that the entity is already using these names. Obviously I can rename all core data entities to have a prefix like xnumber, xreference, ximage, etc., so I will have no collisions between the names/types I want to expose and those I want to hide internally.
Is there any other alternative to that? My fear is ending with a sea of references that are similarly.
What you did in your application is probably most common fail using core data. By no means you should be encouraged to use the core data classes directly as you did, do always use subclassing, categories or (best of all) use wrappers.
Since in most cases when you edit an entity in your model you wish to delete the auto generated file(s) and create new ones those files should be unmodified. This is the main reason I discourage you to use any quick fixes such as modifying the names in your model and then creating the custom setters.
Why I suggest the wrappers most is because you can build your own interface with it. You can create exactly as many methods, accessories as you need on it, you can use data protection such as having read-only parameters... So when you modify the data model there should be no difference in the application at all, when you will possibly add some extra tables for some optimisations or some internal functionality you will have no issues hiding those accessories. Other then that having an extra layer will make it very easy for you to create some caching, easy debugging since you can put a breakpoint or log to more or less any and every accessory, you can internally maintain multithreading operations...
I can understand at this point migrating your code to some other system might take a bit long but that is something you should consider. If the application is anything but almost done I suggest you do migrate it: If you create a wrapper with same properties as are already used in the application it is possible to simply change the class names where it was already used, this shouldn't take too long. If you choose to continue working as it is you will most likely encounter some much harder issues and if nothing else remember this when you will start a new application.
Edit: Wrapper explanation and example
By wrapper I mean a class instance that holds another instance and builds an interface around it. Let me show you a nice example first:
Interface:
#interface EntityWrapper : NSObject
#property NSInteger number;
#property UIImage *image;
+ (NSArray *)fetchAll;
+ (void)invalidateCache;
#end
Implementation:
#class EntityName;
static NSArray *__entityCache = nil;
#interface EntityWrapper() {
EntityName *_boundEntity;
}
#end
#implementation EntityWrapper
- (instancetype)initWithEntity:(EntityName *)entity {
if((self = [super init])) {
_boundEntity = entity;
}
return self;
}
+ (NSArray *)fetchAll {
if(__entityCache == nil) {
NSMutableArray *toReturn = [[NSMutableArray alloc] init];
NSArray *entityArray = nil; //fetch from data base
for(EntityName *entity in entityArray)
[toReturn addObject:[[EntityWrapper alloc] initWithEntity:entity]];
__entityCache = [toReturn copy];
}
return __entityCache;
}
+ (void)invalidateCache {
__entityCache = nil;
}
- (void)setNumber:(NSInteger)number {
_boundEntity.number = #(number);
}
- (NSInteger)number {
return [_boundEntity.number integerValue];
}
- (void)setImage:(UIImage *)image {
_boundEntity.image = UIImagePNGRepresentation(image);
}
- (UIImage *)image {
return [[UIImage alloc] initWithData:_boundEntity.image];
}
#end
As you can see here I am building an interface around the entity using custom setters and getters. I even create a method to fetch all objects from the data base, next step might be to fetch them with some predicate or in this case rather some custom options. I also added a most simple cache just to see the concept.
The number is now a NSInteger instead of a NSNumber which can be quite a convenience but be careful doing this as you might need to know if number is nil. For this case you could also create another property such as BOOL numberIsSet.
Pretty much the same goes for the image. You need no transformers at all, just a getter and a setter (which is pretty much the same with transformers but this approach is much more dynamic).
So creating the wrapper kind of gives you the ultimate power. The idea is to create as small interface as possible and as simple as possible. That means there are just as many methods in the header file as needed, rest is hidden. The logic behind these methods can be extremely complicated but will still be maintainable since it is a closed system (does not depend on nothing but the entity), to give you an example:
Interface:
#property (readonly) NSDecimalNumber *heavyValue;
Implementation:
- (NSDecimalNumber *)heavyValue {
NSDecimalNumber *valueA = _boundEntity.valueA;
NSDecimalNumber *valueB = _boundEntity.valueB;
NSDecimalNumber *valueC = _boundEntity.valueC;
return [[valueA decimalNumberByAdding:valueB] decimalNumberByDividingBy:valueC];
}
Now this is quite a standard procedure and will work great but this method can be quite heavy on the CPU. If this method is called a lot you might get to a point where you want to optimise by storing the result into the entity itself. So all you do is add another value into the model heavyValue and the code:
- (NSDecimalNumber *)heavyValue {
NSDecimalNumber *toReturn = _boundEntity.heavyValue;
if(toReturn == nil) {
NSDecimalNumber *valueA = _boundEntity.valueA;
NSDecimalNumber *valueB = _boundEntity.valueB;
NSDecimalNumber *valueC = _boundEntity.valueC;
toReturn = [[valueA decimalNumberByAdding:valueB] decimalNumberByDividingBy:valueC];
_boundEntity.heavyValue = toReturn;
}
return toReturn;
}
- (void)setValueA:(NSDecimalNumber *)valueA {
_boundEntity.valueA = valueA;
_boundEntity.heavyValue = nil; //this invalidates the value
}
So that is quite an extreme change in logic behind a simple getter but the rest of your code is unharmed, it still all works as it should.
I have an object: indivOrder:
#interface indivOrderDetails : NSObject{
NSNumber* shirtNumber;
NSNumber* pantsNumber;
NSNumber* jacketNumber;
NSNumber* laundryNumber;
NSNumber* blouseNumber;
NSNumber* blazerNumber;
NSNumber* skirtNumber;
NSNumber* suitNumber;
NSString* pickUpOrDropOff;
NSString* pickUpFrom;
NSNumber* totalOrderPrice;
}
They're all given the interface of
#property (nonatomic, retain) NSNumber* propertyName
I have three steps.
First I retrieve the data from a text field:
shirtNumber = [self convertStringToNumber:shirtField.text];
Second, I use this convertStringToNumber method.
-(NSNumber*) convertStringToNumber:(NSString*)stringToConvert
{
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *myNumber = [f numberFromString:stringToConvert];
return myNumber;
}
Then I assign that value to my object variable.
orderDetails.shirtNumber = shirtNumber;
But the only value I'm coming back with when I try to access the orderDetails.shirtNumber variable is zero. The shirtNumber is coming back with the correct value from the ViewController.
Your problem likely lies here:
shirtNumber = [self convertStringToNumber:shirtField.text];
Look at that to which you're assigning. It's one of the ivars you declared. That ivar, despite what you may think, is not the backing for your property of the same name. The backing instead would be
_shirtNumber
since you don't appear to have synthesized (i.e., used the #synthesize directive) any accessors.
As a result, _shirtNumber and shirtNumber are two distinct entities, which means that when you attempt to access
orderDetails.shirtNumber
of course it's going to be nil. You never put anything in it.
So you have two choices: Either use the property name that's prefixed with an underscore
_shirtNumber = [self convertStringToNumber:shirtField.text];
or use #synthesize to set up some accessors, in which case you'd do this
self.shirtNumber = [self convertStringToNumber:shirtField.text];
or this (if you like the old form)
[self setShirtNumber:[self convertStringToNumber:shirtField.text]];
Guess that's three choices. Anyway, it's a subtle, classic 'gotcha.' If you want further background for this, you can find it in this very excellent explanation elsewhere on Stack Overflow.
Good luck to you in your endeavors.
Hi a very simple app it takes in 2 arguments via 2 text boxes, and then totals them and displays them in a label called result. The idea is to have it handled via an object called brain, for which in the later part i have given the code. problem is foo is zero and when you click the button the result goes to nothing.
The plan is to use this to build a better model view architecture for a bigger app i have completed.
#import "calbrain.h"
#import "ImmyViewController.h"
#interface ImmyViewController ()
#property (nonatomic, strong) calbrain *brain;
#end
#implementation ImmyViewController
#synthesize brain;
#synthesize num1;
#synthesize num2;
#synthesize result;
-(calbrain *) setBrain
{
if (!brain) {
brain = [[calbrain alloc] init];
}
return brain;
}
- (IBAction)kickit:(UIButton *)sender {
NSString *number1 = self.num1.text;
NSString *number2 = self.num2.text;
NSString *foo;
foo = [brain calculating:number1 anddouble:number2];
self.result.text = foo;
// self.result.text = [brain calculating:self.num1.text anddouble:self.num2.text];
}
#end
#implementation calbrain
-(NSString *) calculating:(NSString *)number1 anddouble:(NSString *)number2
{
double numb1 = [number1 doubleValue];
double numb2 = [number2 doubleValue];
double newresult = (numb1 + numb2);
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSString *numberAsString = [numberFormatter stringFromNumber:[NSNumber n numberWithFloat:newresult]];
return numberAsString;}
Check your brain using NSLog in the (IBAction)kickit:(UIButton *)sender function. I guess you didn't initialise brain. If this is not the case, you need to provide more code.
i did just that, i came to the conclusion the setter for brain isnt working properly
i put the alloc init line of code before i needed to alloc init the brain, and it works fine, i stubbed out the setter,
i will go back and see why it wasnt overriding the setter made by properties, but interesting stuff none the less. it means i can change my actual larger app to have a cleaner more organised architecture.
thanks for your time.
Try initializing your brain object in viewDidLoad() using your setter method. You have to call setter method to get your brain object initialized.
Something like this
viewDidLoad()
{
brain = [self setBrain];
//You can also do this
brain = [[calbrain alloc] init];
}
and use that brain object in your (IBAction)kickit: method.
Hope this helps.