There are LOTS of iphone-related questions of this type. I know; I read them until I wasn't learning anything new, all in an effort to avoid (a) posting and (b) looking mighty green.
#interface CommonCostingClass : NSObject {
}
-(void) theCCC;
#end
That's the whole thing. As minimal as I could make it. I've even resorted to UIView instead of NSObject. Inside CommonCostingClass.m I have
#import "CommonCostingClass.h"
#implementation CommonCostingClass
-(void) theCCC {
// blah-blah
}
Again, that's all of it. Inside myViewController I coded
#import "CommonCostingClass.h"
- (BOOL) textFieldShouldReturn:(UITextField *)textField {
if (textField.tag == 4) {
[(CommonCostingClass *) self.view theCCC]; // <-ka-boom
}
// other stuff
}
The presence / absence of the cast makes no difference.
self.view generates
* -[UIView theCCC]: unrecognized selector sent to instance 0x5d3dd20
2010-07-20 11:30:54.732 Wind17[3233:207]
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[UIView theCCC]: unrecognized selector sent to instance 0x5d3dd20'
self generates the same message, with the substitution of "myViewController" for "UIView."
Clean all targets has no effect.
I know that neither UIView nor myView Controller "see" method "theCCC".
I don't know how to say, "It's there! It's there!"
Thank you for the help. Someday this situation will be funny and not embarrassing.
following the comments, on this line
[(CommonCostingClass *) self.view theCCC];
you are trying to perform -theCCC method on self.view. self.view is a UIView.
If you want your custom class to be a NSObject subclass as you have it now, you need to create and initialize a CommonCostingClass object in your view controller, then call -theCCC on it.
edit for unrecognized selector: Unrecognized selector means you are calling a method on a class that does not implement that method. In your case you are calling theCCC on UIView which does not implement or know of a theCCC method.
Seems like you really want
+(void) theCCC;
(note the "+") which is a class method, then you would just call
[CommonCostingClass theCCC]
There really is no mystery to "unrecognized selector" It means the first thing in the brackets (in your case self.view) does not understand (have the method) theCC. And why would it?
You have declared a type of class, but in order to make use of that class you have to have an instance somewhere. So how did you think an instance of CommonCostingClass was ever created?
At least two problems here
If you are going to use CommonCostingClass as a view, it needs to subclass UIView. In the code you're posting, CommonCostingClass is a subclass of NSObject. That could be a typo or it could indicate a much more fundamental issue with what you're trying to do here.
You need to make sure the that view property of your UIViewController is set to be an instance of CommonCostingClass. The easiest way to do this is using Interface Builder.
Related
I've got a custom class BoardMatchData, containing information about a chess match.
I've also got a custom UIViewController named BoardViewController, which is alloc'ed when a user selects a match from a list.
Here's a snippet from where I create the BoardViewController, set its delegate, and then set the new BoardMatchData:
Games *selectedGame = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if (!self.bvc) {
NSLog(#"Alloc-ing a BVC");
self.bvc = [[BoardViewController alloc] init];
self.bvc.delegate = self;
}
[self.bvc setNewBoardMatchData:[MasterViewController boardMatchDataFromGame:selectedGame]];
When debugging, I can see this method setNewBoardMatchData being called, and it has valid data coming into it. However, later on within the BoardViewController, this boardMatchData always seems to be nil. Here's the setNewBoardMatchData method:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData {
NSLog(#"BMD is being set");
if (self.boardMatchData != newBoardMatchData) {
self.boardMatchData = newBoardMatchData;
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
And within BoardViewController.h, I just have an instance variable:
BoardMatchData *boardMatchData;
A method declaration:
- (void)setNewBoardMatchData:(BoardMatchData *)newBoardMatchData;
And then at the top of BoardMatchData.m, I have:
#interface BoardViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
#property (nonatomic,retain) BoardMatchData *boardMatchData;
- (void)configureView;
#end
#synthesize boardMatchData = _boardMatchData;
...my intent here was to make sure that the setter was only ever being called by itself, and not by some other object.
Is there something wrong with how I'm trying to set self.boardMatchData?
I'm not doing any retains/releases because I'm using ARC.
Edit:
Caleb - I did that #synthesize statement as part of my flailing around trying to find this bug. Originally I had:
#synthesize boardMatchData;
... which I just switched back to. The behaviour is the same; self.boardMatchData always ends up nil, even after I've set it.
I think now I have only one ivar, boardMatchData, and I'm always accessing it through self.boardMatchData.
How do I prevent this from becoming nil?
#synthesize boardMatchData = _boardMatchData;
This says that the ivar the accessors should use is _boardMatchData, but you've also got an ivar named boardMatchData. If you're using the accessors, only _boardMatchData will be set. Since Objective-C automatically clears any ivars when your object is created, theboardMatchData ivar will always be nil.
Your comment (that you posted as an answer which you shouldn't do) suggests that you work on two different instances.
Here are a couple of possible reasons for this:
self.bvc is just assign property instead of retain.
You load one from within a nib and one is constructed in your code
(as shown) - this is probably the most like one. Maybe you just
forgot to wire up the outlet.
Your set self.bvc to nil somewhere so that you keep creating new
instances.
Aha; I found this question with an almost identical problem:
Objective-C – Retained property after being set is nil?
There was no conclusion to that one, but the last suggestion was to "try logging the address of self in -viewDidLoad and -viewWillAppear and -queueFinished. Something like NSLog(#"self is %p", self); and making sure they are the same."
I did that myself and now see that in initWithNibName/configureView/setNewBoardMatchData, I'm seeing one pointer for self, and then when viewDidLoad runs, I'm getting a different one!
I'm not sure how or why yet, but this clearly appears to be the problem (a new instance of my class is being instantiated AFTER I've set boardMatchData).
Edit:
While the above led me to the path of finding this bug (I was getting multiple versions of my BoardViewController), it's not the complete answer. I wanted to add here should anyone find this and be in the same position.
I was actually using storyboarding in Xcode4, and the Apple provided master-detail template with Core Data.
When I was instantiating my detail controller (BoardViewController), I was doing an alloc/init. I should have simply been referencing [segue destinationViewController], as the segue already instantiated a version for me.
I did not provide enough context to actually get to the root
I am experimenting in adding functionality to my UIViews (configuring CALayers according to state) by setting up a NSProxy subclass to stand in for any UIView I choose. Here's what I've tried:
In my NSProxy subclass, I have the following code:
#pragma mark Initialization / Dealloc
- (id)initWithView:(UIView *)view
{
delegate = view;
[delegate retain];
return self;
}
- (void)dealloc
{
[delegate release];
[super dealloc];
}
#pragma mark Proxy Methods
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:delegate];
[anInvocation invoke];
return;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [delegate methodSignatureForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL rv = NO;
if ([delegate respondsToSelector:aSelector]) { rv = YES; }
return rv;
}
And, using my NSProxy subclass this way:
UILabel *label = [[HFMultiStateProxy alloc] initWithView:[[[UILabel alloc] initWithFrame:cellFrame] autorelease]];
label.text = text;
label.font = font;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[self addSubview:label];
Seems to work until I hit the addSubview: line.
Turning message tracing on ( instrumentObjcMessageSends(YES); ) shows the forwarding for each of the previous messages working until deep inside of the addSubview:, where this series of method calls show up in the log (the first message shown here was invoked via the proxy):
- UILabel UIView _makeSubtreePerformSelector:withObject:
- UILabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- NSMethodSignature NSMethodSignature methodReturnType
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
+ UILabel NSObject resolveInstanceMethod:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject class
- UILabel NSObject doesNotRecognizeSelector:
And I get the following error:
2011-02-20 16:38:52.048 FlashClass_dbg[22035:207] -[UILabel superlayer]: unrecognized selector sent to instance 0x757d470
if I do not use an NSProxy subclass and instead use a UILabel subclass (HFMultiStateLabel), it works fine. Here is the message trace that occurs once addSubview: is called (HFNoteNameControl is the superview of the label):
- HFNoteNameControl UIView addSubview:
- HFNoteNameControl UIView _addSubview:positioned:relativeTo:
- HFMultiStateLabel UIView superview
- HFMultiStateLabel UIView window
- HFNoteNameControl NSObject isKindOfClass:
- HFNoteNameControl NSObject class
- HFNoteNameControl UIView window
- UIWindow NSObject isKindOfClass:
- UIWindow NSObject class
- HFNoteNameControl UIView _shouldTryPromoteDescendantToFirstResponder
- HFMultiStateLabel UIView _isAncestorOfFirstResponder
- HFMultiStateLabel UIView _willMoveToWindow:withAncestorView:
- HFMultiStateLabel UIView _willMoveToWindow:
- HFMultiStateLabel UIView willMoveToWindow:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- HFMultiStateLabel UIView willMoveToSuperview:
- HFMultiStateLabel UIView _unsubscribeToScrollNotificationsIfNecessary:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- CALayer CALayer superlayer
I can verify that each of the methods up until -superlayer are called successfully when using NSProxy. For some reason, with the NSProxy, superlayer on UILabel is being called instead of CALayer. Perhaps somewhere something gets confused and UILabel is inserted into the sublayers instead of its CALayer?
Does the UIKit do some sort of optimizations that bypass the normal mechanism that NSProxy hooks into?
PS I have only tried this in the Simulator, not the device. Would that behavior be any different?
I was trying to solve the same issue - use NSProxy with UIView (in my case UITableViewCell) when I encountered this problem. I logged all calls to the console:
...
App[2857:c07] MyHeaderCell: --- method signature for: _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- method signature for: _makeSubtreePerformSelector:withObject:
App[2857:c07] MyHeaderCell: --- _makeSubtreePerformSelector:withObject:
App[2857:c07] +[MyHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] CRASH: +[SMSHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] Stack Trace:...
It crashes on the unrecognized selector exception.
Normally, the object is asked the - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel method first and when that is returned, it invokes the - (void) forwardInvocation:(NSInvocation *)invocation in the proxy. This way we can redirect the messages. If there is not NSMethodSignature returned, the doesNotRecognizeSelector: method is called on the object. So we get even unrecognized selector calls.
This works for instance methods, but this crash is caused by a class method, which we have no power over - the object itself is not called (the class is). I wanted to force the runtime to call my proxy class even for class methods by overriding the getter of my NSProxy subclass
- (Class) class
{
return _myRealClass;
}
Which did not work. So NSProxy is not enough to do this. Right now I'm trying to use the NSObject instead of NSProxy to achieve all the desired behavior and since NSObject has the + (BOOL)resolveClassMethod:(SEL)sel method which might be useful. I will edit this post once I found out if NSObject is better suited for this.
//Edit
It seems that the problem is that with NSProxy, superlayer is being called on UIView instead of CALayer.
So it really seems like a UIKit shortcut problem - they're not sending a regular message call (speed optimization I would guess).
Anyways, this I am searching for a way to get around this now.
I gave up trying. I've come to the conclusion that NSProxy is such an underused object that it's potential for uses beyond Apple examples has not been fully explored nor debugged. In short, I believe that NSProxy is not ready to be used as a generic way to extend an object's functionality without subclassing or adding a category.
In the old days, I would have used a poseAsClass call to implement my desired functionality.
My solution ended up something like this:
I added a category to UIView that added additional properties. These property implementations forwarded their set & get messages to a "addOn" property of the UIView that I also put into the category. The default value of this "addOn" property in the UIView's category implementation is, of course, nil. (I could have implemented a static hash table to enable associating an AddOn instance for any UIView, but it struck me as a risky ploy to manage with the retain counts properly.)
The "AddOn" class had extra code in it to directly manipulate the UIView, and it added extra drawing code in it.
For each type of UIView that I wanted to add this added functionality, I had to subclass it with code that:
a) Created an instance method and corresponding property code for the "AddOn" class
b) Subclassed any functions I covered to give the "AddOn" code a chance to add its functionality.
Each of these subclasses has essentially the same code in it to forward the desired functionality to the AddOn instance.
SO, I ended up minimizing code duplication as much as I could, but each of the UIView's descendant subclasses that enable use of the the "AddOn" functionality ends up duplicating code.
It appears that I could have further minimized code duplication by using class method manipulation functions, but that learning curve and further obfuscation of the code deterred me from following that path.
I have never tried using NSProxy with views, but I have done something similar by using a custom view class to display another view. Maybe the system requires an actual view and not a proxy object. There are two ways you could use a "proxy" view:
Make the proxied view a subview of the proxy view. The proxy would take the frame, autoresizing mask, etc. from the proxied view, then add the proxied view as its subview and set its frame to be the proxy view's bounds, and its autoresizing mask so that it always fills the proxy view. When the proxied view is removed, any settings are copied back into it from the proxy view. Any properties not copied into the proxy are passed to the proxied view using forwarding.
The proxy view passes almost every message to the proxied view. The proxy view does not override the lock/unlockFocus, display, etc. methods. It overrides drawRect: to call drawRect: on the proxied view.
After trying the same thing, and searched for the error (which got me here), I tried to circumvent the problems... It wasn't pretty.
Identifying the root problem was easy. Somewhere in the framework, Apple is using direct pointer access to the variables in UIView subclasses. If you check the headers, the variables are declared with #package access identifier.
What I basically tried was:
Create a proxy class at runtime with ivars copied from the UIView class definition, and then set the values of these pointers to the objects in the UIView. Couldn't get far there.
Declare just the CALayer * in the proxy subclass, and only copy that pointer from the protected UIView instance. Worked, but I think it was buggy? It didn't work with auto layout at all, though, so I decided to move away from that solution.
The code I tried can be found in the RTLSegmentedControl repo under the proxy-pattern branch
I also wrote a blog post about the details.
I have an NSOperation that wraps some web service functionality. The NSOperation has a delegate that is to be messaged when the operation is over.
As the NSOperation lives on a different thread I have to make the call like this:
[delegate performSelectorOnMainThread:#selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];
It works just fine, but it gives me a warning:
warning:
'-performSelectorOnMainThread:withObject:waitUntilDone:'
not found in protocol(s)
I completely agree with the compiler on this one, it sees a delegate, it checks the protocol, it finds no declaration of a performSelector method.
My question is: can I remove the warning by making this call in a different manner?
My two guesses are that I could (1) write a method called
- (void) callDelegateMethodOnMainThred {
[delegate getDealersIDSuccess:result]
}
and call that through performSelectorOnMainThread, but I find that solution to be cumbersome and an extra, hard to read, step on top of the delegation.
The second solution could be to cast the delegate to the type of my parent object inside the selector, but that is just plain crazy and goes against the delegate encapsulation pattern.
I would really appreciate a third solution from someone with a better understanding of the language:)
Thank you in advance.
EDIT: Added delegate declaration:
id <ISDealersIDDelegate> delegate;
I declare my delegate as id. The delegate it self extends UIViewController.
I could see that declaring it NSObject would work.
performSelectorOnMainThread:withObject:waitUntilDone: method is declared in NSObject class. If your delegate object inherits from NSObject you can declare it as
NSObject<MyDelegateProtocol> *delegate;
So compiler will know that delegate responds to NSObject's methods and won't issue a warning.
It might be even a better solution not call performSelectorOnMainThread: on a delegate or other protocol implementation.
Make it the responsibility of the delegate/receiver to determine if it needs to do things on the main thread.
[delegate performSelector:#selector(delegateAction:)
withObject:actionData];
Delegate implementation
- (void)delegateAction:(NSData*)actionData
{
[self performSelectorOnMainThread:#selector(updateUIForAction:)
withObject:actionData
waitUntilDone:NO];
}
- (void)updateUIForAction:(NSData*)actionData
{
// Update your UI objects here
}
It might look like more code, but the responsibility is in the right place now
Actually on iOS 4 I prefer using NSNotifications and Observers (with Blocks) to perform updates on the mainthread.
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block
I wrote down my design here.
1) Declare your delegate's protocol to extend the NSObject protocol in the .h file
#protocol YourDelegateProtocol <NSObject>
2) Cast to NSObject in your call
[(NSObject*)delegate performSelectorOnMainThread:#selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];
I definitely recommend number (1).
In my view controller (viewDidLoad) I am populating an array defined in the view controllers header file with settings data (from an archive). I am using data from the array to write labels on buttons in the view, no problem so far.
I would like to pass one element of the array through an IBAction to add to the new view controller. I keep getting an error from within the IBAction
'NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x5d230f0').
Here is the relevant part of my IBAction code:
-(IBAction)pushedTimer:(id)sender {
if (!timerViewController) {
timerViewController = [[TimerViewController alloc] init];
}
[timerViewController setPreset:[[settingsArray objectAtIndex:0] settingLength]];
[[self navigationController] pushViewController:timerViewController animated:YES];
}
I was thinking that since the array is accessible in other methods, it should also work with the IBAction? The error leads me to believe that the IBAction can not determine that the settingsArray is really an array?
Thanks again.
I am not sure but here is what I think for this common problem:
Your array is garbaged already. What this means is that your array is dealloc somewhere in your code (by your over call of release or autorelease) then the string is allocated into the same memory area of the array. So that when the system try to call the object in that memory area, it is calling a NSString object not a NSArray object anymore.
You should double check all the release and autorelease of your array
Do instances in settingArray implement settingLength?
The question was itself a answer. I meant instances should implement settingLength.
How are you defining the settingArray? Are you creating a #property and #synthesizing it? If you're not, then you can't access that in allocated instances of that object, so your array will be nil. Also make sure to check what #alones said!
-[NSCFString objectAtIndex:]: unrecognized selector sent sounds like settingsArray is a NSString, not a NSArray.
I'm very new to cocoa touch and am stuck on a problem that doesn't seem to have a google solution. I have added a view to my application, and this view has a button on it. When the button is pressed I want the callback to perform an operation.
My problem is that the callback isn't being called. I created the view in Interface Builder, connected the touch-up-inside connection to my Owner class (in this case a viewController class), and selected the appropriate callback.
The error I get is as follows:
2009-10-13 17:13:51.708 MyApp[7467:20b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSCFSet contactsButtonPressed:]: unrecognized selector sent to instance 0x4c27330'
As I understand it this suggests that the connection between contactsButtonPressed and MyViewController is wrong. I'm not sure where the NSCFSet object comes from.
Does anyone have any idea?
Thanks!
The error I get is as follows:
2009-10-13 17:13:51.708 MyApp[7467:20b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSCFSet contactsButtonPressed:]: unrecognized selector sent to instance 0x4c27330'
You're probably under-retaining whatever controller object of yours is supposed to receive that action message. Add an NSLog call in the controller's dealloc method; you'll probably find that it gets deallocked before you expect it to.
The question to ask then is what should own that controller. Then, make sure that all of the owners are retaining it.
If you're holding the controller in a property, make sure that you actually use that property. A common mistake is to write myController = theController, which bypasses the property and assigns directly to the instance variable, instead of self.myController = theController (property access syntax, implicit accessor message) or [self setMyController:theController] (explicit message syntax).
Also, if you've implemented your own accessors for the property (especially setMyController:), make sure your setter releases the old object and retains the new one. Of course, this is assuming you have a reason to implement your own accessor; normally, you should simply #synthesize the property and let the compiler write the accessor for you.
somewhere in your code you have a line that looks like this:
[button addTarget:self action:#selector(contactsButtonPressed:)];
If you have a line that looks like this, you will also need to have a method with this signature:
- (void)contactsButtonPressed:(UIButton *)sender {
...
}
If you look at the error, it seems that you are sending the message to an NSCFSet object instead of the controller. I would check that you are setting delegate to self or the controller.
I'm new to cocoa too,
Maybe you forgot to add the sender parameter to your contactsButtonPressed.
Is your view's class named NSCFSet?
Try pasting some code.