What is the benefit of using associated objects vs static object defined in the category implementation file? - objective-c

The problem is I don't see the benefit of using associated objects vs static objects defined in the category implementation file with getter/setter methods.
I was thinking about defining the getters and setters in the header file of the category. Like this:
#interface NSObject (test_static)
- (id)getStaticObject;
- (void)setStaticObject:(id)a_static;
#end
and then to declare a static variable in the implementation file and implement getter/setter methods, like this:
static id test;
#implementation NSObject (test_static)
- (id)getStaticObject
{
return test;
}
- (void)setStaticObject:(id)a_static
{
test = a_static;
}
Why I shouldn't use this approach and use associated objects instead ?
Well, I guess I didn't get how properties work and how they've solved the fragile base class problem. Maybe it's related...

There is a huge difference. Associated objects is a way to simulate properties in a category.
Using a single static variable means you have a single, shared value used across all instances.
The choice is which to use depends on your goal. If you want an instance specific result from your two category methods, do not use a static variable - use associated objects. If you want the same object back from the two category methods regardless of the object instance, then use the static variable (and probably change your category methods to class methods instead of instance methods).

Related

Objective-C generics - is there a way to make the generic match the class (in cases of inheritance)

I have ClassA and ClassB, and I'd like to make ClassB inherit from ClassA to streamline my code. The problem is that almost everything is shared except that for ClassA I have a generic array like so:
#property NSMutableArray<ClassADataType*> objectsArray;
whereas for ClassB I have a generic array:
#property NSMutableArray<ClassBDataType*>
Similarly for ClassA I have a property of a single class instance like so:
#property ClassADataType* object
and for ClassB I have a property like so
#property ClassBDataType* object
ClassBDataType inherits from ClassADataType, and similarly I would like to make ClassB inherit from ClassA. Is there a way to do this, basically overriding a class property of the class from which a class inherits?
No, this would violate Liskov Substitution, particularly because these are mutable properties as written. For example:
ClassA *a = [ClassB new]; // Legal and proper because B is an A.
a.object = [ClassADataType new]; // "Legal," but completely broken.
(If they were immutable properties, in principle this kind of feature could exist without creating these problems, but ObjC doesn't provide the feature even in that limited case, and it's hard to prove a property is always readonly in ObjC.)
In most cases the right answer is not to use inheritance. Generally it is better to handle shared code through composition (helper objects) rather than class hierarchies. This is especially true since you say this is "to streamline my code." You should never create inheritance unless you can say "in all ways, a B is a more specific kind of A." It's not good enough to say "they have a lot of code in common."
If you really do need to use inheritance, the common technique is to add another property to B that downcasts.
#property (readonly) ClassBDataType* classBObject;
- (ClassBDataType *) classBObject {
return (ClassBDataType *)self.classAObject;
}
It is very important in this case that these properties be readonly to the outside world, or else you can get the inconsistencies mentioned above.
In addition to the answer by Rob. I have had similar cases and I think there could be a case for inheritance, however if B is not a direct descendant from A I have found the best pattern is an abstract class baseAB that both A and B inherits from and that has all the shared code.
For the typed array property then each subclass can have it's own, but that is probably not so good as then a lot of code cannot move to the base class. Depends on how central this array is to the class.
The option then could be to put a generic array property in the base class with a shared ancestor of the ClassADataType and ClassBDataType, but that may defeat the idea of typing the array.
Or you could skip the generics for the array, make it an iVar of the base class and provide custom typed methods in the subclassess to set and get values and do the type checking.

Why we cannot add iVar in Categories?

