Can't get custom #protocol working on iOS - objective-c

Note: the below is using iOS with Automatic Reference Counting (ARC) enabled. I think ARC may have a lot to do with why it isn't working as this is set up as per examples i've found via google.
I am trying to create a protocol to notify a delegate of the filename the user selects from a UITableView.
FileListViewController.h
#protocol FileListDelegate <NSObject>
- (void)didSelectFileName:(NSString *)fileName;
#end
#interface FileListViewController : UITableViewController
{
#private
NSArray *fileList;
id <FileListDelegate> delegate;
}
#property (nonatomic, retain) NSArray *fileList;
#property (nonatomic, assign) id <FileListDelegate> delegate;
#end
FileListViewController.m
#import "FileListViewController.h"
#implementation FileListViewController
#synthesize fileList;
#synthesize delegate;
This gives an error at the
#synthesize delegate;
line which is "FileListViewController.m: error: Automatic Reference Counting Issue: Existing ivar 'delegate' for unsafe_unretained property 'delegate' must be __unsafe_unretained"
If i change FileListViewController.h putting __weak and (weak) then it will run.
#protocol FileListDelegate <NSObject>
- (void)didSelectFileName:(NSString *)fileName;
#end
#interface FileListViewController : UITableViewController
{
#private
NSArray *fileList;
__weak id <FileListDelegate> delegate;
}
#property (nonatomic, retain) NSArray *fileList;
#property (weak) id <FileListDelegate> delegate;
#end
But when I try to set the delegate the app crashes. A view called 'ImportViewController' is creating a view from 'FileListViewController' and setting the delegate to itself (ImportViewController) so I can implement my custom protocol of 'didSelectFileName'. The error I get is;
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ImportViewController setDelegate:]: unrecognized selector sent to instance 0x6c7d430'
The code I am running is;
ImportViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FileListViewController *fileListViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"filelist"];
[fileListViewController setDelegate:self];
[self.navigationController pushViewController:fileListViewController animated:YES];
}
My Questions are:
Why does putting (weak) and __weak in make it work? I don't
understand why this works as I found it googling and there wasn't an
explanation.
Why can't I set my delegate using this
'[fileListViewController setDelegate:self];' ? It seems like the
compiler doesn't know 'delegate' exists.

Under ARC ivars default to strong. So the error
Automatic Reference Counting Issue: Existing ivar 'delegate' for unsafe_unretained property 'delegate' must be __unsafe_unretained"
is telling you that you've declared a property with __unsafe_unretained (assign) ownership, where the underlying ivar has __strong ownership, which is illegal. To avoid the error, you have 3 options:
Omit the ivar. It's not necessary to declare an ivar for a synthesized property. The ivar will be declared implicitly with ownership matching your property.
Define the ivar to match your (assign) property declaration: __unsafe_unretained id <FileListDelegate> delegate;
Define the property to match the ivar's implicit __strong ownership: #property (weak) id <FileListDelegate> delegate;
Personally, I'd omit the ivar declaration so you have the ownership semantics in one place, on the property declaration.

It seems that with :
FileListViewController *fileListViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"filelist"];
you didn't get an FileListViewController object. Look at the message it says :
-[ImportViewController setDelegate:]: unrecognized selector sent to instance 0x6c7d430
and that why your app crashes. Also try to define a retain property, instead of just assign, in case the delegate is deallocated elsewhere, your app won't crash.

I just ran across this same issue, forcing me to finally delve into the ARC documentation.
Also try to define a retain property, instead of just assign, in case the delegate is deallocated elsewhere, your app won't crash.
To maybe clarify the above quote from user756245 's answer, based on my reading I don't think that iOS 5 has changed the best practice that you shouldn't be retaining your delegate as this is a good way to leak. I think the __weak and (weak) tokens are annotations for the compiler for the sake of being able to correctly deal with generating code for the delegate.

Related

#property hides _ivar when setter and getter are custom [duplicate]

