Each view class has multiple init methods - ones already included as part of UIView, and then additional ones - and each of them set up the same elements in the same way. I therefore usually have them all running a [self initialSetup]; method, which includes the setting up of all of these elements.
The problem i've ran into is that if a subclass also has an initialSetup method, it would override the superclass initialSetup method, and thus the superclass would have to have the method be public in order to still function. This causes problems with organisation, as the method should never be called other than from init, so has no reason to be public.
You've hit upon a problem that there's no perfect fix for. What you'd ideally have is a method that can't be subclassed in the normal sense, that's accessible only to instances of that exact type of class.
Where this is a risk, the normal practice seems to be to incorporate the class name into the setup method. So instead of initialSetup you'd have something like myViewSubclassInitialSetup.
You can also add something like this at the top of your method:
NSAssert([self isMemberOfClass:[ThisClass class]],
#"IniitalSetup called by sub- or superclass")
Then your debug builds will raise an exception if a subclass or superclass ends up calling your init method. That'll give you a place for a breakpoint and a stacktrace that should allow you to find the problem very quickly.
It won't add any code to your release builds.
Change the name of initialSetup to something like initialSetupClassName - subclasses, even if they accidentally used the same pattern, would not use the same name as they had a different class name.
You can also use an "_" prefix for private methods you would rather not be called, but the subclasser may do that also.
It sounds like you are missing a designated initializer. Designate one initializer as the official one that actually performs the setup, and have all the others just call that with some degree of customization. Usually the designated initializer will be the one with the most detail — for example, if you have init, initWithName:, initWithName:age: and initAsFiveYearOldNamed:, the designated initializer will be initWithName:age: and the other initializers would just call that method with the arguments filled in appropriately.
Unfortunatly Objective C doesn't provide a way to achieve that in a "clean" way. The ideal solution would be a protected method. But that's not possible in Objective C
Apple had this problem, when they were creating the UIGestureRecognizer. There were some methods they really didn't want to get called by somebody, but which had to be overwritten by subclasses. The way they chose to deal with this, was to create a seperate header file (UIGestureRecognizerSubclass.h), that contains a category to the original UIGestureRecognizer with those "protected" methods. The additional header is only to be imported by subclasses (i.e. for subclassing purposes). See UIGestureRecognizer Class Reference for some details.
Of course that doesn't prevent anybody from misusing the additional header file, but at least it clearly states your intention and keeps your code well structured. Also you won't be "bothered" by autocompletion for the additional methods, when just using the class.
Personally I only use an additional header, if it is extremely important that nobody calls it directly. In most cases I think it's ok to use public methods and make a note for what it's inteded. The iOS Framework also has many of these cases. F.e. many methods of UIViewController's viewDidLoad etc.
Related
I've defined two classes in an m file, the first subclassing UIView and the second UIViewController. The UIViewController is instantiated at some point, and the vc is who instantiates my first class.
the first class implements the touchesEnded method, to simulate a button. when the touchesEnded method is fired in the first class, is it possible to easily call a method defined in the 2nd class, without going into delegates and such?
I tried playing with selectors with no luck
is it possible to easily call a method defined in the 2nd class
Yes, assuming that you are creating an instance of the second class and calling the method on that instance.
Regardless of whether the two classes are subclasses of the same type, or in the same or different files, you need a reference to an instance of that class to call a method on it, or force it to perform a selector.
The proper OO way to do this is with delegates, but you could theoretically do something like pass a reference to view 2 into view 1 when you create the views. If you create them in IB you could create outlets so they reference each other that way.
In short: Yes, it is possible and easy to do, but I can't give you too much in terms of specific code without a more specific example of your situation
If I am using a custom UITableViewCell I can use the following methods to change the cell's appearance when transitioning state:
- (void)willTransitionToState:(UITableViewCellStateMask)state
- (void)didTransitionToState:(UITableViewCellStateMask)state
Is there a way to achieve this if I'm not using a custom tableview cell?
Edit: Please see Daniel Hanly's comment. Categories may be selectively applied using #import. My apologies to anyone that may have been misled by this answer. Relevant sections will be redacted for future reference.
Okay, attempt number two. As far as I am aware, there is no other documented way to implement the functionality you require without subclassing UITableViewCell. It's worth noting that Apple's docs on UITableViewCell specifically mention that the state transition methods are meant to be implemented by subclasses. That having been said, If you absolutely need to implement them without a subclass, there are a couple of less conventional solutions. Each comes with its own issues, and it may end up being unfeasible for you to implement them, but it's an interesting question nonetheless.
Disclaimer
If you only want a sane and relatively simple explanation, then consider the answer to your question to be "no, there is no way to do what you want." I only present the options below with the assertion that they will work. In no way do I endorse actually using them. Consider this my penance for providing my first answer with such an obvious flaw.
Option One - Categories
It is possible to get the functionality you're looking for by overriding the methods you listed in a custom UITableViewCell category.
The problem is that this approach would be a pretty bad idea 99% of the time. Once you define the category on UITableViewCell, those methods would be defined for all UITableViewCell objects throughout the app. Unless you want the exact same state transition functionality for every single table cell in the app, this approach isn't very helpful.
Option Two - Runtime magic
You can use the low-level Objective-C runtime functions to change the implementation of any method on the fly. Unlike the categories option, this approach is flexible enough to redefine the intended behavior whenever you need to, instead of being a one-shot deal.
For example, if you're trying to manage state transitions from a UITableViewController, you could do this:
CustomTableViewController.m
#import <objc/runtime.h>
- (void) customStateWillChange:(UITableViewCellStateMask)state
{
//custom UITableViewCell code
}
- (void) viewDidAppear:(BOOL)animated
{
//Store the original implementation
Method originalStateWillChangeMethod = class_getInstanceMethod([UITableViewCell class], #selector(willTransitionToState:));
originalStateWillChangeImplementation = method_getImplementation(originalStateWillChangeMethod); //variable declared in header file as type IMP
//Get the new implementation
Method newStateWillChangeMethod = class_getInstanceMethod([self class], #selector(customStateWillChange:));
IMP newStateWillChangeImplementation = method_getImplementation(newStateWillChangeMethod);
//Replace implementation
method_setImplementation(originalStateWillChangeMethod, newStateWillChangeImplementation);
//the rest of your viewDidAppear code
[super viewDidAppear:animated];
}
- (void) viewDidDisappear:(BOOL)animated
{
//restore the original implementation
Method originalStateWillChangeMethod = class_getInstanceMethod([UITableViewCell class], #selector(willTransitionToState:));
method_setImplementation(originalStateWillChangeMethod, originalStateWillChangeImplementation);
//rest of viewDidDisappear code
[super viewDidDisappear:animated];
}
This code may not suit your exact purposes, but I think it provides a useful example.
It's incredibly ugly though because the customStateWillChange: method defined here is only intended to be run as a part of the UITableViewCell class, but in this example it will be compiled as though it were part of the CustomTableController class. Among other annoyances, you would have to eschew the property dot notation, ignore compiler warnings and give up most if not all compile-time checks for that method's body.
Option 3 - Category with runtime magic
Exactly what it sounds like. Define any custom state change methods you like within a category (or several categories) on UITableViewCell. Be sure that each one has a separate name - adding two categories that each have a method of the same name will result in undefined behavior. Also, each one needs to have the same return type and argument types as the method it is intended to replace.
Then the references to [self class] in the above code would be replaced with [UITableViewCell class], and the customStateWillChange: method would be moved to the custom category. While still ugly, you can at least rely on the compiler to interpret the method bodies properly.
Of course, messing with the runtime adds a whole lot of complexity to keep track of. It could work fine, but it's not good design, it would take serious effort to ensure it worked safely and correctly, and it would be likely to bring anguish and despair to anyone maintaining it.
References
The Objective-C Programming Language - Categories and Extensions
Objective-C Runtime Reference
Absolutely. The UITableViewDelegate protocol specifies a number of methods to manage state transitions for the table view's cells. Take a look at the UITableViewDelegate Class Reference, specifically the methods listed under the heading "Editing Table Rows".
Edit
Sorry, you're right. The UITableViewDelegate methods don't respond to direct changes to the cell's properties. I've found a way that does work, but I'm going to put it in a different answer to avoid confusion.
I know that there are functions in the objective-c runtime that allow you to create objects directly, such as class_createInstance. What I would like to know is if anything actually uses these functions other than the root classes' (NSObject) alloc method. I think things like KVC bindings might, but those aren't present on the iPhone OS (to my knowledge, correct me if I'm wrong), so is there anything that would do this?
In case you're wondering/it matters, I'm looking to allocate the size of an instance in a way that circumvents the objc runtime by declaring no ivars on a class, but overriding the +alloc method and calling class_createInstance(self, numberofbytesofmyivars).
Thanks
EDIT
I think I need to be more specific. I am adding classes to the runtime at runtime, and possibly unload and reload an altered version of the same class. I've worked around most of the issues so far, due to things like class_addMethod, but there's no equivalent for ivars after the class has been registered. The two solutions I can think of are having no actual ivars as far as the runtime is concerned, but overriding alloc to make sure I have enough room for them through extraBytes, or alternatively declaring an ivar which is a pointer to all of my actual ivars, which I can then obviously do whatever I want with. I would prefer to use the former strategy but there are a number of things that can go wrong, like if something allocates an instance of my object without going through my overloaded alloc method. Does anyone know of one of these things?
I'm not sure if you're trying to change the behavior of existing classes, which is not safe, or trying to do something for custom classes you own that are direct subclasses of NSObject, which probably is.
Almost all NSStrings you see in practice are instances of a private subclass, and that subclass allocates space for the string inline with the object. Like, instead of containing a pointer to a char*, the character data comes right after the ivars in the object. The extraBytes parameter in NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone) is there for purposes such as this.
So on the one hand, yes, you can pull tricks like that. On the other, you need to know you're doing it with your stuff. If you try to do something like that with the private subclass of NSString (which is private, so you're only going to interact with through runtime introspection), you're probably going to conflict.
There are a few public cocoa classes that also do stuff like this, so you're best off if your classes inherit directly from NSObject. NSLock is one. The layout in memory for a custom subclass of NSLock looks like { isa, <ivars of NSLock> <ivars of subclass of NSLock> <more NSLock stuff, space reserved using the extraBytes parameter> }.
Also, just for the heck of it, note that +alloc calls +allocWithZone:, and +allocWithZone: is the more common override point.
I'm not sure why you'd want to do what you're suggesting--I don't see any reason you couldn't do it, but according to this post, there's usually no reason to use class_createInstance directly (I don't know of anything that uses it specifically). class_createInstance also doesn't take into account memory zones or other possible optimizations used by alloc. If you're just trying to hide your ivars, there are better ways.
EDIT: I think you're looking for the class_addIvar function, which (as the name suggests) dynamically adds an ivar to a class. It only works with the new runtime, so it won't work on the simulator, but it will work on the iPhone.
EDIT 2: Just to be totally clear (in case it wasn't already), you can definitely rely on allocWithZone always being called. Fundamental Cocoa classes, such as NSString and NSArray, override allocWithZone. class_createInstance is almost never used except at the runtime level, so you don't have to worry about any parts of Cocoa using it on your classes. So the answer to the original question is "no" (or more specifically, objects are sometimes created without alloc, but not without allocWithZone, at least as far as I know).
Well there is nothing technically to stop you from overriding alloc. Just create a method in your class called +alloc. I just can't imagine any reason why you would need to.
Sounds like you are trying too hard to manage memory. Let the OS dynamically allocate memory when you create an object. If you are using too much, the OS will send a notification that you are getting close to the limit. At that point you can dealloc stuff you don't need anymore.
If you need so much memory that you have to use tricks, your implementation may need rethinking at the core level instead of trying to fit your square design into the round hole of the iPhone OS.
Just my opinion based on the info you provided.
I have a method where I do some startup animations. The method gets called many times during usage of the app, but on it's first call it needs to do some special things in addition.
Are Singletons the way to go? Maybe there is also an better way, instead of measuring how many times this method was called, and storing that in an ivar.
- (void)someMethod {
static BOOL hasBeenCalledBefore = NO;
if (!hasBeenCalledBefore) {
// perform setup
hasBeenCalledBefore = YES;
}
// do other stuff
}
Extra work may be required if you're using threads, but that's the basic idea.
Why isn't that initialization code in the constructor? Maybe you need to factor that method out into it's own class which uses the constructor to handle the init block you mention.
An amendment to chuck's answer (pretty much correct)
His works and answers your question, but another option you could use (assuming it didn't need access to any of the variables being passed to that method) would be to take the code out of your method and put it in a static initializer. It will only be executed when the class is first loaded and will isolate what is essentially completely different pieces of code.
If you want it called for every new class, use Chuck's answer but with a member variable, or use a class initializer.
ok.. think of objectA.. objectA has optional properties defined in its protocol. the delegate may or may not implement these, but if you do decide to implement them, you must provide a valid return.
now what if i have objectA.delegate set to objectB but object be needs to implement them all at compile time, and at runtime will decide which ones it may or may not support.
The problem in implementing them all as stubs means that the objectA will simply use 'respondsToSelector' to see if the delegate is trying to use optional properties, and if so it will assume they are valid and use them.
Since objectB(delegate) has to decide conditionally which ones to implement, the only solution i can see is to use 'class_addMethod'.
I was hoping there could be a more elegant and possibly more optimal solution, but not sure as what it may be.
Anytime you think you need to use class_addMethod() to implement something, you are almost always doing it wrong.
From what you describe-- which is rather vague-- an easy solution would be to create something that can both act as the delegate and can conditionally forward the methods, as needed.
Now, you say that if the delegate does implement a method it must return a valid value. If that is the case, class_addMethod() isn't really going to help. Most classes that implement delegates will check once if the delegate implements the method and then will assume it does forever unless the delegate changes. You could get into the business of switching delegates often, but down that path lies madness.
A better answer might be to ask a question; what are you trying to do?
Yeah, use NSObject's + (BOOL)resolveInstanceMethod:(SEL)aSEL in combination with class_AddMethod and hook up the implementation you want based on whatever criteria you want. Not sure if this is what you are looking for or not. Hope it helps.