I would like to first note that actually we "can"
Just use associated objects:
#implementation UIButton (BGButtonWithImages)
static char UIB_ImageOfButton;
-(void)setImageObject:(Image *)imageObject
{
objc_setAssociatedObject(self, &UIB_ImageOfButton, imageObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(Image*)imageObject
{
return (Image*)objc_getAssociatedObject(self, &UIB_ImageOfButton);
}
#end
But that leads to another issue. Why is it that something that can be done easily with Associated Objects cannot be done regularly? Why does Apple not implement properties in categories like this?
The synthesized property accessors use instance variables. You're not allowed to add instance variables via categories because there would be no guarantee of uniqueness - the same instance variables might be added by another category.
As you notice, the inside-out object pattern lets you define accessors in categories, as long as your key for the associated object table is unique. It's fine to carry on using that. If you want first-party compiler support for generating those accessors you'd have to ask Apple.

"Decorate" several classes with a specific method in Obj-C

I'm not yet that into design patterns so "Sorry!" to bother you with such a question, that might be obvious.
The thing is, I have several classes: Show, Segment, Command. These three classes are totally different, except the one thing: They all have an NSArray called schedules, which contains ScheduleItem classes.
In my workflow I need to check, if the current time matches a scheduleItem to set the Show,Segment or Command active. So, I'd like to have a method on all these three classes called isActive(). Since this method does the same for all current and future classes, I'm looking for a way to implement the isActive method just once, and reuse it in those classes.
Is there a nice way doing this?
To remember, those classes have absolutely nothing in common, except the schedules array. So, I'd like to avoid subclassing. But you can convince me otherwise.
You can create smth like this
#interface ScheduleCollection : NSObject {
NSArray* schedules;
}
#property NSArray* schedules;
/**
Return true if matches.
*/
-(BOOL) match:(ScheduleSclass); //or call it isActive or whatever you like
#end
Then replace schedules array in Show, Segment, Command with ivar of this class. If you need to compare time just get the property and call match:
Show* show = ...;
BOOL m = [show.schedules match: my_time];
There's really no design pattern for this except generic inheritance (shared base class with the method). You can't add it as a category for all three, as they don't share a base class.
If you want to avoid introducing a base class, you can use the fact that type id is a typeless object, and you can invoke any method on it at runtime. Only it will fail if the actual objec doesn't have the method...
On each of the objects, create a method called getSchedule like this:
- (NSArray*) getSchedule {
return schedule;
}
Then just create this method somewhere else
-(BOOL) isActive:(id)anyObjectWithGetScheduleAnyOtherWillFailWithSelectorNotImplemented
{
// You could do an explicit check to determine if the object passed in implements
// getSchedule, but I skipped it here.
NSArray* schedule = [anyObjectWithGetScheduleAnyOtherWillFailWithSelectorNotImplemented getSchedule];
<your implementation here>
}
In my opinion, you would be better off just introducing a shared base class, as it's a lot clearer and won't really take that much more work. But if you have good reasons not to, this will also do the job.

cannot respond to warning in Objective C

I am getting a warning:
RS232Msg cannot respond to
"-initWithRS232MsgRawEncoded"
Code is
-(void)createMessage
{
RS232Msg* pMsg;
//pMsg = new RS232MsgRawEncoded(static_cast<int>nMessageNumber); in cpp
pMsg = [pMsg initWithRS232MsgRawEncoded:(int)nMessageNumber];
}
initWithRS232MsgRawEncoded is a derived class of RS232Msg.
and pMsg is a pointer to RS232Msg. The createMessage is a method that is declared in RS232Msg How to make it to access ?
If you defined initWithRS232MsgRawEncoded in a class derived from RS232Msg you cannot use that selector with RS232Msg*.
If I understand correctly what you are trying to do, you would like to add one more possibility of creating RS232Msg objects by initializing them with raw encoding.
You can do that in different ways. One is creating a sort of "factory" class (it would not be an orthodox factory as per GoF patterns, but that does not matter). This class can have a static function that is exactly your initWithRS232MsgRawEncoded.
Another option you have is define a category for RS232 and then add the initWithRS232MsgRawEncoded into it. Categories are a way to extend classes without the need of subclassing them. This is a skeleton of how you would go about it in your case:
#interface RS232 (MyRS232Extension)
(id)initWithRS232MsgRawEncoded:....;
#end
#implementationRS232 (MyRS232Extension)
....
#end

NSMutableArray with only a particular type of objects

is it possible to specify that a NSMutableArray can only contain a certain type of objects.
For example, if I want to store only this kind of objects :
#interface MyObject : NSObject {
UInt8 value;
}
In order to be able to use the instance variable like this :
- (void)myMethod:(NSMutableArray *)myArray{
for (id myObject in myArray){
[self otherMethod:myObject.value];
}
}
because I'm getting this error :
request for member 'value' in something not a structure or union
Thank you for your help
It sounds like you're coming from a Java/C# type background where limits can be imposed on collections.
Collections in Cocoa don't follow that pattern. There is no way to set a restriction on what type of objects can be inserted (unless you write a wrapper class that enforces this).
Objective-C, by design, follows the "if it walks like a duck and it quacks like a duck, then it most probably is a duck" philosophy. That is to say that rather than checking whether an object is a particular type, you should be checking whether it can do what you want it to do regardless of its type.
You can do this using respondsToSelector:.
Finally, your problem isn't actually related to the fact that the array has no restrictions. Your object doesn't appear to declare the instance variable value as a property, or expose any accessor methods for it.
This is why you're seeing the error when you try myObject.value. That syntax in Objective-C is how you access properties.
The default scope for instance variables in Objective-C is #protected, which means anything outside your class can't access them without going through an accessor method of some kind.
You need to declare and define the methods - (UInt8)value and - (void)setValue:(UInt8)aValue and use them.
Alternatively, you could declare it as a property.
You are getting that error, because for as far as Objective-C is concerned, myObject is of the non-type id, which doesn't support the value property. To make Objective-C aware of the fact it's always dealing with a MyObject in this loop, you'll have to tell it the myObject object is an instance of MyObject.
for (MyObject *myObject in myArray) {
Also, you have to make sure the value ivar is accessible using dot-notation by implementing getter and setter methods for it. You can do this yourself by implementing -value and -setValue:, or you can use #property and #synthesize to let Objective-C do this.
Objective-C doesn't work like that. You need to use [myObject value] (which will work irrespective of the kind of object, as long as it responds to -[value]. If you only want one type of objects in it, insert only that type of objects.
You would have to write a wrapper-class for the NSMutableArray, see for example this question.
Subclass NSMutableArray and override methods that mediate the addition of objects to the array. You would check the object type in these overridden methods, only calling [super addObject:xyz] if the type is accepted.
maybe you can use protocol:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;