Objective C Class or struct? - objective-c

I have a class Song with properties Title, Key, Artist, etc. There are no methods. I loop through a database of song information and create a Song object for each, populating the properties, and then store the Song objects in an NSArray.
Then I thought, why not just have a struct Song with all those same properties instead of a class Song. Doing so would eliminate the class files, the #import Song line in the using class's .m file, and the need to alloc, init, release.
On the other hand, I'd have to put the struct definition in every class that might need it. (Unless there's some globally accessible location -- is there?) Also, can a struct be stored in an NSArray?

I would do it with a class. A struct cannot be stored in an NSArray (or any of the other container classes for that matter), unless you wrap it in a NSValue, which can be done but is a bit fiddly.
Plus, as you have pointed out, you have to define the struct somewhere. The normal way would be to define it in a header (.h) file just as for a class. So there's no 'gain' from a struct there.

You can't store structs in an NSArray (look at the signatures of its methods — they all take and return objects). But besides that, as I said in the answer to another recent question, putting objects in structs is just about always a bad idea. Objects need to be retained and released and structs don't have code associated with them to ensure that happens at the right times. This makes more sense as a model object.

It could go both ways but why not separate your concerns and keep it in it's own class file? Plus, doing so is more in line with the Single Responsibility Principle of SOLID. Why give your main class file another reason to change? Break it out.

Related

Where and how are an Objective-C class's methods stored?

I know that when an object is instantiated on the heap, at the least enough memory is allocated to hold the object's ivars. My question is about how methods are stored by the compiler. Is there only one instance of method code in memory? Or is the code generated an intrinsic part of the object in memory, stored contiguously with the ivars and executed?
It seems like if the latter were the case, even trivial objects such as NSStrings would require a (relatively) large amount of memory (NSString inherits methods from NSObject, also).
Or is the method stored once in memory and passed a pointer to the object which owns it?
In a "standard" Objective-C runtime, every object contains, before any other instance variables, a pointer to the class it is a member of, as if the base Object class had an instance variable called:
Class isa;
Each object of a given class shares the same isa pointer.
The class contains a number of elements, including a pointer to the parent class, as well as an array of method lists. These methods are the ones implemented on this class specifically.
struct objc_class {
Class super_class;
...
struct objc_method_list **methodLists;
...
};
These method lists each contain an array of methods:
struct objc_method_list {
int method_count;
struct objc_method method_list[];
};
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};
The IMP type here is a function pointer. It points to the (single) location in memory where the implementation of the method is stored, just like any other code.
A note: What I'm describing here is, in effect, the ObjC 1.0 runtime. The current version doesn't store classes and objects quite like this; it does a number of complicated, clever things to make method calls even faster. But what I'm describing still is still the spirit of how it works, if not the exact way it does.
I've also left out a few fields in some of these structures which just confused the situation (e.g, backwards compatibility and/or padding). Read the real headers if you want to see all the gory details.
Methods are stored once in memory. Depending on the platform, they are paged into RAM as needed. If you really want more details, read the Mach-O and runtime guides from Apple. It's not usually something programmers concern themselves with any more unless they're doing something pretty low-level.
Objects don't really "own" methods. I suppose you could think of it as classes owning methods, so if you have 400 NSStrings you still only have one copy of each method in RAM.
When a method gets called, the first parameter is the object pointer, self. That's how a method knows where the data is that it needs to operate on.

Objective-c: Reference to ivar persistent? Good idea?

