I have a base class, say BaseClass, which provides a common interface for several subClasses, defined by, for example #interface SubA:BaseClass.
The interface of the base class includes a method, say -(void)method1:arg;
In my view, I want to send message to different subClass instances, depending on circumstances. View has a property something like
#property (strong) BaseClass *currentInstance
When I use, in a controller object,
[self.myView setCurrentInstance:instanceOfSubA]
I get a compiler warning.
Then in the view at some point I have
[self.currentInstance method1:thisArg]
and everything works right; that is:
Checking the class in the view, gives the class as SubA.
And all the overridden functions give the right results.
Can I get rid of the warnings? Or is this a signal that I'll get in trouble somewhere down the line.
I seem to have found the answer in Cocoa With Love
In his description of why not to say "polymorphic" Matt Gallagher gives this example of overriding:
MyBaseClass *newObject = [[[MySubClass alloc] init] autorelease];
[newObject instanceMethodDeclaredInMyBaseClass];
Tried it, and it works. Simple and straight forward, but I wouldn't have thought of it if I hadn't seen this example.
By the way, I've benefited from Cocoa With Love many times.
Related
Is it possible to fields defined only in implementation but not in interface definition ?
#interface MyInterface .... #end --> dict not defined here!!!
#implementation MyInterface
...
NSDictionary *dict;
...
#end
In this case if somewhere I somehow accessed to this class, can I access to the dict or should I create a getter just like in Java ?
Edit after #Rob's answer
Thanks for the answer Rob, I wish I have the implementation of these interface and classes. Instead I am trying to bind two different libraries ( I know it is reallllly bad as architectural point of view but this is where I end up).
Basically, I am in react-native world. And we are using react-native-video as our player layer. But since AVPlayer does not support some subtitle types our head company sent us a library that needs a player instance and a view instance to draw subtitle on the view. I believe they will bind to events of the player and draw sub titles based on player states.
So react-native-video is in this github repo with the interface and implementation.
I find the UIView that includes the properties and casted it to the object itself RTCVideo in this case). But now I am stuck. I can go and change some stuff as per your suggestion in the "Development Pods" to be able to access but this is my last bullet :) I prefer to convince these two libraries in a friendly way :)
Yes, but the above syntax isn't what you want. The modern way to do this is with a class extension.
Your header file is the same:
#interface MyInterface
#end
But in your .m file, you create an extension by appending ():
#interface MyInterface ()
#property (nonatomic, readwrite) NSDictionary *dict;
#end
Now, inside your .m file, you can access self.dict normally, but outside of your .m file it won't appear available.
For full details, see Programming with Objective-C: Class Extensions Extend the Internal Implementation.
The syntax you've written actually creates a static (global) variable called dict that isn't tied to any instance.
It is possible to create raw instance variables using a {...} syntax, either on the extension or on the implementation, but this isn't used that often today, except for managing raw buffers that you don't want accessors for. The syntax is either:
#interface MyInterface () {
NSDictionary *_dict;
}
...
#end
or on the implementation:
#implementation MyInterface {
NSDictionary *_dict;
}
...
#end
But I recommend simple extensions with properties any time you can. And if you are considering creating an accessor for it, you definitely want to use #property and let the system do it for you.
If I understand your edits correctly, you're trying to read the internal ivars of an object that doesn't expose them with an accessor, correct? I believe specifically you want to access _player.
There's several common ways to do that. The key feature you want is Key-Value Coding.
The simplest approach for this problem is -valueForKey:
AVPlayer *player = [view valueForKey:#"player"];
The first thing -valueForKey: looks for is _<key>, and if it's just an object pointer (as in this case), it just returns it.
(This can be broken if a class return false for +accessInstanceVariablesDirectly, but the default is true, and it's unusual to override this.)
Another very common approach is to just declare any methods you know exist as a category. (This won't work for _player, since it's not a method, but in case you need similar things.) Imagine you wanted to call the "private" method -removePlayerTimeObserver. In your .m file, just say you know about it using a category:
#interface RCTVideo (PrivateMethods)
- (void)removePlayerTimeObserver;
#end
And since you know about it, you can call it:
[video removePlayerTimeObserver];
If you're wrong, and that method doesn't really exist, then the program will crash. In Objective-C, almost all rules are advisory. You can break them if you want to. ObjC programmers tend to be big rule-followers because otherwise the program crashes and ObjC has very clear rules that are pretty easy to follow. It's not because the system forces us to.
for example let's say i have a class car with the following .h:
Car.h
#interface Car : Automobile
#property Engine * V6;
-(void)igniteEngine:(int) key;
-(void)StopEngine;
#end
and now I have a category Car(Test) with the .m file
Car(Test).m
#implementation Car(Test){
Oil * testingOil;
}
...
#end
and i want to override a function in the following way
-(void)igniteEngine:(int) key //inside test
{
[self applyTestingOil];
[(reference to original class) igniteEngine:key];
}
how would I go by doing this?
if there is a different way of approaching this problem that would also be helpful
Overriding a method in a category is not an option, as the behavior at runtime will be undefined: you have no way to know which of the base class or category implementation will be executed.
See Avoid Category Method Name Clashes.
If possible, the simplest way to do this is to subclass the base class, and override the method there.
Is subclassing is not an option, you can use method swizzling. This will exchange the method implementation with your own at runtime.
But as Mattt put it: "Swizzling is widely considered a voodoo technique", so I'd avoid it if I could.
If you think swizzling could solve your problem, and you're ready to "take the risk", take a look at JRSwizzle, which will handle most of the boilerplate for you.
Considering your ivar Oil * testingOil;, subclassing is again the only simple option. As rmaddy said in the comments, categories does not allow you to add ivars to the class. You'll need to use Associated Objects for this. And well... it's not pretty.
I have a two different ways of representing data in my app: via UITableView or UIScrollView.
So I have 2 main classes: AppTableView: UITableView and AppScrollView: UIScrollView.
And I want to implement the same additions to both views. So I wrote two classes: SomeAdditionsTableView: UITableView and SomeAdditionsScrollView: UIScrollView. The code of this classes is the same.
Main classes now looks like
AppTableView: SomeAdditionsTableView and AppScrollView: SomeAdditionsScrollView.
How to avoid this code duplicate? Thanks in advance.
Yeah this is a problem with the lack of multiple inheritance in Objective-c. I had the same problem when needing certain methods on a subclass of UIView and UIScrollView separately here: Subclassing UIView vs UIScrollView. There are 3 possible solutions I know of:
If you don't need to store any kind of instance variable, simply declare a category on UIScrollView and make sure to import that category into the two subclasses. This is the easiest solution, but least likely to work since you probably need to store state information if you're subclassing anyway.
Only create a subclass of UITableView and simply don't use it as a UITableView when you don't want a UITableView. You can technically just use a UITableView as a UIScrollView without invoking any of the tableView's methods. Of course, you're going to end up carrying around the 'weight' of a tableView (all of it's instance variables) but there no reason you have to use a UITableView as a UITableView and not just a UIScrollView.
Delegate as much of your code to a separate object to minimize code duplication. In each separate subclass carry an instance variable that is the method delegate and forward method calls to that delegate. Now here's where it gets fun. You can use protocols to declare the delegate methods in your subclass and override a special NSObject method: - (id) forwardingTargetForSelector:(SEL)aSelector to make sure those method calls get sent to the delegate. You use a category on the subclass that conforms to the protocol declared in the delegate class. This will expose all the methods of the delegate class in the subclass without requiring you to actually implement those methods in the subclass. When the runtime can't find the declared method in the subclass, it will call - (id) forwardingTargetForSelector:(SEL)aSelector, which you can use to return your delegate/forwarded class. This will prevent you from needing forward each individual method. Depending on what those method calls do, this may take a little more 'wiring', but it'll save you a lot of code writing in the end. It essentially 'mimics' multiple inheritance in objective-c using protocols. See my question/answer here for more details: https://stackoverflow.com/a/9419587/1147934.
Of the three, the last option tends to work the best for me. It takes a little work to get your head around but will significantly reduce code duplication. I also use it when I want to subclass without subclassing. The biggest requirement, though, is any class that you want to do this with will have to move it's method declarations out of it's interface into a separate protocol. But it's really not a big deal and the benefits of getting 'multiple inheritance like behavior' is great.
Also, there are times you may need the forwarded class to access instance variables in the forwarding class (the subclass). You can achieve this by using a delegate pattern whereby the forwarded class maintains a weak reference to the forwarding class in order to access those instance variables. For example, in your case, if you're trying to delegate methods that operate on a UIScrollView, those methods may need to be able to access that view. If those methods are stuck in a delegate class, they won't have direct access to the view's variables unless you give it to them. As usual with any delegate pattern, be very careful you don't create a retain cycle.
If your additions don't need any state of their own, you can make them a category on UIScrollView. Then, since a UITableView is a type of UIScrollView, you can use the category methods on one of those too.
If they do need to define new variables, then I would make it an independent class and have a MyTableView : UITableView subclass with a SomeAdditions property and, similarly, MyScrollView : UIScrollView.
You can achieve a lot by using protocols and "has_a"-relationships, instead of inheritance's "is_a"-relationships.
One very common pattern is delegate, but protocols are also useful for forwarding methods calls to encapsulated or wrapped objects.
in the following example to classes, that are not related to each other, share a common object, but it is also possible, that objects of the same kind use objects of different classes, that all implement a common protocol, so equal objects could do very different stuff.
#interface ClassA : NSObject
#property (strong) id<BrainProtocol> *brain
#end
##implementation ClassA
#synthezise brain;
-(void)theMethod
{
[brain theMethod];
}
#end
#interface ClassB : NSObject
#property (strong) id<BrainProtocol> *brain
#end
##implementation ClassB
#synthezise brain;
-(void)theMethod
{
[brain theMethod];
}
#end
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
//A object, that implements the BrainProtocol
Brain *brain = [[brain alloc] init];
[a setBrain:brain];
[b setBrain:brain];
I have a problem with creating my own subclass of NSData, which I want to have a custom description method. Even creating a dummy NSData subclass:
#interface MyData : NSData {}
#end
and
#implementation MyData
#end
and using it results in weird bugs (the function that uses it never exits, and control somehow returns to the run loop). I thought that maybe I am responsible for rewriting the designated initializers of NSData (calling the super implementation), but none is mentioned in the doc. So:
what are the designated initializers of NSData?
what is the bare minimum I need to write for a dummy subclass of NSData?
Making an NSData subclass is difficult because (as drewag noted) it is a member of a class cluster. From the Binary Data Programming Guide:
...data objects are not actual instances of the NSData or NSMutableData classes but instead are instances of one of their private subclasses.
When you do [[NSData alloc] initWith...] you don't get back an NSData; you probably get back an NSConcreteData. The extraordinary Cocoa With Love has a discussion and demonstration of subclassing class clusters.
The best (and most idiomatic) option is probably composition: your custom class should simply contain an NSData ivar, and implement a description method that operates on that enclosed object.
While drewag's response is technically correct, this is a dangerous technique to use on Cocoa classes; it will override the description method of every NSData object in the program, whether you create it directly or not.
In the specific case of the description method this may be okay, but for another method more likely to be relied upon by other objects in the framework, it could cause large, hard-to-trace problems. You should only do this if you are sure that there is no other way.
It would be far better to create a category and method with a prefix:
#interface NSData (FX_Description)
- (NSString *)FX_description;
#end
The Apple docs specifically mention this category-override technique and advise against it:
Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
An earlier version of the docs went on to say:
The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes. [Emphasis mine.]
If all you want is to override a single function "description" consider using a "Category" instead:
#interface NSData (MyData)
-(NSString*)description;
#end
#implimentation NSData (MyData)
-(NSString*)description
{
return #"something";
}
#end
Then, you can use this function on any instance of NSData.
It is very difficult to subclass NSData because it is a "Class Cluster." The public API treats it as one class, but in reality it is a collection of hidden subclasses. You can research overriding a class cluster, but it is almost never needed. Another option is to create your "MyData" class with NSData as a member variable instead of using a subclass.
Stuck on what I figure is simple thing here. Basically I need to pass a pointer to an object as an argument into an instance method of another class. Said differently: I have a class that creates "Things" and I have an instance of another class that I want to receive the "Things."
Working with Cocos2D frameworks. The Things are a custom subclass of CCSprite, and the instance that receives them is a CCLayer.
I figure I'm misunderstanding something basic about ivars or maybe properties here. Any pointers in the right direction would be appreciated.
Here's the interface for ThingLayer, which should receive the "thing":
#interface ThingLayer: CCLayer {
CCTextureAtlas *textureAtlas;
ThingLayer *thingLayer;
NSMutableArray *ThingsArray;
}
- moveThingtoLayer:(Thing*)athing;
#end
And here's how I'm trying to message to the instance, from outside the class:
[ThingLayer moveThingtoLayer:thing];
I realize I'm asking the class here, not the instance... which is giving me "may not respond to..." errors. But this isn't working either (asking name of instance)...
[thingLayer moveThingtoLayer:thing];
Any obvious answers?
Looks like you should have
ThingLayer *thingLayer = [[ThingLayer alloc] init];
[thingLayer moveThingtoLayer: thing];
As a side thought, you most likely want to init a new thing in thingLayer so that instance owns the Thing, and release thing after calling moveThingToLayer.