I need to pass self into a method, how do I do it ?
I don't know what type of object self is ?
I've tried :(id)dg
When you are inside an #implementation block for a class Foo, self is Foo*. This means that you can type the method parameter as Foo* or id (= any object, no type checking done):
#class Foo, SomeCollaborator;
#interface SomeCollaborator
- (void) doSomethingWithMe: (Foo*) myself;
- (void) doSomethingWithMe2: (id) myself;
#end
#implementation Foo
- (void) someFooMethod {
[someCollaborator doSomethingWithMe:self];
}
#end
That seems right to me. (id) represents all possible objects.
Here's some code that works:
#implementation Inspector
- (void)printClassOf:(id)instance {
NSLog("instance is of class: %#", [instance class]);
}
#end
#implementation SomeClass
- (void)someMethod {
Inspector *myInstance = [[[Inspector alloc] init] autorelease];
[myInstance printClassOf:self];
}
#end
What is the signature of the method (in other words, how is the method defined in the interface)?
Or do you mean, you want to define a method in class B to allow an instance of class A to call that method and pass in itself as one of the parameters? If so, :(id)sender is often used as a generic way to do that. For example, in NSWindow,
- (void)makeKeyAndOrderFront:(id)sender;
- (void)orderFront:(id)sender;
- (void)orderBack:(id)sender;
Within the implementation of that method, you can do something like this to help determine what to do:
- (void)makeKeyAndOrderFront:(id)sender {
if ([sender isKindOfClass:[NSWindowController class]]) {
// do something
} else if ([sender isKindOfClass:[MyCoolClass class]]) {
// do something
} else if ([sender respondsToSelector:#selector(whyDidYouOrderMeFront)]) {
// do something
} else if ([sender conformsToProtocol:#protocol(someCoolProtocol)]) {
// do something
} else {
// do something
}
}
Related
I have a view controller which defines a protocol which itself inherits another protocol.
I want any object that implements my protocol to also implement the inherited protocol.
I want to set my class to intercept some of the messages in the inherited protocol in order to configure some things internally but eventually would like to forward all of the messages to the delegate of my class
I could write a lot of boiler plate code to stub all of the protocol and intern call the delegate but I see that it breaks a lot of the time - any time the "super" protocol changes I need to restub this class once again.
I see that this is very predominant in custom UI controls. When reusing existing components - for instance tables or collection views you would like your data source to respond to all of the common protocols but some instances you need to configure the view according to the index or save a particular state.
I've tried using forwardingTargetForSelector in order to forward the messages I do not respond to , but it isn't always forwarding...
Here is a contrived code example:
Class A: (the top most protocol)
#
protocol classAProtocol <NSObject>
-(void)method1;
-(void)method2;
-(void)method3;
#end
My Class
#protocol MyClassProtocol <classAProtocol>
-(void)method4;
#end
#interface MyClass
#property (nonatomic,weak> id <MyClassProtocol> delegate;
#end
#interface MyClass (privateInterface)
#property (nonatomic,strong) ClassA *classAObject;
#end
#implementation MyClass
-(init)
{
self = [super init];
if (self)
{
_classAObject = [[ClassA alloc] init];
_classAObject.delegate = self; // want to answer some of the delegate methods but not all
}
}
-(void)method1
{
// do some internal configuration
// call my delegate with
[self.delegate method1];
}
-(id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self respondsToSelector:aSelector])
{
return self;
}
if ([self.delegate respondsToSelector:aSelector])
{
return self.delegate;
}
return nil;
}
-(void)setDelegate:(id <MyClassProtocol>)delegate
{
self.delegate = delegate; // will forward some of the messages
}
#end
Returning self from forwardingTargetForSelector: makes no sense because it would never be called if self responded to the selector.
You need to implement these three methods:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self.delegate respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:self.delegate];
else
[super forwardInvocation:anInvocation];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [self.delegate methodSignatureForSelector:selector];
}
return signature;
}
You shouldn't ever return self; from forwardingTargetForSelector:. Your check should mean it never is but if you ever did return self it would cause an infinite loop.
You need to be sure that a super class isn't implementing the method as this will prevent forwardingTargetForSelector: from being called. Check if the method is actually called.
forwardingTargetForSelector: is also only called when a method is called on your controller that it doesn't respond to. In your example you aren't calling [self ...];, you're calling [self.delegate ...]; so forwardingTargetForSelector: will not be called.
I want to use an static function from a protocol in a function:
#implementation IPadPanoramaViewController
- (void)viewDidLoad
{
[self.view addSubview:[PanoramaContent getPanoramaContentByPanoramaItem:[[PanoramaListItem alloc] init]];
[super viewDidLoad];
}
#end
#protocol PanoramaItemProtocol
+ (UIView *) getPanoramaItemBySection;
#end
#implementation PanoramaContent
+ (UIView *) getPanoramaContentByPanoramaItem:(id<PanoramaItemProtocol>) itemKind {
return [itemKind getPanoramaItemBySection]; //here is the problem "unrecognized selector sent to instance"
}
#end
I wish "PanoramaListItem" don't be a NSObject
The problem is that you've defined getPanoramaBySection to be a class method, when you're calling it on an instance. In the protocol declaration, replace the + with a -.
First, static methods can only be sent to a class. If you don't want to create an object, the parameter of getPanoramaContentByPanoramaItem: should be of type Class. You can use something like the following:
+ (UIView *) getPanoramaContentByPanoramaItem:(Class)itemKind {
UIView *v = nil;
if( [itemKind respondsToSelector:#selector(getPanoramaItemBySection)] ) {
v = [itemKind getPanoramaItemBySection];
}
return v;
}
and to send the message:
[self.view addSubview:[PanoramaContent getPanoramaContentByPanoramaItem:[PanoramaListItem class]]];
I'm trying to add a convenience constructor to my custom object.
Similar to [NSArray arrayWithArray:]
I know it involves a class method that returns an auto released object. I've been googling around but all I can seem to find is the definition of a convenience constructor but not how to write one.
Let's say you have the following:
#class PotatoPeeler : NSObject
- (instancetype)initWithWidget: (Widget *)w;
#end
Then to add a factory method, you'd change it to this:
#class PotatoPeeler : NSObject
+ (instancetype)potatoPeelerWithWidget: (Widget *)w;
- (instancetype)initWithWidget: (Widget *)w;
#end
And your implementation would simply be:
+ (instancetype)potatoPeelerWithWidget: (Widget *)w {
return [[[self alloc] initWithWidget: w] autorelease];
}
Edit: replaced id with instancetype. They are functionally identical, but the latter provides better hints to the compiler about the method's return type.
Generally my approach is the following: first I create a normal initializer method (instance method), then I create a class method that calls the normal initializer. It seems to me Apple uses the same approach most of the time. An example:
#implementation SomeObject
#synthesize string = _string; // assuming there's an 'string' property in the header
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
self.string = string;
}
return self;
}
+ (SomeObject *)someObjectWithString:(NSString *)string
{
return [[[SomeObject alloc] initWithString:string] autorelease];
}
- (void)dealloc
{
self.string = nil;
[super dealloc];
}
#end
I am trying to set the value of an NSTextField, but it's not working properly.
I have a button linked to an IBAction, and when I set it using self, it works fine:
#import <Foundation/Foundation.h>
#interface TestMessage : NSObject {
IBOutlet NSTextField *text;
}
- (IBAction) setMessage: (id) controller;
- (void) Message:(NSString *) myMessage;
#end
#import "TestMessage.h"
#implementation TestMessage
- (IBAction) setMessage: (id) controller {
// This works
[self Message:#"Hello"];
// but this doesn't
TestMessage * messageTest= [TestMessage new];
[messageTest Message:#"Hi"];
}
- (void) Message: (NSString *) myMessage {
[text setStringValue: myMessage];
NSLog(#"Message Was Called");
// This returns <NSTextField: 0x1001355b0> when called
// using self, but null when called the other way.
NSLog(#"%#", text);
}
#end
I've searched for a while, but still can't find the answer.
I guess it has something to do with the delegate, but I'm not sure.
Thanks in advance.
Are you sure message is called when you call it from anotherFuntion? If anotherFuntion is a method of another class, calling [self message:] won't work as you expected to...
I know this is an old post, but I have been fiddling with the same issue today.
You have to return string value in textfield:
[textField stringValue];
The code
TestMessage * messageTest = [TestMessage new];
is unusual, specifically new. I'm going to assume that new is just a class method does normal alloc/init equivalent to
TestMessage * messageTest = [[TestMessage alloc] init];
The main problem is that IBOutlet NSTextField *text will be initialized only if the class TestMessage is loaded with a Nib file. It would have to be named as the class of an object in Interface Builder, like so
and you would have to implement initWithCoder and encodeWithCoder something like this in order to extract your field value from the IB encoding:
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.text = [coder decodeObjectForKey:#"text"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:self.text forKey:#"text"];
}
Fundamentally, IBOutlet fields do not get wired up wherever you create an instance of that class. If they did, how would you express that field A should be wired to UI object A and field B should be wired to UI object B? The connection is established only in the context of loading a class from a Nib file.
I'm new to the Objective C business (Java developer most of the time) and am woking on my first killer app now. :-)
At the moment I am somehow confused about the usage of selectors as method arguments. They seem to be a little bit different than delegates in C# for example.
Given the following method signature
-(void)execute:(SEL)callback;
is there a way to enforce the signature for the selector passed to such a method?
The method is expecting a selector of a method with the following signature
-(void)foo:(NSData*)data;
But the SEL (type) is generic, so there is a good chance to pass a wrong selector to the
execute method. OK at least at runtime one would see a funny behavior... but I would like to see a compiler warning/error when this happens.
The quick answer is: no, there is no way to have the compiler enforce the method signature of a method selector that is provided via a SEL argument.
One of the strengths of Objective-C is that it is weakly-typed language, which allows for a lot more dynamic behaviour. Of course, this comes at the cost of compile-time type safety.
In order to do what (I think) you want, the best approach is to use delegates. Cocoa uses delegates to allow another class to implement "callback"-type methods. Here is how it might look:
FooController.h
#protocol FooControllerDelegate
#required:
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#interface FooController : NSObject
{
id <FooControllerDelegate> * delegate;
}
#property (assign) id <FooControllerDelegate> * delegate;
- (void)doStuff;
#end
FooController.m
#interface FooController (delegateCalls)
- (void)handleData:(NSData *)data;
#end
#implementation FooController
#synthesize delegate;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
delegate = nil;
...
return self;
}
- (void)doStuff
{
...
[self handleData:data];
}
- (void)handleData:(NSData *)data
{
if (delegate != nil)
{
[delegate handleData:data forFoo:self];
}
else
{
return;
// or throw an error
// or handle it yourself
}
}
#end
Using the #required keyword in your delegate protocol will prevent you from assigning a delegate to a FooController that does not implement the method exactly as described in the protocol. Attempting to provide a delegate that does not match the #required protocol method will result in a compiler error.
Here is how you would create a delegate class to work with the above code:
#interface MyFooHandler <FooControllerDelegate> : NSObject
{
}
- (void)handleData:(NSData *)data forFoo:(FooController *)foo;
#end
#implementation MyFooHandler
- (void)handleData:(NSData *)data forFoo:(FooController *)foo
{
// do something here
}
#end
And here is how you would use everything:
FooController * foo = [[FooController alloc] init];
MyFooHandler * fooHandler = [[MyFooHandler alloc] init];
...
[foo setDelegate:fooHandler]; // this would cause a compiler error if fooHandler
// did not implement the protocol properly
...
[foo doStuff]; // this will call the delegate method on fooHandler
...
[fooHandler release];
[foo release];
To directly answer your question, no, the SEL type allows any type of selector, not just ones with a specific signature.
You may want to consider passing an object instead of a SEL, and document that the passed object should respond to a particular message. For example:
- (void)execute:(id)object
{
// Do the execute stuff, then...
if ([object respondsToSelector:#selector(notifyOnExecute:)]) {
[object notifyOnExecute:self];
}
// You could handle the "else" case here, if desired
}
If you want to enforce the data handling, use isKindOfClass inside your selector. This works a lot like instanceof which you are familiar with in Java.