I am subclassing a pod's class, and in this class there's a private instance variable that I want to expose and use within my class:
#interface MySuperClass () {
UIScrollView *_scrollView;
}
Usually with exposing a private member or method, I would use a category like someone previously mentioned here, but I am having a problem doing it with a private instance variable. I read here that Associative References might work, but I wasn't able to make it work.
Try implementing in child class:
- (UIScrollView *)scrollView {
return [self valueForKey:#"_scrollView"]
}
Unfortunately, in Objective-C there is no way to declare private instance variables.
Whatever you want your subclass to be able to see, you'll have to declare in your .h-file. The Associative References that you were talking about work in that exact same way, but they solve a different problem, namely the one of declaring instance variables in a category.
This is due to the design of the language, and I guess it makes sense in the way that .m files are really implementation files, and no other class should actually care about the implementation of another, even with inheritance relationships like subclassing.
The option for you with the private instance variable of that pod's class would be to either put it in a property or indeed implement a category where you add methods to access it.
Related
I am working with a objective-C framework.
I have a public framework header "MyPublicHeader.h" exposed to the client application. I have a custom class in the project,
//MyCustomClass.h file
#interface MyCustomClass.h
- (NSString *) methodA;
#end
//MyCustomClass.m file
#inplementation
- (NSString *) methodA {
}
#end
If I want the client to instantiate the class I have to make it as public framework header. I want to hide the interface as a curiosity, is there any way to do it???
First know that nothing can be truely hidden in Objective-C due to the nature of dynamic dispatch and the features in the runtime which allow discovery of methods etc.
That said there are a number of ways to do this, a couple:
Use a subclass. Declare a superclass and publish its interface as part of your framework. Make your class a subclass of this and publish its interface only within the framework. You define one or more init methods in the superclass which return and instance of the subclass, and if you want to expose any further API define it in the superclass with dummy (or faulting) implementations and less the subclass override etc. This approach is similar to the model used for classes like NSString.
A .h file is just text and you can exploit this: make two .h files, say MyCustomClass.h and InternalMyCustomClass.h. In the first just declare the interface with no members, or the API you wish to make public, and publish that to users of the framework. In the second declare the real interface used within the framework. You must make sure to keep all three of the files (2 .h, .m) in sync. This approach would be call little naughty by some, "here be dragons" by others, or "needs must" by yet others...
You might also like to look into "class extensions" which are related to categories.
Hope that satiates your curiosity a little, and keep up with the curiosity its good (except for cats)!
You could create an empty wrapper class which only holds a reference to your MyCustomClass object.
When they create this object you secretly instantiate an object of your MyCustomClass inside and extract it when they pass you an object of the wrapper class.
Not sure if this is exactly what you want to achieve, but could be a workaround.
// apply borders to the buttons
-(void)viewDidLoad
{
[super viewDidLoad];
[self.hostGameButton MH_applySnapStyle];
[self.joinGameButton MH_applySnapStyle];
[self.singlePlayerGameButton MH_applySnapStyle];
}
I am confused about this piece of code. If self is just referring to the instance of that object and when we use #property it creates an instance called _hostGameButton ... etc; why can't we just use this code instead?
[_hostGameButton MH_applySnapStyle];
You can, it's just not considered good practice or style. It breaks the encapsulation created by using properties, as well as the (usually desirable) KVO notifications the accessors generate. Modern Objective-C practice is to use properties for everything and only access the synthesized ivar in the init, dealloc (if necessary), and lazy accessor methods, if necessary.
Instances are the heart of the action in an Objective-C program. Most of the methods you’ll define when creating your own classes will be instance methods; most of the messages you’ll send in your code will call instance methods.
So when you create instance of something(like uibutton,uitext field) as property you can access
it anywhere in your.m file by using self. But if you create instance of it in a class you can access it only in that class but no where outside.
As per Apple Documentation, if you’re accessing an object’s properties from within its own implementation, in this case you should use self.
B is subclass of A. C is a subclass of A. When i create c i do if((self = [NSKeyedUnarchiver unarchiveObjectWithFile:…])) { } inside the .m in C.
So despite i set the the superclass in C.h to be B (which is what i want), when i create the C object from unarchiveObjectWithFile: since this object is subclass of A, i can’t force it to be subclass of B. (Hard to explain sorry).
Is there any workaround ???
In other words: when i unarchiveObjectWithFile:… an object that pertains to a class A, and i have another class B that is subclass of A, can the unarchiveObjectWithFile: be a subclass of B? (wich is allowed since B is subclass of a)
If I understand correctly: No.
If you archive an instance of B then when you unarchive it you get an instance of B, likewise for instances of A and C.
However, in non-technical language, a subclass is everything its superclass is plus some extra bits. You can't[*] take an instance of a superclass, created directly or from unarchiving, and make it into an instance of one of its subclasses - the "extra bits" are not there.
HTH
[*] Before someone comments: Yes, this is not absolutely true in all situations, but you are entering obscure, highly specialised, and dangerous waters. Don't go there.
Addendum
Seems you might be in the category where changing the class of an object is safe: you should be able to change the class of an instance to a subclass provided the subclass adds no instance variables or properties. To do this you can use the Objective-C runtime functions:
id obj = ... some instance of class A
object_setClass(obj, [B class]); // where B is a subclass of A which adds no properties or variables
You can also use the unarchiving classes to set the class.
Do not do this casually. Consider alternatives, but sometimes it is an appropriate solution.
The unarchiver wants to create the same type of class that was perviously archived, and that's generally what you want too.
You could look at using setClass:forClassName: to change the class that will be unarchived, but you stand a good chance of ruining everything sooner or later...
Based on the particle comment:
Another option is to take the superclass instance and 'wrap' it in your subclass instance. So, you can archive and unarchive your subclass instance and it will contain the superclass instance. Any method that your subclass doesn't directly respond to you can forward on to the superclass instance.
In this way you have 2 objects but you aren't tampering with anything or relying on implementation details of classes that you don't own.
I must have misunderstood Categories I made a category on a class to extend it with some methods, and make some methods abstract following the OOP guidelines. But I thought that only when I #import and use the category will those methods in the category be called. Instead I find when I #import and use the base class, that this class will automatically call that method but in the Category on the class, not itself.
What I wanted was if the user tried to use this method in the class without a category a exception would get trowed. And this way I could make different categories on the same class
with slightly different internal behavior.
Am I just misunderstanding Categories ?
Categories add methods to a class without condition or other means of picking and choosing.
Once the methods are added to the class, there is no removal short of mucking with the runtime directly.
Subclasses inherit the additional methods.
It doesn't matter if you #import the method declarations or not.
If you want different versions of a class, declare different subclasses.
So we have a library with header files as below (for instance):
Public
MyCustomClass.h (subclasses MyClass)
Private
MyClass.h
When this is imported into an app however it then complains that it can't find MyClass.h. This is fair enough. Its #imported into MyCustomClass.h and yet its hidden.
So I changed it to a forward class declaration #class MyClass. Now it complains that I can't use a forward declaration for a super class (also makes sense).
How can I get around this then? I need to subclass something, but I only want people using the library to have access to the child class, not the super class.
You're writing a library, and you want clients to instantiate MyCustomClass which inherits from MyClass. But clients should not ever instantiate MyClass.
Approach 1: You don't actually need or want to hide MyClass. You just want to avoid instantiating instances of it.
a) Make its constructor private
b) Create a factory method that creates instances of MyCustomClass.
The factory might be part of MyClass, or it could be elsewhere.
#implemenation MyClass
+ (MyCustomClass*) createWith: (Param*) someData;
Approach 2: consider composition rather than inheritance. Let MyCustomClass own an object of type MyClass, and let it do all the work:
#implementation MyCustomClass
#property (nonatomic,strong) MyClass* myClassInstance;
- (void) doSomething
{
[self.myClassInstance doSomething];
}
Since clients can't subclass MyClass anyway, the inheritance is an implementation detail. Don't inherit when you don't need to.
Approach 3: Are you sure you really want to expose MyCustomClass? Is it really an object, or is it a protocol? Perhaps your library should offer the interface publicly, and both MyClass and MyCustomClass are private implementations.