This question already has answers here:
Adding a getter makes using an underscore incorrect syntax
(3 answers)
Closed 7 years ago.
#import "AppDelegate.h"
#interface AppDelegate ()
#property (strong, readwrite, nonatomic) NSNumber *currentNumber;
#end
#implementation AppDelegate
- (NSNumber *)currentNumber {
}
- (void)setCurrentNumber:(NSNumber *)currentNumber {
}
Why I can't access _currentNumber in currentNumber?
If I will remove setCurrentNumber then I can access _currentNumber in currentNumber?
The #property does not cause the ivar generation; rather, it's the implicit #synthesize. However, when you implement both the getter and setter, there is nothing to (implicitly) synthesize, so clang doesn't bother. If you add an explicit #synthesize statement, you'll get your ivar.
Like Avi mentioned, there is #synthesize keyword. If you just declare property(without implementing getter and setter for it) the corresponding ivar will be generated. Its name will be [underscore][property_name].For example declaring #property currentNumber; leads to implicit applying `#synthesize currentNumber = _currentNumber;
But if you want to assign ivar with another name to your property you can declare this ivar and synthesize the property with it:
#interface AppDelegate ()
{
NSNumber *_anotherIvar;
}
#property (strong, readwrite, nonatomic) NSNumber *currentNumber;
#end
#implementation AppDelegate
#synthesize currentNumber = _anotherIvar;
#end
#synthesize tells the compiler to create corresponding getter and setter which are backed by ivar on the right side of assignment. And if you provide your own setter - you prevent compiler from creating implicit synthesize(and generating ivar), so you need to provide ivar by yourself somehow(for example use explicit synthesize).
Here is good explanation about #synthesize.

Program receives SIGABRT when setting an ivar

[[self numSidesBox] setName: #"numSidesBox"];
This line of code receives the SIGABRT signal and i don't know why. numSidesBox is an instance of my subclass of UITextField. I have an NSString ivar that uses the
#property (nonatomic, retain) NSString *name;
way of creating setters/getters. I have no idea what's causing this problem.
Why not try:
self.numSidesBox.name=#"numSidesbox"
This assumes you have the following in your numSidesBox header:
#property (nonatomic, retain) NSString*name;
and in your .m:
#synthesize name;
This is just a general idea to get you started and point you in the right direction; you might prefer something other than retain and will also need your numSidesBox object similarly synthesized in the current .h/.m to use dot notation on it.
Additionally, just because numSidesBox has an ivar, does not mean it actually exists in memory. Before you can use it, you have to at some point initialize it with alloc and init or a custom or dedicated initializer.

Overriding properties which conform to protocols

I seem to be getting a new error when using LLVM Compiler 2.0, which I haven't had before.
I have a protocol called DTGridViewDelegate defined as:
#protocol DTGridViewDelegate <UIScrollViewDelegate>
I have a property called delegate on DTGridView (a subclass of UIScrollView, which itself has a delegate property). This is defined as:
#property (nonatomic, assign) IBOutlet id<DTGridViewDelegate> delegate;
Now the message I get is:
DTGridView.h:116:63: error: property type 'id<DTGridViewDelegate>' is incompatible with type 'id<UIScrollViewDelegate>' inherited from 'UIScrollView'
Because I had said that the DTGridViewDelegate conforms to UIScrollViewDelegate, I thought that this would be ok to override this property in this way, and indeed this is the first compiler to suggest there is a problem.
I have fixed the error by declaring the property as such:
#property (nonatomic, assign) IBOutlet id<DTGridViewDelegate, UIScrollViewDelegate> delegate;
I am wondering whether this is a compiler issue?
Your setup looks like the same one used in the case of UITableView inheriting from UIScrollView. The UITableViewDelegate protocol inherits from UIScrollViewDelegate protocol.
I set up the following which compiles fine:
// .h
#protocol ParentClassDelegate
-(NSString *) aDelegateMethod;
#end
#interface ParentClass : NSObject {
id delegate;
}
#property(nonatomic, assign) IBOutlet id <ParentClassDelegate> delegate;
#end
//.m
#implementation ParentClass
#synthesize delegate;
-(id) delegate{
return #"Parent delegate";
}//-------------------------------------(id) delegate------------------------------------
-(void) setDelegate:(id)someObj{
delegate=someObj;
}//-------------------------------------(id) setDelegate------------------------------------
#end
//.h
#protocol ChildClassDelegate <ParentClassDelegate>
-(NSArray *) anotherDelegateMethod;
#end
#interface ChildClass : ParentClass{
}
#property(nonatomic, retain) IBOutlet id <ChildClassDelegate> delegate;
#end
//.m
#implementation ChildClass
//#synthesize delegate;
-(id) delegate{
return #"childDelegate";
}//-------------------------------------(id) delegate------------------------------------
-(void) setDelegate:(id)someObj{
delegate=someObj;
}//-------------------------------------(id) setDelegate------------------------------------
#end
Not sure what is causing your problem. I would note that in the header the UITableViewDelegate protocol looks like:
#protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
... so maybe the compiler likes things more explicit sometimes.
I would suggest a clean and build. That solves a lot of problems.
Since there isn't a formal Objective-C language specification, it's impossible to say whether the compiler is behaving properly. All we can say is that Apple's gcc doesn't seem to have a problem with the above scenario, though it's conceptually unsound as it can break Liskov substitution, since delegate is covariant from UIScrollView to DTGridView (though covariance is just as much a problem). What would happen if you passed a DTGridView to code expecting a UIScrollView, which then proceeded to set delegate to an object that conformed to UIScrollViewDelegate but not DTGridViewDelegate?

