How do I store a NSUInteger using the NSCoding protocol, given that there is no method on NSCoder like -encodeUnsignedInteger:(NSUInteger)anInt forKey:(NSString *)aKey like there is for NSInteger?
The following works, but is this the best way to do this? This does needlessly create objects.
#interface MYObject : NSObject <NSCoding> {
NSUInteger count;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:#"count"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [[decoder decodeObjectForKey:#"count"] unsignedIntegerValue];
}
return self;
}
NSNumber has a lot of methods to store/retrieve different sized and signed types. It is the easiest solution to use and doesn't require any byte management like other answers suggest.
Here is the types according to Apple documentation on NSNumber:
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value
- (NSUInteger)unsignedIntegerValue
Yes your code example is the best way to encode/decode the NSUInteger. I would recommend using constants for the key values, so you don't mistype them and introduce archiving bugs.
static NSString * const kCountKey = #"CountKey";
#interface MyObject : NSObject <NSCoding> {
NSUInteger count;
}
#end
#implementation MyObject
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:kCountKey];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [[decoder decodeObjectForKey:kCountKey] unsignedIntegerValue];
}
return self;
}
#end
The problem is that NSUInteger may not be the same size as an unsigned int. On many (if not most by now) Mac OS X machines, NSUInteger will be an unsigned long, which will be twice as big. On the other hand, a keyed archiver should handle that without a problem, since it builds a dictionary.
Further complicating matters is the fact that NSCoder doesn't have any methods to deal with unsigned types. I can't think of how this would cause any data loss, but it would require some ugly casts, plus it just feels dirty.
If you want to insist upon an unsigned type, the simplest way would be to encode the raw bytes (preferably in network byte order using htonl and ntohl) in the largest type available (unsigned long long) using encodeBytes:length:forKey: and decodeBytesForKey:returnedLength:. For maximum safety, you should check the length of what you decoded and cast the pointer (or use a union) to extract the correct-sized type.
The drawback of this is that the value will be represented in the output as data, not an integer. This mainly matters only if somebody decides to read in the raw plist data for your archive instead of using the keyed unarchiver like you do, and even then only to them. The other cases where it might matter is if Apple (or you) should ever switch to an architecture that has even larger integer types, types whose size in bits is not a power of two (there's at least one old platform where a word was 24 bits), or types with an unusual layout (not big- or little-endian).
As for your NSNumber solution: You might want to crack open your archive in Property List Editor and see what it emitted. If the output contains an integer element, then it's the same as using encodeInteger:forKey:. If the output contains a data element, then it's the same as the solution I mentioned above. To be thorough, you should check the output from every architecture you support.
That is the best way to do it. It does seem like needless work, but there's no other type available that can portably represent the entire range of values NSUInteger can hold.
You're going to want to use NSCoder's
encodeInt:ForKey:
and
decodeIntForKey:
methods. So, in your case you would need :
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInt:count forKey:#"count"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
count = [decoder decodeIntForKey:#"count"];
}
return self;
}
Hope that helps, also, there's plenty more encode "encode" methods, take a look at the documentation for NSCoder :D
Related
The following code, which maps simple value holders to an object, runs over 15x faster in Java than Objective-C using XCode 7 beta3, "Fastest, Aggressive Optimizations [-Ofast]". I can get over 280M lookups/sec in Java but only about 19M in the objc example. (I posted the corresponding Java code here as this started as a Swift comparison: Swift Dictionary slow even with optimizations: doing uncessary retain/release?).
This is a simplified version of my real code which is definitely bound by hash lookup time and exhibits this overall performance difference as well. In the test below I'm testing the value for null just to make sure the compiler doesn't optimize away the lookup, but in the real app I'd be using the value in most cases.
When I look at instruments I see a lot of time spent in retain / release, msgSend, and some locking calls that I don't understand.
Any ideas on what could account for this being 10-15x slower than Java or any workarounds would be appreciated. I can actually implement a perfect hash like the one below so I could use a fast int-object dictionary for iOS if I could find one.
#interface MyKey : NSObject <NSCopying>
#property int xi;
#end
#implementation MyKey
- (NSUInteger)hash { return self.xi; }
- (BOOL)isEqual:(id)object { return ((MyKey *)object).xi == self.xi; }
- (id)copyWithZone:(NSZone *)zone { return self; }
#end
NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:2501];
NSObject *obj = [[NSObject alloc] init];
int range = 2500;
for (int x=0; x<range; x++) {
MyKey *key = [[MyKey alloc] init];
key.xi=x;
[map setObject:obj forKey:key];
}
MyKey *key = [[MyKey alloc] init];
int runs = 50;
for (int run=0; run<runs; run++)
{
NSDate *start = [NSDate date];
int reps = 10000;
for(int rep=0; rep<reps; rep++)
{
for (int x=0; x<range; x++) {
key.xi=x;
if ( [map objectForKey:key] == nil ) { NSLog(#"missing key"); }
}
}
NSLog(#"rate = %f", reps*range/[[NSDate date] timeIntervalSinceDate:start]);
}
You could reimplement your -isEqual: method like this to avoid property accessors:
- (BOOL) isEqual:(id)other
{
return _xi == ((MyKey*)other)->_xi;
}
That would not be acceptable if your MyKey class might be subclassed, but I see from the Java code that the class there is final.
The computational complexity of the NSMutableDictionary is the next (from CFDictionary.h file):
The access time for a value in the dictionary is guaranteed to be at
worst O(N) for any implementation, current and future, but will
often be O(1) (constant time). Insertion or deletion operations
will typically be constant time as well, but are O(N*N) in the
worst case in some implementations. Access of values through a key
is faster than accessing values directly (if there are any such
operations). Dictionaries will tend to use significantly more memory
than a array with the same number of values.
Means, almost all the time you should have O(1) complexity for access/insertion/deletion. For Java HashMap you should get pretty much the same.
According to this research there are no benefits in using dictionaryWithCapacity: convenience initializer.
In case you use integer as a key, probably it would be possible to replace dictionary with array.
In this WWDC session they explained objc_msgSend performance issues and how to deal with them.
The first solution is to use C++ and STL containers. The second one is to use Swift, because unlike Objective-C it is only dynamic when it notes to be.
This question already has answers here:
Would it be beneficial to begin using instancetype instead of id?
(5 answers)
Closed 7 years ago.
I've just been reading and learning about instancetype and how in most cases it should be used instead of id in modern objective-c. Can I just ask when, then, would it be advisable to actually use id and not instancetype?
Thanks.
id
id is the generic type variable. Id doesn't warn us at compile time but it will crash if there is any problem.
Instancetype
instancetype does type checking for us at compile time to warn us of problems.
eg:
Animal.h
#interface Animal : NSObject
+ (id)giveMeAnimalA;
+ (instancetype)giveMeAnimalB;
+ (Animal *)giveMeAnimalC;
#end
Animal.m
#implementation Animal
+ (id)giveMeAnimalA {
return [[[self class] alloc] init];
}
+ (instancetype)giveMeAnimalB {
return [[[self class] alloc] init];
}
+ (Animal *)giveMeAnimalC {
return [[[self class] alloc] init];
}
#end
Suppose if we use [[Animal giveMeAnimalA] count];
The compiler will warn us of nothing, but we will crash at runtime with an exception because Animal doesn't have a count method.
And If we use [[Animal giveMeAnimalB] count];
The compiler would immediately warn us that Animal does not have a count method, and we could avoid crashing at runtime. But wouldn't it be simpler just to make our return type Animal* ?
Imagine we have a Dog subclass of Animal:
#interface Dog : Animal
- (void)makeSound;
#end
Now if we tried to call
[[Dog giveMeAnimalC] makeSound];
This wouldn't work because we would have been returned an Animal that doesn't have a makeSound method.
For complete last answer, i suggest you an example when Id is supported. It's on the ForIn Loop (fast enumeration)
Imagine, you have an array with three different objects like below :
NSArray *anotherArray = #[#"One element of Another Array",#"Second Element of Another Array"];
NSArray *array = #[#"First",#[anotherArray],#(12)];
for (id item in array)
{
if ([item isKindOfClass:[NSString class]])
{
NSLog(#"Im a NSString");
}
if ([item isKindOfClass:[NSArray class]])
{
NSLog(#"Im a NSArray");
}
if ([item isKindOfClass:[NSNumber class]])
{
NSLog(#"Im a NSNumber");
}
}
The id is a generic data type which can hold any type of data like nsstring,uiimage,nsarray and remaining all,so if you are having the requirements like returning the objects dynamically from a method you better use the return type of that method as id,hope you will get it
You can not use instancetype as return type when the type of the value that is returned is not known beforehand. If a method might return either an NSButton or an NSString depending on context, you can only use id.
instancetype is just a placeholder for the class that it is being used in; if a method of class Foo is like
- (instancetype) getMeFoo
then it is equivalent to
- (Foo *) getMeFoo
It can not return an NSString; the compiler would complain. However,
- (id) getMeFoo
can return any class type.
You could theoretically use a common superclass of the possibly returned types (for example, NSObject); but then you would need to typecast it when assigning to a concrete variable, or the compiler would bug you with warnings.
- (NSObject *) getMeFoo {
return #"foo!";
}
NSString *myString = (NSString *)[self getMeFoo];
The id type is "automatically" cast:
- (id) getMeFoo {
return #"foo!";
}
NSString *myString = [self getMeFoo];
But never forget to check if you really got the expected type:
NSString *myString = [self getMeFoo];
if (![myString isKindOfClass:[NSString class]]) {
// Danger, Will Robinson!
}
"I've just been reading and learning about instancetype and how in most cases it should be used instead of id in modern objective-c. Can I just ask when, then, would it be advisable to actually use id and not instancetype? Thanks."
You learned wrong. Kind of. The problem is that a language like Objective-C is complicated, and every rule will come with a long list of "do this IF a and b and c"... which you have to understand.
instancetype is used in one very particular situation: As the return type of init methods. You can't use for example UIButton* because an init method of UIButton could be used by a subclass, so the init method doesn't actually a UIButton but some subclass. That's why "id" was used which means "some object but I have no idea which object actually". "instancetype" on the other hand tells the compiler "you are clever, you figure it out. So with [[UIButton alloc] init] the compiler knows it returns UIButton*. [[MyButtonSubclass alloc] init] the compiler knows it returns MyButtonSubclass*.
In no other situation would you use instancetype.
Always give the compiler as much information as you can. If you have an object declared as UIButton* the compiler knows it's a UIButton or a subclass. If you have an object declared as id the compiler knows nothing. That means the compiler can't tell you if you do something stupid (like assigning a UIButton* to an NSString*, or calling the length method on a UIButton).
I am using NSUUID for unique ids in my app like so:
[[NSUUID UUID] UUIDString]
and as is the expected result I get an id like this one: 102A21AD-7216-4517-8A79-39776B767E72
For backend reasons I need the letters in the uuid to be lowercase. I tried to call
[[NSUUID UUID] UUIDString].lowercaseString
but the returned string is empty.
Do I really have to iterate over all of the characters in the string and convert the appropriate ones to lowercase? If so does anyone have any advice of the most efficient way to do this?
EDIT:
The way I was trying to implement this was by subclassing NSUUID and then overriding the
-(NSString*) UUIDString;
method.
My implementation of this was
-(NSString*) UUIDString{
return [super UUIDString].lowercaseString;
}
The accepted answer explains why this doesn't work.
Based on your edit to the question a little investigation is in order...
It looks like NSUUID behaves like a class cluster and you cannot sub-class it without implementing it's key methods and providing the functionality of UUID generation yourself. If you do sub-class it you get a parent class whose UUIDString is the empty string. While a standard init of the class gives you back an instance of __NSConcreteUUID whose UUIDString is more useful!
If the above is confusing the following partial implementation shows one way to do this:
#interface LowerUUID : NSUUID
#end
#implementation LowerUUID
{
NSUUID *real;
}
- (id) init
{
self = [super init];
if (self)
real = NSUUID.new;
return self;
}
- (NSString *) UUIDString
{
NSString *original = [real UUIDString];
NSString *lower = original.lowercaseString;
return lower;
}
#end
To be complete you also need to provide implementations of the other methods.
For this particular class it is unlikely you'll find this worth it, but for class clusters like NSMutableArray it does make sense.
You could submit a bug report to Apple stating the documentation does not state you cannot trivially sub-class NSUUID.
This works for me:
NSString *lower = [[[NSUUID UUID] UUIDString] lowercaseString];
I tried that and it works:
NSString *str = [[NSUUID UUID] UUIDString];
NSLog(#"1: %#", str);
NSLog(#"2: %#", str.lowercaseString);
I have a subclass of NSMutableArray (necessary to enforce certain restrictions on the contained objects). I encode the array as usual, and then decode it. The problem is that while the encoded class (given to NSKeyedArchiver) is a JOTypedMutableArray, the output class is another subclass of NSMutableArray (one of the private ones).
I looked into the archived data with LLDB: po [NSPropertyListSerialization propertyListFromData:archivedData mutabilityOption:0 format:NULL errorDescription:NULL]. The output of that command contained this:
"$classes" = (
NSMutableArray,
NSArray,
NSObject
);
"$classname" = NSMutableArray;
It appears to me that while the archiver is given a subclass of the class cluster, it is set to ignore the concrete subclasses and encode as the abstract superclass.
The question here is: how could I force the archiver to encode the object correctly? (Or am I doing something else wrong here?)
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
NSString *stringType = [aDecoder decodeObjectForKey:#"JOTypedMutableArrayType"];
Class classType = NSClassFromString(stringType);
_type = classType;
NSMutableArray *backingArray = [aDecoder decodeObjectForKey:#"JOTypedMutableArrayContents"];
_jo_backingArray = backingArray;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:NSStringFromClass(self.type) forKey:#"JOTypedMutableArrayType"];
[aCoder encodeObject:self.jo_backingArray forKey:#"JOTypedMutableArrayContents"];
}
When you encode using the property-list serialization you're restricting yourself to the basic property-list types: array, string, dictionary, data, some others I forget right now...docs are here.
This is a "feature" of NSPropertyListSerialization , because property-lists are textual and don't contain explicit type information. If you want to include all the type information you'll need to use a different class of archiver, check NSArchiver.
From what little I know of your case, though, I might recommend just changing the array back to the type you want when you read it in, so you can continue to use the nice text format.
Like, you could do:
NSMutableArray *backingArray = [JOTypedMutableArray arrayWithArray:[aDecoder decodeObjectForKey:#"JOTypedMutableArrayContents"]];
I'm sure I've got a basic problem in my C knowledge. I have one variable defined in the #interface:
uint * theBytes;
and then I have a method for checking the values of that array.
- (IBAction) checkNow {
NSLog(#"now? %d %d %d", theBytes[0], theBytes[1], theBytes[2]);
}
- (void)viewDidLoad {
[super viewDidLoad];
uint tryThis[3] = {72,2,244};
theBytes = tryThis;
[self checkNow];
}
The initial checkNow displays the values fine. If I call checkNow on a button press later, the values are totally different and strange.
I can do this with NSData quite easily, but I'd like to do it with straight arrays.
You can't assign a local array to a pointer that you want to stick around after the current stack frame. You have to use dynamic memory, obtained from malloc() or a similar function. If you want to use a byte array, you're going to have to malloc(), memcpy() and free() the memory appropriately every time you want to reassign it.
For example, that would be:
- (IBAction) checkNow {
NSLog(#"now? %d %d %d", theBytes[0], theBytes[1], theBytes[2]);
}
- (void)viewDidLoad {
[super viewDidLoad];
uint tryThis[3] = {72,2,244};
theBytes = malloc(3 * sizeof(uint));
memcpy(theBytes, tryThis, 3 * sizeof(uint));
[self checkNow];
}
// In order not to leak, you'll also need:
- (void)dealloc {
free(theBytes);
[super dealloc];
}
Yes, working with C arrays is a pain. This is why it's generally avoided in every other language, even close relatives like C++ and Objective-C.