Use self as parameter in a singleton - objective-c

I have the following method-header in a singleton:
-(int) reconnectToServerForClass:(id)myClass
The parameter myClass is always the self-object of the calling class. Different ViewControllers in my project call this method so my question is this:
Is it possible to access myClass.view or what can I do to achieve that?
my intention is that I want to show a progress-hud for every ViewController that calls the method:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:myClass.view animated:YES];
Isn't there the need for casting myClass before I can access the view-property?

If I understand correctly you can do the following:
-(int) reconnectToServerForClass:(UIViewController *)myClass
{
//myClass.view
}
or inside the method
-(int) reconnectToServerForClass:(id)myClass
{
if([myClass isKindOfClass:[UIViewController class]])
{
UIViewController *viewController = myClass; //edit as suggested by Peter Segerblom
//viewController.view
}
}
Which ever option you choose is that it will depend on how you use it and how you want to manage or limit the callers, in option 1 the parameter must be a sub class of UIViewController and the same for option 2 but the difference is that you can pass in anything but if the type is not UIViewController it will just do nothing.
Disclaimer: This code is not tested and typed from memory, so it might not compile, let me know if you come across issues and I will edit answer.

Yes.
If you want to access it from the singleton class, you make a member or property of which is a pointer to a view, then set that in reconnectToServerForClass: and access it as you would any other member.
If you want to access it from outside, you do the same, and do something like [[MySingleton instance] view]
Be aware that dragons lie ahead of your path, you will need to make sure that views "unset" this property before they are destroyed, otherwise your singleton might try to access a no-longer-existing view instance.

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:]

Unable to access App Delegate property

I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.

Assigning another viewcontroller at initWithNib

