Objective-C: NSArray of Custom Class? - ios

I am trying to learn iOS, I come from Java background where we can have lists/arrays of specific classes like
List<String> l = new ArrayList<>();
List<MyClass> l = new ArrayList<>();
Looking at Objective-C, I can make use of NSArray class to make immutable arrays, but how do I specify that this NSArrray is strictly of type MyClass?

Now Xcode 7 supports some kind of generics for standard collections(e.g. NSArrays). So you can make an array and provide kind of storing objects like this:
NSArray<NSString*> *myArrayOfStrings;

As far as I know, there isn't a built-in mechanism for specifying the type of objects put into NSArrays in Objective-C, but it looks like Swift does what you're looking for if that helps at all:
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_172
On a slightly related note, there's a decent write-up here about enforcing inserting objects of only a certain type into a subclassed NSMutableArray and throwing exceptions if trying to insert the wrong object types:
NSMutableArray - force the array to hold specific object type only

Sadly there are no generics in Objective-C.

Related

Swift collection that has defined types and copies by reference?

In converting old projects from Objective-C to Swift, I've mostly been able to use Dictionary in place of NSMutableDictionary. But in some cases, it's a hassle or uses a lot of extra memory to have the Dictionaries copying by value.
I thought I could simply change some Dictionary collections to NSMutableDictionary to make them objects that copy by value, but I don't see a way to specify the key and value types. This works in Objective-C:
NSMutableDictionary<NSString*, NSString*> *dict = [NSMutableDictionary dictionary];
But this gives an error "Cannot specialize non-generic type 'NSMutableDictionary'" in Swift:
let dict: NSMutableDictionary<String, String> = [:]
Is there a way to specify the types so I don't have to be constantly casting the values I get out of the dictionary?
Alternatively, is there another kind of collection object that supports key and value types like Dictionary but copies by reference like NSMutableDictionary?
UPDATE
I tried using NSMapTable as suggested in the comment below. That's missing some features of NSDictionary (e.g., it doesn't conform to IteratorProtocol), so I made a subclass to try making a drop-in replacement for Dictionary. I then ran into problems making my subclass generic since Swift and Objective-C have different support for that.
Since that would either require a lot of casting (or making a different subclass for each type of data I wanted to store in the dictionary), I then tried just using NSMutableDictionary and casting all the values when I read them. Unfortunately, after all that work, I couldn't see any difference in memory usage compared to using Dictionary.
So I guess having collections that copy by value isn't really my problem. It shouldn't be since I'm never retaining anything for very long, but I didn't see these memory problems until I migrated from Objective-C. I'll have to do more testing and explore other solutions.
The Objective-C specification:
NSMutableDictionary<NSString*, NSString*>
Is not a true implementation of Generics. It simply gives hints to the compiler that the dictionary will contain strings as the keys and values and the compiler can tell you, at compile time, if you make a simple mistake.
At the end of the day, there is nothing at runtime to enforce those type specifications. An NSDictionary (mutable or not) will have id for the keys, and id for the values and the runtime format will not change. That's why you can get away with using [NSMutableDictionary dictionary] to initialize all NSDictionaries... the type spec only has meaning at compile time.
In contrast when you use a identical syntax in Swift, say Dictionary<String, Int>, you are using true generics. The runtime representation of the dictionary may change depending on what key and value types you use.
In other words, in spite of similarities in their in Syntax, the <type, type> construct in Objective-C and in Swift mean VERY different things.
In Swift's Eyes, an NSDictionary (mutable or not) is simply a NSObject, just like every other NSObject so NSDictionary<NSString, NSString> is a nonsensical use of the generic type specification syntax - you're saying you want to use generics with a type that is not a generic type (hence the error).
There is no Swift syntax (that I'm aware of) that lets you specify the type you'd like to stand in for NSObject in things like NSDictionaries and NSArrays. You're going to have to use casting.
Even in Objective-C the type specs mean nothing and it's possible to squeeze something in there that doesn't belong. Consider:
NSDictionary<NSString *, NSString *> *myDictionary = [NSMutableDictionary dictionary];
((NSMutableDictionary *)myDictionary)[[NSNumber numberWithInt: 3]] = [NSURL URLWithString: #"http://www.apple.com"];
Here I declare the dict to use Strings, then shove in a number and a URL. The only way to guard against this would be to check the types, that is to do typecasting (or at least type-checking), for each key and value. Most folks code doesn't do that because it would be a pain, but the only way to get true safety.
Swift, in contrast, focus on the safety right up front. It's one of the defining differences between Swift an Objective-C. So you have to go through the pain if you insist on using "unsafe" Objective-C types.

Xcode Parse Issue Expected '>', Expected ')' [duplicate]

I'm new to Mac/iPhone programming and Objective-C. In C# and Java we have "generics", collection classes whose members can only be of the type declared. For example, in C#
Dictionary<int, MyCustomObject>
can only contain keys that are integers and values that are of type MyCustomObject. Does a similar mechanism exist in Objective-C?
In Xcode 7, Apple has introduced 'Lightweight Generics' to Objective-C. In Objective-C, they will generate compiler warnings if there is a type mismatch.
NSArray<NSString*>* arr = #[#"str"];
NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'
And in Swift code, they will produce a compiler error:
var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'
Lightweight Generics are intended to be used with NSArray, NSDictionary and NSSet, but you can also add them to your own classes:
#interface GenericsTest<__covariant T> : NSObject
-(void)genericMethod:(T)object;
#end
#implementation GenericsTest
-(void)genericMethod:(id)object {}
#end
Objective-C will behave like it did before with compiler warnings.
GenericsTest<NSString*>* test = [GenericsTest new];
[test genericMethod:#"string"];
[test genericMethod:#1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'
but Swift will ignore the Generic information completely. (No longer true in Swift 3+.)
var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'
Aside from than these Foundation collection classes, Objective-C lightweight generics are ignored by Swift. Any other types using lightweight generics are imported into Swift as if they were unparameterized.
Interacting with Objective-C APIs
This answer is outdated but remains for historical value. As of Xcode 7, Connor's answer from Jun 8 '15 is more accurate.
No, there are no generics in Objective-C unless you want to use C++ templates in your own custom collection classes (which I strongly discourage).
Objective-C has dynamic typing as a feature, which means that the runtime doesn't care about the type of an object since all objects can receive messages. When you add an object to a built-in collection, they are just treated as if they were type id. But don't worry, just send messages to those objects like normal; it will work fine (unless of course one or more of the objects in the collection don't respond to the message you are sending).
Generics are needed in languages such as Java and C# because they are strong, statically typed languages. Totally different ballgame than Objective-C's dynamic typing feature.
No, but to make it clearer you can comment it with the type of object you want to store, I've seen this done a few times when you need to write something in Java 1.4 nowadays) e.g.:
NSMutableArray* /*<TypeA>*/ arrayName = ....
or
NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
This was released in Xcode 7 (finally!)
Note that in Objective C code, it's just a compile-time check; there will be no run-time error just for putting the wrong type into a collection or assigning to a typed property.
Declare:
#interface FooClass <T> : NSObject
#property (nonatomic) T prop;
#end
Use:
FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];
Be careful about those *s.
There are no generics in Objective-C.
From the Docs
Arrays are ordered collections of objects. Cocoa provides several array classes, NSArray, NSMutableArray (a subclass of NSArray), and NSPointerArray.
Apple has added generics to ObjC in XCode 7:
#property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
see here:
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID61
Generic NSArrays can be realized by subclassing NSArray, and redefining all provided methods with more restrictive ones. For example,
- (id)objectAtIndex:(NSUInteger)index
would have to be redefined in
#interface NSStringArray : NSArray
as
- (NSString *)objectAtIndex:(NSUInteger)index
for an NSArray to contain only NSStrings.
The created subclass can be used as a drop-in replacement and brings many useful features: compiler warnings, property access, better code creation and -completion in Xcode. All these are compile-time features, there is no need to redefine the actual implementation - NSArray's methods can still be used.
It's possible to automate this and boil it down to only two statements, which brings it close to languages that support generics. I've created an automation with WMGenericCollection, where templates are provided as C Preprocessor Macros.
After importing the header file containing the macro, you can create a generic NSArray with two statements: one for the interface and one for the implementation. You only need to provide the data type you want to store and names for your subclasses. WMGenericCollection provides such templates for NSArray, NSDictionary and NSSet, as well as their mutable counterparts.
An example: List<int> could be realized by a custom class called NumberArray, which is created with the following statement:
WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
// generated class names
NumberArray, MutableNumberArray)
Once you've created NumberArray, you can use it everywhere in your project. It lacks the syntax of <int>, but you can choose your own naming scheme to label these as classes as templates.
Take a look at:
https://github.com/tomersh/Objective-C-Generics
It appears to be a sort of poor-man's generics, by repurposing the protocol checking mechanism.
Now dreams come true - there are Generics in Objective-C since today (thanks, WWDC).
It's not a joke - on official page of Swift:
New syntax features let you write more expressive code while improving consistency across the language. The SDKs have employed new Objective-C features such as generics and nullability annotation to make Swift code even cleaner and safer. Here is just a sampling of Swift 2.0 enhancements.
And image that proofs this:
Just want to jump in here. I've written a blog post over here about Generics.
The thing I want to contribute is that Generics can be added to any class, not just the collection classes as Apple indicates.
I've successfully added then to a variety of classes as they work exactly the same as Apple's collections do. ie. compile time checking, code completion, enabling the removal of casts, etc.
Enjoy.
The Collections classes provided by Apple and GNUStep frameworks are semi-generic in that they assume that they are given objects, some that are sortable and some that respond to certain messages. For primitives like floats, ints, etc, all the C arrays structure is intact and can be used, and there are special wrapper objects for them for use in the general collection classes (eg NSNumber).
In addition, a Collection class may be sub-classed (or specifically modified via categories) to accept objects of any type, but you have to write all the type-handling code yourself.
Messages may be sent to any object but should return null if it is inappropriate for the object, or the message should be forwarded to an appropriate object. True type errors should be caught at compile-time, not at run-time. At run-time they should be handled or ignored.
Finally, Objc provides run-time reflection facilities to handle tricky cases and message response, specific type, and services can be checked on an object before it is sent a message or put into an inappropriate collection.
Beware that disparate libraries and frameworks adopt different conventions as to how their objects behave when sent messages they do not have code responses for, so RTFM. Other than toy programs and debugging builds, most programs should not have to crash unless they really screw up and try to write bad data to memory or disk, perform illegal operations (eg divide by zero, but you can catch that too), or access off-limits system resources.
The dynamism and run-time of Objective-C allows for things to fail gracefully and should be built in to your code.
(HINT) if yo are having trouble with genericity in your functions, try some specificity. Write the functions over with specific types and let the runtime select (thats why they are called selectors!) the appropriate member-function at run-time.
Example:
-(id) sort (id) obj; // too generic. catches all.
// better
-(id) sort: (EasilySortableCollection*) esc;
-(id) sort: (HardToSortCollection*) hsc;
...
[Sorter sort: MyEasyColl];
[Sorter sort: MyHardColl];

Typedef NSArray of type in Objective C

It has been a long time since I have worked in Objective C but now I am using it because I need to write something that will remain mostly source compatible for future versions. I want to create an init method that allows me to init my viewController with an array of my custom model object. In Swift I would do it like this:
typealias Stack = [StackBarTabItem]
…
func init(stacks:[Stack])
But how would I typedef an NSArray like that? I am pretty sure I can't do something like typedef NSArray<StackBarTabItem> Stack; so what is the syntax in objective c?
Until iOS 9 and Xcode 7, this isn't officially supported. One way to do this is to subclass NSArray or NSMutableArray and enforce typing in your subclass, but this isn't really recommended. One way to deal with the fact that NSArray can only hold ids is to use respondsToSelector before calling a method on any of the objects in the array.
This solution isn't really a substitute for a good typing system, but it's a common practice to get around this limitation. Thankfully, generic support is getting added soon!
Objective-C is dynamically typed. You simply do not check for it.
Asking the audience on talks and in internet fora, the real danger that code will be shipped with a typing bug is minimal and by far lower than other sources of errors. Simply do not care about this.
Ask yourself: How could that happen without getting a runtime error at the very beginning of your next program run?

Creating a Model Object with an NSDictionary

This is something I have been playing with, and have yet to make my mind up about.
When querying a database, it is extremely common that you will use the data in the response to create custom model objects. Let's use 'Book' as an example.
I have received JSON describing multiple Book objects. I parse the JSON into an NSArray of NSDictionarys. I now have a few options:
Make all properties on Book mutable (I hate this option). You could then have a BookManager class with takes an NSArray of NSDictionarys and maps the data in the dictionary to the correct properties.
Add an initialiser to the Book object which accepts the important details.
Example:
- (instancetype)initWithTitle:(NSString *)title author:(NSString *)author publishDate:(NSDate *)publishDate;
The aforementioned BookManager class could then take the NSDictionarys as before, but create the Book objects with this initialiser. This is nice, because you could then make all of the public facing properties on Book readonly. However, it is very limited, and if (as is often the case) there are a lot of properties on the model, this is not feasible.
Add an initialiser to Book which accepts the NSDictionary representation of itself. I dislike this approach in one way because I feel the responsibility is not the model's to create itself from a dictionary. I prefer the idea of a manager class mapping the dictionary to the model. However, I do like the fact that it means that all properties can be readonly.
There is no doubt in my mind I am missing other options, and if you are aware of them, please point them out. The aim of this question is to finally determine the best way to approach this scenario.
I regularly use an init method with the important arguments, but yes, it becomes very unwieldily when the number of arguments reach double digits and/or several of the arguments can be nil. The longest such method I have seen in the iOS SDK is CLLocation's
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate
altitude:(CLLocationDistance)altitude
horizontalAccuracy:(CLLocationAccuracy)hAccuracy
verticalAccuracy:(CLLocationAccuracy)vAccuracy
course:(CLLocationDirection)course
speed:(CLLocationSpeed)speed
timestamp:(NSDate *)timestamp
Regarding your last option, adding an initWithDictionary: method to Book could be expanded to also include a class-level method for creating instances of Book from an NSDictionary.
+ (instancetype)bookWithDictionary:(NSDictionary *)dictionary
And optionally a convenient way to get a dictionary representation from a Book instance.
- (NSDictionary *)dictionaryRepresentation
If you search the iOS documentation for "withDictionary" and "dictionaryRepresentation" you will see a few places where this is used. In other SDKs, you will sometimes see the method named as someObjectFromDictionary:
I think u can do this using JSONModel
see this
http://code.tutsplus.com/tutorials/getting-started-with-jsonmodel--cms-19840
https://github.com/icanzilb/JSONModel

A NSZombie of an odd type

so i am getting a NSzombie and it says this
-[__NSArrayI _cfTypeID]: message sent to deallocated instance
Any idea what that is? assumably an array although i thought if it were an NS type it would say.
Yes — that'll be some type of array. Rather than being single classes, most of the foundation types are class clusters. So exactly how you initialise the array affects exactly which subclass of NSArray you get back.
The exact behaviour is undocumented and basically guaranteed to change over time but for example if you created an immutable array with less than a certain number of entries then the system might decide to return a single linked array and perform searches as simple linear searches. If you create one above the threshold then it might instead create an array that adds some sort of hierarchical logic for searching (or, more likely, contains the logic to create suitable hierarchical tables if the user starts trying to search the array).
Related lessons to learn:
never try to subclass a foundation class;
don't expect isMemberOfClass: to work properly;
don't even expect isKindOfClass: necessarily to be able to tell immutable from mutable versions of the foundation classes.
Apple needs a way to differentiate these classes and to flag them as private, so you end up with underscores and suffixes. In practice I think __NSArrayI is a vanilla immutable array.
Basically that means your NSArray object is already deallocated.
Something in Foundation.framework tried to access your NSArray's private method _cfTypeID and crashed.
And about question why there's _cfTypeID method in NSArray object. NSArray Core Foundation counterpart of CFArray. Two type's are interchangeable with "toll-free bridge".
So actually apple uses that method for internal uses.
If you want deeper understand of this. You can visit http://code.google.com/p/cocotron/source/browse/Foundation/NSArray/NSArray.m and this is Cocotron's implementation of NSArray. It is not same with the apple's implementation but still implementations are similar.

Resources