Passing argument 4 of 'obj_setProperty' from incompatible pointer type

I am getting the above compiler error in XCode, and I cannot work out what's going on.
#import <UIKit/UIKit.h>
// #import "HeaderPanelViewController.h"
#import "HTTPClientCommunicator.h"
#import "WebSocket.h"
#class HeaderPanelViewController;
#protocol ServerDateTimeUpdating
-(void)serverDateTimeHasBeenUpdatedWithDate:(NSString *) dateString andTime:(NSString *) timeString;
#end
#interface SmartWardPTAppDelegate : NSObject <UIApplicationDelegate, WebSocketDelegate> {
}
#property (nonatomic, retain) id<ServerDateTimeUpdating> *serverDateTimeDelegate;
....
#end
Then in this line
#synthesize serverDateTimeDelegate;
in ApplicationDelegate.m I am getting the error "Passing argument 4 of 'obj_setProperty' from incompatible pointer type". I did a bit of research and found that 'retain' only works on class types, which is fair enough. If I actually remove the 'retain' from the line
#property (nonatomic, retain) id<ServerDateTimeUpdating> *serverDateTimeDelegate;
it does compile without complaints. However, I think, that's the wrong thing to do. Surely my 'id' is a class type and surely it should be retained in the setter. BTW, here is the declaration of my HeaderPanelViewController which implements the aforementioned protocol:
#interface HeaderPanelViewController : UIViewController<ServerDateTimeUpdating> {
}
...
#end
Also, if I actually do remove the retain I get a problem later down the track when I actually call the setter to register my HeaderPanelViewController as the delegate:
// Register this instance as the delegate for ServerDateTimeUpdating
// Retrieve the ApplicationDelegate...
ApplicationDelegate *applicationDelegate = (ApplicationDelegate *) [UIApplication sharedApplication].delegate;
// ...and register this instance
applicationDelegate.serverDateTimeDelegate = self;
The last line causes the XCode error message "Passing argument 1 of 'setServerDateTimeDelegate' from incompatible pointer type".
Your problem is the property declaration:
#property (nonatomic, retain) id<ServerDateTimeUpdating> *serverDateTimeDelegate;
If you command-double click "id", you'll see it defined as:
typedef struct objc_object {
Class isa;
} *id;
In other words, id is already an object reference. Therefore, the * right before serverDateTimeDelegate is unnecessary and wrong. Having it there means a pointer to an object reference, when you really just want an object reference.
Your problem is here:
#property (nonatomic, retain) id<ServerDateTimeUpdating> *serverDateTimeDelegate;
id is already a pointer type, so you declaring serverDateTimeDelegate as a pointer (*) effectively makes the property a pointer to a pointer.
Get rid of the * and everything should work fine.

Why am I getting an error for this?

Why am I getting these errors?
alt text http://img39.imageshack.us/img39/2203/help.tif
It says:
Error: Request for member "jokeTableView" in something not a struction or union
What does that mean? And why is it breaking. I tried reading about initWithStyle but I just could catch up on it
Here is my .h file:
#import <UIKit/UIKit.h>
#interface TableViewController : UITableViewController {
NSMutableArray *jokes;
IBOutlet UITableView *jokeTableView;
}
#property (nonatomic, retain) NSMutableArray *jokes;
#end
Thanks!
Your object (TableViewController) has no property named jokeTableView.
In order to access jokeTableView with the special dot operator, it needs to be a property. Otherwise you have to access it using Key-Value-Coding compliant methods or directly using the -> operator (or just use it as an ivar and no reference to self):
jokeTableView.delegate = self;
or
self->jokeTableView.delegate = self;
or
[self jokeTableView].delegate = self;
or
#property (retain) UITableView *jokeTableView;
// later...
self.jokeTableView.delegate = self;
Also note, however, that you are setting an outlet in the initializer and this won't work. You'll have to set this in the -[TableViewController awakeFromNib] method since self->jokeTableView will be nil when the initializer is actually called (which happens in IB prior to serializing the object into the nib file).
Since you are doing this at init time, the outlets should be NULL, so this initialization shouldn't do anything. This should be done at awakeFromNib time at the earliest.