I am just curious if lets per say I have a singleton property of webView I am trying to assign at the initialization point of another viewController. The compiler is generating the error indicating "Incompatible pointer types".
I am not sure why it is doing so, as the Super Class of that Class is still UIViewController. Any help here would be really appreciated.
Thanks.
Code below:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nil bundle:nil];
if (self) {
// Work your initialising magic here as you normally would
if ([[CartCheckout sharedInstance] universalVC]) {
//self = [[CartCheckout sharedInstance] universalVC];
NSLog(#"testing");
self = [[CartCheckout sharedInstance] universalVC];
}
NSLog(#"initWithNibName allocated");
}
return self; }
This is happening because the view controller of the actual instance you are trying to do the assignment to is not of the correct type, it doesnt matter that they share a super class.
self in this case is of one and only one type, the type of the view controller subclass.
Also, even if they were the same type, Objective C is not going to let you replace the instance of the class in an init method with anything.
init methods are instance allocation methods, if you return a pointer to something besides the instance you are actually in, you are going to crash immediately following.
So you have two problems, one, you are trying to inject a singleton instance into an init method, and two you are trying to hijack the container of a view.
Neither of these will work. Instantiate the correct type of viewController to go with your view at the time you need it.
What you want instead is this:
MyUniversalViewController *universalVC = [MyUniversalViewController alloc] initWithNibName:#"blabla
Trying to switch types in the constructor is too late.

Test type of NSNotification

I need to check whether an object is an NSNotification. It is not enough to know if it is a subclass, as I want to differentiate between whether it is an NSNotification or a subclass of NSNotification.
So to elaborate I need to differentiate between the following:
An NSConcreteNotification
A Subclass of NSNotification (But not NSConcreteNotification)
The problem is that NSNotifications are actually NSConcreteNotifications and NSConcreteNotification is a private class so I can't use it to test against.
[object isMemberOfClass: [NSNotification class]] // returns NO in both cases
[object isKindOfClass: [NSNotification class]] // returns YES in both cases
There is no reason to subclass NSNotification the way you're describing. First, NSNotification already carries a userInfo dictionary. You can put any data you want in there. You can use category methods to read and write into that dictionary if you like (I do this all the time). For example, a very common thing I want to do is pass along some object, say the RNMessage. So I create a category that looks like this:
#interface NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message;
#end
#interface NSNotification (RNMessage)
- (RNMessage *)message;
#end
static NSString * const RNMessageKey = #"message";
#implementation NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message {
[self postNotificationName:aName object:anObject userInfo:[NSDictionary dictionaryWithObject:message forKey:RNMessageKey];
}
#end
#implementation NSNotification (RNMessage)
- (RNMessage *)message {
return [[self userInfo] objectForKey:RNMessageKey];
}
As #hypercrypt notes, you can also use associated references to attach data to any arbitrary object without creating an ivar, but with NSNotification it's much simpler to use the userInfo dictionary. It's much easier to print notification using NSLog. Easier to serialize them. Easier to copy them. Etc. Associated references are great, but they do add lots of little corner cases that you should avoid if you can get away with it.
That sounds like a really bad idea. When you first receive the notification, you already know what type it is, because it's passed as an explicit argument to a notification callback method. Consider storing the notification as a strongly typed property of another object, or inserting in a dictionary under an appropriate key if you're adding it to a collection, or passing it to other methods that don't preserve the type information to make it easier to identify later.
Creating dependencies on private API (including the names of private classes) will make your code more fragile, and much more likely to break in a future release. Obviously, one of the reasons these classes are private is to make it easier for Apple's engineers to change them as they see fit. For example, the concrete subclasses used by NSArray and NSMutableArray just changed in a recent release of the SDK.
To test id object is an NSNotification use:
[object isMemberOfClass:[NSNotification class]];`
To test if it is a NSConcreteNotifications use
[object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")];
Change the string to the name of a different class as needed...
You can then combine the two check for 'A Subclass of NSNotification (But not NSConcreteNotification'.
Either:
if ([object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{
// It's a NSConcreteNotifications...
}
else if ([object isKindOfClass:[NSNotification class]])
{
// It's an NSNotification (or subclass) but not an NSConcreteNotifications
}
Or
if ([object isKindOfClass:[NSNotification class]] && ![object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{ /* ... */ }
If you want to add properties to NSNotifications you should look into Associative References.
The basic idea is:
static const char objectKey;
- (id)object
{
return objc_getAssociatedObject(self, &objectKey);
}
- (void)setObject:(id)object
{
objc_setAssociatedObject(self, &objectKey, object, OBJC_ASSOCIATION_RETAIN);
}
As others have pointed out, it is a bad idea to rely on the name of a private class. If you are looking for one specific subclass, you could just explicitly check for that class.
[notification isMemberOfClass:[MyNotificationSubclass class]];
You could use multiple statements to check for multiple subclasses, but that would be a little cluttered. This method also requires changes every time you add a new class to look for. It may be better to define a readonly property which indicates whether a notification supports the feature you are looking for, so you aren't relying on the class so much as the ability of the class. You could use a category on NSNotification which simply returns NO for this property, and any subclasses which have the feature would override the method to return YES.
#interface NSNotification (MyFeature)
#property (readonly) BOOL hasMyFeature;
#end
#implementation NSNotification (MyFeature)
- (BOOL)hasMyFeature {
return NO;
}
#end
In the subclasses which support it:
- (BOOL)hasMyFeature {
return YES;
}
- (void)performMyFeature {
...
}
This would also allow you to change whether or not a notification has the feature enabled by changing a flag which is returned for hasMyFeature, and your checking code would simply be:
if(notification.hasMyFeature) [notification performMyFeature];

Cocoa textfield returning null

Sorry if it's a bit long but I want to be as descriptive as possible.
I'm making a test application with several classes and 2 nib files (each class is the owner of one nib). One of the nib files has a textfield, the other has a button. I'm trying to log whatever is inside the text field when the button in the other nib view is pressed, but it returns (null). In one of the classes (ViewClass) I have this:
- (IBAction)startAction:(id)sender {
MyClass *anInstance = [[MyClass alloc] init];
NSString *string= [anInstance name];
NSLog(#"startAction logged: %#", string);
"Name" is a property of MyClass. What I want to do is have "name" set in the init of MyClass, that way, when anInstance is initialized, the MyClass init method does this:
- (id)init {
if ( self = [super init] ) {
[self setName:[nameInput stringValue]];
NSLog(#"init value: %#", name);
}
return self;
"NameInput" is the textfield. I thought this would return whatever was in the textfield, but I get null instead. When I use setName:#"text" it gets passed fine, so something is wrong with the text field.
I previously did this with my own getter, and in that case, it didn't return null when the method was called from it's own class, but if it was initialized and called from the other class, then it returned null, I used this:
- (NSString *)name {
NSLog(#"nameMethod = %#", [nameInput stringValue]);
return [[[nameInput stringValue] retain] autorelease];
This way, I can tell it is all properly set up, but something is happening when I init MyClass from the ViewClass, and try to get "name", that it keeps saying that the text field is null.
Not sure if it helps but the nib with the button belongs to MyView (which is a subclass of NSViewController) and the textfield belongs to MyClass (subclass of NSObject).
Someone suggested the field was not properly linked, but if that was true, it wouldn't have worked when called from it's own class, but it did. Someone else mentioned it might be a problem with the textfield being initialized to nil, so I tried the init thing above. Neither has worked so far.
Thanks for the help.
IBOutlets are not guaranteed to be hooked up until awakeFromNib, which is after your objects' init methods have run. You'll need to do your nib setup in awakeFromNib.
EDIT NOW THAT I'VE GOTTEN HOME: Sorry, I didn't read carefully enough before. What I said above was true, but there's a deeper problem as well. I see now that you also are dealing with two different objects — one in the nib, one created in code. If you have an object in a nib with an outlet hooked up to an interface element, that doesn't make other objects of that class also have an instance variable referring to the element. Two independently created MyClass instances don't share the same instance variables any more than every NSArray in your program holds the same set of items. If you want to use the instance from the nib, you'll need to use that instance.
How you do this is a matter of how you structure your program. There's no simple [self magicallyGetObjectFromNib]. Somehow, one object needs to either find the other (say, by knowing the nib's owner) or be told about the other by an object that knows about them both.
You're in the init routine when you try to extract a value from a field in the same object, and apparently expect it to be initialized. 'Tain't gonna happen.