Moving functions to separate classes - objective-c

I have been building an iOS app and one of my ViewControllers had become full of functions like:
CGPoint randomPoint()
{
//Code goes here
}
I now want to move them to class A (or protocol, I am not sure what will work for me), import it in the VC and call the just like before:
p=randomPoint(); //NOT A.randomPoint(), [A randomPoint] or whatever
I tried using the C++ class template, but it has problems with CGPoint, CGRect and so long.
How can I accomplish that?

If you are wondering were to put C functions like the one you are describing, the best practice is to move them into a separate .h file that has a meaningful name. For instance MyGeometry.h
Make sure you give a descriptive name to your function, such as:
static inline CGPoint CGPointMakeRandom() {
// your code
return point;
}

You could make a separate objective c class with class methods.
In the header file declare the method like this (let's say you want to call it
#import <UIKit/UIKit.h>
#interface pointHelper : UIViewController
+(CGPoint) randomPoint;
And then in the .m file
#implementation pointHelper
+(CGPoint) randomPoint{
//// implementation
}
When you want to evoke the method in another file.
#import "pointerHelper.h"
You will then be able to access the method like this...
CGPoint thePoint = [pointHelper randomPoint];
Or if you had an object of the class..
CGPoint thePoint = [[pointHelperObject class] randomPoint];
This is a better way of doing it, since it makes your code much clearer. [pointHelper randomPoint] tells you why you are evoking the method and what it is doing. You are using a class that has utilities for points, and you are using it to grab a random point. You don't need an object to evoke this method, because it is controlled abstractly by the class. Just be careful not to try to access properties of the class within the class method.

Related

How to allocate an NSObject subclass instance FROM an instance of its superclass?

Given a class structure such as...
#interface SuperClassView : NSView #end
#interface SubClassedView : SuperClassView #property int aProp; #end
How can one instantiate a SubClassedView from an instance of a SuperClassView?
Say a method returns an instance of the superclass SuperView....
SuperClassView *superInstance = [ViewFactory makeSuperClassView];
but I want to get an instance of the subclass SubClassedView? It is not possible to simply "cast" it...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
and there is no built-in (or easily-imagined implementation of an) NSObject method like
self = [super initWithInstance:[superInstance copy]];`
Is the only way to either copy the superclass instance's desired properties to the newly instantiated subclass object, like...
SubClassedView *subClsInstance = SubClassedView.new;
for (NSString* someKey in #["frame",#"color",#"someOtherProperty])
[subClsInstance setValue:[superInstance valueForKey:someKey] forKey:someKey];
Or add (swizzle at runtime) the subclass' "additional property methods" (in this case setAProp: and aProp) to the superclass instance (and also cast it upwards)...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
[subClsInstance addSwizzleMethod:#selector(setAProp:) viaSomeMagic:....];
[subClsInstance addSwizzleMethod:#selector(aProp) viaSomeMagic:....];
Hopefully this is an easy runtime trick that I simply don't know... not a sad sign that I am haplessly trying to trick ObjC into multiple-inheritance via some embarrassing anti-pattern. Either way, ideas?
EDIT: Pending #BryanChen posting his comment as an answer this is achieved easily via his suggested runtime function, or as a category on NSObject á la..
#implementation NSObject (SettingClass)
- (void)setClass:(Class)kls { if (kls) object_setClass(self, kls); } #end
What you are trying to do is pretty non-idiomatic... it feels like you are trying to do something like prototype based OOP. A couple of quick points:
Don't do the swizzle. You can't swizzle onto an instance, you swizzle onto the class definition, so if you do that you won't be adding the subclasses methods onto "an" instance of the superclass, you will be adding them onto all instances of the superclass.
Yes, if you want to do this you just need to copy the the properties you want from the super into the new instance of the subclass.
You can have a factory method in the superclass that returns a subclass, and encapsulate all the the copying in there (so, -[SuperClassView makeSubclassView] that returns SubClassedView *. That is actually relatively common, and is how many of the class clusters are implemented (though they return private subclasses that conform to the implementation of the superclass)
object_setClass is not the droid you're looking for.
Yes, it will change the class of the instance. However, it will not change the size of it. So if your SubClassView declares extra properties or instance variables that are not declared on SuperClassView, then your attempts to access them on this frankenstein instance will result in (at best) buffer overflows, (probably) corrupted data, and (at worst) your app crashing.
It sounds like you really just want to use self in your factory method:
+ (instancetype)makeView {
return [[self alloc] init];
}
Then if you call [SuperClassView makeView], you get back an instance of SuperClassView. If you call [SubClassView makeView], you get back an instance of SubClassView.
"But," you say, "how do I customize the properties of the view if it's a SubClassView?"
Just like you would with anything else: you override the method on SubClassView:
#implementation SubClassView
+ (instancetype)makeView {
SubClassView *v = [super makeView];
v.answer = 42;
return v;
}
#end
object_setClass may or may not be the "runtime trick" you are looking for. It does isa swizzle which change the class of an instance at runtime. However it does have many constrains such as that the new class cannot have extra ivars. You can check this question for more details.
I think the better way to do is that instead of making view using [ViewFactory makeSuperClassView], make it [[SuperClassView alloc] initWithSomething]. Then you can do [[SubClassView alloc] initWithSomething]
or if you really want use ViewFactory, then make it [ViewFactory makeViewOfClass:]

How to share code between 2 classes that have the same root class?

I have created a custom class called RCTextField, whose purpose is to create an NSTextField with rounded corners (thus RC...).
This RCTextField inherits from NSTextField and overrides the drawRect: method in order to create its own rectangle with rounded corners.
Now, I want to do the same thing for NSSecureTextField, that is, have a way to make it have rounded corners. I can't just make it inherit RCTextField, because that one doesn't have any secure implementation for text that NSSecureTextField has.
And if I inherit from NSSecureTextField, I'd have to rewrite all the drawRect: implementation again.
An option I thought about would be to have an RCBaseTextField class which contains a static method called drawRect:forNSTextField:withParams:, and call that one in the drawRect: of RCTextField and RCSecureTextField. However, that seems a bit hackish, and it feels like there could be a better OOP way to do this in Objective-C.
So, what would be the best/sanest/software-engineering-"esquest" way to share the draw-rounded-corners code from RCTextField between an RCTextField and an RCSecureTextField?
Unfortunately, you cannot make an Objective-C inherit behavior from more that one other class. I think providing a function (using a Object-Oriented language doesn't prevent from using functions when they are appropriate) that will be invoked by both drawRect: implementation is the simpler way.
Or you may just ignore NSSecureTextField and make RCSecureTextField inherit from RCTextField. Then implement the class method cellClass to return NSSecureTextFieldCell and you should get the same exact features as NSSecureTextField.
#interface RCSecureTextField : RCTextField
#end
#implementation RCSecureTextField
+ (Class)cellClass { return [NSSecureTextFieldCell class]; }
#end
I find that object composition helps with situations like this. Move all your methods that are shared into a separate class and then have both RCTextField and RCSecureTextField insatiate your helper class and call the methods appropriately.
Sounds like a case for dependency injection (note: NSSecureTextField inherits from NSTextField):
#interface RCTextField : NSObject {
NSTextField *textField
}
- (id) initWithTextField:(NSTextField *)_textField;
#end
And then just expose whatever functionality you want.
Make a C function with this prototype:
void drawTextFieldRoundRect(NSTextField* instance, CGRect rect);
declared in a file named (say) TextFieldRounded.h and defined in the corresponding TextFieldRounded.m (contains no classes, just this C function. But make it .m so you can use Objective-C syntax).
Inside this function you perform your custom drawing. You have access to 'self' through the parameter instance.
Next, in RCTextField you implement -drawRect: like this:
#import "TextFieldRounded.h"
- (void) drawRect:(CGRect) rect
{
drawTextFieldRoundRect(self, rect);
}
(and do the same for RCSecureTextField)
Not the most elegant, I know...
Option 2: IF you are using rounded text fields only, you can add a category on NSTextField and NSSecureTextField should inherit the 'rounded' behavior. But if you want both rounded and normal text fields, it can't do.
You probably want to subclass NSTextFieldCell, not the controls. I would subclass NSSecureTextFieldCell and use it in both controls; from the drawing perspective the only difference is the value of echosBullets property.

NSArrayController adding categories

I'm trying to add new categories to the NSArrayController class: it can select the first and the last item. I did so:
#import "NSArrayController+selectEnds.h"
#implementation NSArrayController (selectEnds)
- (void)selectFirst:(id)sender {
if (self.arrangedObjects !=nil){ BOOL ignore = [self setSelectionIndex:0];}
}
- (void)selectLast:(id)sender {
if (self.arrangedObjects !=nil){
NSUInteger lastItem = [self.arrangedObjects count]-1;
BOOL ignore = [self setSelectionIndex:lastItem];}
}
#end
I get no errors, but I would like to put this object in IB, using a blue cube and binding buttons to its "selectFirst" and "selectLast" methods.
But I'm a bit lost: which standard object to start with? A standard ArrayController? And then, which class name to choose to have the new methods listed?
Thanks for your help…
Since you didn't show NSArrayController+selectEnds.h (which is what IB actually looks at), just NSArrayController+selectEnds.m, it's hard to know exactly what you got wrong, but there's two plausible guesses.
First, if you want these new methods to be part of the interface of the class NSArrayController, you have to add them to the interface declaration, not just to the implementation.
Second, if you want Xcode (or IB) to know that these new methods are actions, you have to label them as such: in the interface, instead of marking them plain void methods, mark them IBAction methods. (In the implementation, you can do either; it doesn't matter.)
So, NSArrayController+selectEnds.h should be:
#import <Cocoa/Cocoa.h>
#interface NSArrayController (selectEnds)
- (IBAction)selectFirst:(id)sender;
- (IBAction)selectLast:(id)sender;
#end

ObjC: must I specify the inheritance in the header file?

Common examples for a ObjC object are like this (for the header file):
#interface A: B {
int x;
int y;
}
#end
Is it possible to avoid the inheritance specification (i.e. B here) in the header file?
In my case, the framework A defines that class A and another (sub-)framework B defines the class B (which is a subclass of NSView). A links to B. In my application, I link to A and I don't need to know anything about B except that it is a subclass of NSView. And I want to avoid to link to B. But if B is in the header file, I think I cannot avoid it, that's why I was asking about how to avoid it.
No. You have to specify the superclass for any subclass. May I ask why you would want to do something like this?
Your application will need the code for B, therefore you must either link to B's framework, or compile the B framework into your A framework. Either way, you cannot use an instance of A without the code for B, and you must include B's header in your A header.
no.
you must often work around this with a class cluster, hold a private implementation, or create an object factory. then you can minimize the dependencies across modules.
you'll still ultimately need to link to the sub library at some stage if you intend to use it (e.g. create an instance of).
Update - Demonstrate Private Implementations
Private implementations can be entirely opaque. If you do expose them, here are two ways to implement private implementations which are visible to clients:
via protocol:
// MONDrawProtocol.h
// zero linkage required
// needs to be visible to adopt
// may be forwarded
#protocol MONDrawProtocol
- (void)drawView:(NSView *)view inRect:(NSRect)rect;
#end
// MONView.h
#protocol MONDrawProtocol;
#interface MONView : NSView
{
NSObject<MONDrawProtocol>* drawer;
}
#end
// MONView.m
#include "MONDrawProtocol.h"
#implementation MONView
- (void)drawRect:(NSRect)rect
{
[self.drawer drawView:self inRect:rect];
}
#end
via base:
// MONDrawer.h
// base needs to be visible to subclass and types which use MONDrawer
// may be forwarded
#interface MONDrawer : NSObject
- (void)drawView:(NSView *)view inRect:(NSRect)rect;
#end
// MONView.h
#class MONDrawer;
#interface MONView : NSView
{
MONDrawer * drawer;
}
#end
// MONView.m
#include "MONDrawer.h"
#implementation MONView
- (void)drawRect:(NSRect)rect
{
[self.drawer drawView:self inRect:rect];
}
#end
If you don't specify a superclass in the interface, then your class is a root class. This means it doesn't inherit from any other class, so it is responsible for providing its own implementation of the required methods (most of those defined by the NSObject class and protocol). Since this is not a simple task, it is highly encouraged that you inherit from some other class which provides these methods.
Yes you can, with that you will also lost default implementations of alloc, init, etc. Which makes you write your own alloc, init and other stuffs which was there in NSObject
can't you just include a mock version of the class you're inheriting from A's header itself? Not sure if that will cause problems, but it would allow you to clean up your linking requrirements a bit. B-new could then be a category of B'Original
I have one solution now. Instead of providing the class, I just provide a function like this:
NSView* allocA();
Internally in the framework A, A is a subclass of B.

iPhone SDK: Accessing methods in other classes

In my iPhone application I have multiple class files, I have my main application's class files, then I have my UIView class files. I have a simple -(void) method declared in my UIView class files, how can I access it from my main applications class files?
A bit more detail: In my application a video is played, when this video finishes playing a notification is sent and actions are preformed, which I have already successfully set up, however when the movie finishes I would like a method declared in another class file to be preformed. If the method was declared in the same class file I would simply use this code: [self mySimpleVoidMethod]; But obviously this doesn't work If the method is declared in a different class file. I believe it is possible to access a method declared in a different class file, but I just haven't got a clue about how to do it. Sorry if I'm using completely incorrect terms to name things. But I am relatively new to programming all together.
You've got a couple of options, depending on your setup. Here are a few:
1) Add a reference to the class with the function (the callee) as a property in the caller's class:
Caller.h
#interface Caller : SomeObject {
Callee *myCallee;
...
}
#property(nonatomic, retain) Callee *myCallee;
Caller.m
#synthesize myCallee;
-(void)someAction {
[myCallee doSomething];
}
Something that sets up Caller after initializing both classes:
caller.myCallee = callee;
2) Use another notification event, like it looks like you already know how to do.
3) Use a protocol if you've got a bunch of different classes that Caller might need to call that all support the same method:
DoesSomething.h
#protocol DoesSomething
-(void)doSomething;
#end
Callee.h
#interface Callee : NSObject<DoesSomething> { // NSObject or whatever you're using...
...
}
-(void)doSomething;
Caller.h
#interface Caller : SomeObject {
id<DoesSomething> *myCallee;
...
}
#property(nonatomic, retain) id<DoesSomething> *myCallee;
... Then as per example 1.
4) Use performSelector to send a message to the class.
Caller.h
#interface Caller : NSObject {
SEL action;
id callee;
}
-(void)setupCallbackFor:(id)target action:(SEL)callback;
Caller.m
-(void)setupCallbackFor:(id)target action:(SEL)callback {
callee = target;
action = callback;
}
-(void)someAction {
if([callee respondsToSelector:action]) {
[callee performSelector:action];
}
I'm sure there are other ways, and there are pros and cons to each of these, but something in there should fit your needs and/or give you enough to scan the documentation to fill in any gaps...
I did a blog post a few weeks ago that outlines one way to do this. It is similar to the previous answers, and includes some sample code you can download and look at. It is based on using table view controllers, but you should be able to adapt the ideas to your application without too much difficulty.
Passing values and messages between views on iPhone
You'll need an instance of the other class, accessible from the code that runs when the movie finishes. Often, this is accomplished by storing an instance of the other class as a field in the class, set either via a "setter", or during construction. You could also use key-value observing, watching a key representing the playstate of the movie; an instance of the other class can register to observe the changes to this key.
Specifically for patterns using UIView, your UIViewController for the view will have access to it (through the view method). If your "main application's class files" have a pointer to the controller - which they probably will, setup via Interface Builder - then that's an easy way to get to a UIView instance.