I have a situation where I'm keeping references to ivars which need to be persistent. In one object, I have an array of pointers to ivars in another object, which are used over the entire lifetime of the program. In other words, I'm not just passing a reference to retrieve a value -- I'm keeping the pointers around.
Is this a valid? Is it possible that the ivars might move? Are there cases where objects instantiated objects are moved around at runtime unbeknownst to the program? Or, do objects stay exactly where they are created. If the later is the case, is there any reason not to use references the way I am?
I'm using ARC.
Note: This probably wasn't a good way to design this to begin with, but... it's all done and working 99%! (except for a nasty crash which reboots the entire phone... )
Objects and their instance variables don't move once created. However, you also need to keep a strong reference to the object that holds the ivar. Otherwise, the object might be deallocated, leaving you with a dangling pointer.
Note that it is generally a very bad idea to have pointers to another object's insntance variables.
While there's no technical problem with accessing the ivars from outside (as rob stated) there's still the architectural design to consider: The approach you've taken breaks encapsulation. Additionally it is very uncommon for Objective-C.
So regarding maintainability of your code I would recommend to refactor the code. In Objective-C there's no friend declaration as in C++, so it's unusual to access ivars from outside the declaring class.
Let's say an object of class A wants to access the ivars of an object of class B persistently (in your example).
What you normally do is create a property (with the strong annotation, like #property (strong) ClassB *myBVar) in class A to reference an object of class B.
If you want to set or read B's properties you use the dot notation or call the getter/setter methods:
myBVar.name = #"Jim";
NSLog(#"Name:%#",myBVar.name);
[myBVar setName:#"Jim"];
NSLog(#"Name:%#",[myBVar name]);
You never call a ivar directly as it's implementation might change.

Why do we have NSNumber and NSTemporaryNumber as two different classes?

I went through the source code of GNUStep's NSNumber's implementation to understand how does factory method implementation works there.
From there What I could understand was we have NSNumber with blank implementation for all initWithXYZ kind of methods. and NSTemporaryNumber was acting like an intermediate class in the hierarchy that implemented all the initWithXYZ methods where it actually created objects of specific types , autoreleased itself and returned the created object.
Also allocWithZone was overridden to avoid creation of NSNumber object and to create object of NSTemporaryNumber if it was so otherwise create objects of specific types.
What I didn't understand was, can't the same things be done by NSNumber itself ?
why give blank implementations at all , create the object of specific type and then autorelease self.
Also if we have implementations of createXYZ methods in NSNumber itself, why have initWithXYZ methods ?
If I have to implement a factory implementation for my project where say I have two mediaItems, video , audio and photo etc.
for which I have separate classes and corresponding enums which I pass to initWithMediaType who will create an object of correct child class, return it and destroy itself.
Have two classes like NSNumber and NSTemporaryNumber: say Media and TemporaryMedia, one with blank implementations other with implementations as mentioned above ?
Should I do something like this ?
Is this the way we have to implement Factory pattern in Objective C ?
My question might seem silly biut I come from a Java, C++ background where things looked different.
The purpose might be the same but I am finding it difficult to understand the way Objective C does it since it does not have abstract classes.
Link to the source:
http://www.koders.com/objectivec/fid46956186C20201706AFE1744AA7AFEEE09D1FE5A.aspx
The point is that NSNumber is a class cluster. The class you actually end up with may be an NSIntNumber, an NSFloatNumber or one of several others. They all respond to the same messages as NSNumber (and, usually in this pattern will be subclasses of it, but that isn't required) so it makes no real difference to the caller.
When you call alloc there's no way to know what sort of object to create, so a neutral type is created and returned instead. It substitutes appropriately upon receiving an appropriate init.
So this pattern is for the implementation of class clusters. You can ignore it if writing a class that provides only instances of itself.

struct or class

I'm developing an iPhone 3.1.3 application.
I have a class, called Object2D, with fields and methods; which represent a shape. Object2D has a field that represents its size, and another field that represent its type.
Now, I need another class called Pattern, which is a set of shapes. In Pattern I'm going to have an array of shapes. From these shapes I only need their size, their type and their location inside pattern, I don't need any method.
I'm wondering it is better to have a new struct that represent a shape instead of using Object2D class.
I ask this because I think on memory performance. I'm not sure, but struct are smaller than classes and maybe it's better to use them instead of classes.
What do you think?
If you're looking at memory use, the difference is truly negligible in almost every situation. Objects are one pointer bigger than an equivalent struct.
The greater difference is in how you can use them. Objects can have methods and hierarchies and interact with the rest of Cocoa. With structs, you need to make custom functions for each struct type or hack together a pseudo-object system. On top of that, arbitrary structs don't interact well with the rest of Cocoa (Touch). For example:
If you store objects in them, you have to be very careful to do your own memory management correctly
You can't store them in NSArrays without creating an object wrapper (since NSArray is an array of objects)
You can't hook structs up in Interface Builder — it doesn't know anything about them
You can't do KVO or KVC with structs
In general, if you're working with a Cocoa-like framework, objects are a good default choice. Use structs once you've decided you really need them.
Objective-C classes are in fact structs, and, provided the instance variables are laid out in the same order as the structure members are, the only difference in size is the 4 or 8 bytes of NSObject's isa variable.
NOTE: the answer above assumes the legacy Objective-C runtime, without the use of #property and #synthesize directives, which could potentially affect their size.

NSCoder - archive a pointer?

I have an objective-c class which contains a pointer to another class. I want to archive an instance of this class via NSCoder:
#interface Barn
{
int m_numHorses;
// Barn does not allocate this instance, it just points to it.
Farmer* m_pFarmer;
}
#end
...
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInt:m_numHorses forKey:#"numHorses"];
[encoder encode?:m_pFarmer forKey:#"pFarmer"];
}
- (void) setPointer:(Farmer*)pFarmer
{
m_pFarmer = pFarmer;
}
How would I archive the m_pFarmer pointer? It doesn't make sense to me as all it is is an address, and I don't see what NSCoder could serialize to disk for you such that it knows how to restore the link later on when you deserialize?
You can't directly encode a pointer, because when you unarchive the objects, the pointer's value is going to be entirely different. I mean, you could store it with
encodeValueOfObjCType:#encode(id) at:&m_pFarmer
but there's no guarantee that the deserialized pointer will point to the deserialized farmer; in fact, it's very very likely that it won't.
If Barn doesn't own the Farmer, then Barn shouldn't recreate it on deserialization; you'll end up with a new Farmer that's not separate from the original one. What you need, then, is a way to find the deserialized instance of the original Farmer, and replace Barn's instance of the Farmer with the other one.
Somebody owns the Farmer, right? So Barn needs to have a findMyFarmer method, which looks through all the FarmerOwners and finds the original instance it should be using. (Maybe by comparing a farmerID ivar on the Farmer?) Once that's done, you can implement -[NSObject awakeAfterUsingCoder:(NSCoder *)] on the Barn to trigger a farmer-replacement routine.
Hope that makes sense. Look at the documentation on Archiving and Serialization, especially the page on Encoding and Decoding Objects to see more about replacing objects on the fly.
Update
NSArchivers and NSKeyedArchivers support the idea of conditional archiving, (via encodeConditionalObject:) in which the object is added to the archive only if some other object in the archive has already added it. The documentation says "Typically, conditional objects are used to encode weak, or non-retained, references to objects.". So if your Farmer is being archived already, then you'd want to add it, but if you're only encoding your Barn without any farmer, then you wouldn't want to.
Definitely check out the documentation referenced above.
You should use encodeObject: and implement NSCoding in your Farmer class as well, so it gets called recursively.
You should first read the docs that BJ points to.
The serialization libraries are smart enough to detect repeats of the same object, and will only encode it one time. So in general, you should be encoding this object if you need it later. The serialization libraries can even handle graph loops. You do not need to worry about these things, and actively should not try to second-guess how other objects are going to handle this object. ObjC does not have the kind of strong object ownership concepts that C++ often requires. Reference counted memory management does not require it, and you shouldn't recreate it.
Speaking of reference counting, do you mean not to retain Farmer in your setter? Is this because Barn is already retaining Farmer? Or is this garbage collected code? Or was this an unintentional under-retain? This could definitely cause a crash when you deserialize this stuff if you've actually under-retained it.
If you literally want to encode the address held by the pointer (because you can guarantee the Farmer object will stay at that location until you decode the archive) then you can encode the pointer as an NSInteger.
i.e. [encoder encodeInteger:(NSInteger)m_pFarmer forKey:#"pFarmer"];
Generally speaking though, this is a bad idea unless you are very careful to hold onto the Farmer object longer than the archive.
I did something similar. In your case, I would generate a UUID (unique identifier) for the Farmer, using
var uniqueIdentifier: String = NSUUID().UUIDString
Then I would save this UUID on the Farmer using NSCoding.
I would also have the Barn point to the Farmer via this UUID (you can have a computed "Farmer" property on the Barn determining a Farmer via the UUID).
And save the UUID of the Barn with NSCoding.
This way, you will never lose the pointer when saving data on disk.
Of course, a better technical solution would be to use CoreData, but that's a different story ...
EDIT : I had a case in which I tried to encode a pointer to another object with NSCoding. My case was a stock alert pointing to a stock (defined as a property of the stock).
It appears that NSCoding created a copy of the original object, "frozen" in its current state. And that copy would live independently from the original object. Stocks have properties, some stored (current share price), other calculated (gain in currency or in %). The stored properties of the stock property of the stock alert were unchanged, while the computed properties (which depended on other factors) evolved.
So the conclusion of my experience would be that it's not a good idea trying to save a pointer to another object with NSCoding.