Class Extension - unrecognized setter - iphone

The following codes crashed:
#interface AppDelegate (PrivateMethods)
#property (nonatomic, strong) NSString * name;
#end
#implementation AppDelegate
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.name = #"foobar";
...
Error is:
'-[AppDelegate setName:]: unrecognized selector sent to instance 0x6d73df0'
When I change
#interface AppDelegate (PrivateMethods)
to
#interface AppDelegate ()
Then it is okay, what would be the reason?
Update: As answered below, since I must use class extension for this purpose, now this question become: Is the use of class extension to declare private methods acceptable?
e.g.
#interface AppDelegate ()
- (void) start;
#property (nonatomic, strong) NSString * name;
#end

Class extension is basically used to enhance the public property variable. Suppose you have exposed readonly object, or getter method of any variable, then you have make the same object as readwrite in extension.Whereas Category is only used to enhance the method/functionality of class.
check this
#interface MyClass : NSObject
// property here is used as readonly.
#property (retain, readonly) float value;
#end
// Private extension, typically hidden in the main implementation file.
#interface MyClass ()
#property (retain, readwrite) float value;
#end
or
#interface MyClass : NSObject
// here we have exposed getter method of private instance.
- (float) value;
#end
// Private extension, typically hidden in the main implementation file.
#interface MyClass ()
#property (retain, strong) float value;
#end

One is a category, the other is a class extension. If you want to add properties to an existing class, you need to use the latter.
This is the right approach:
#interface AppDelegate ()
#property (nonatomic, strong) NSString * name;
#end

Related

Declare delegate when using ARC

In fastpdfkit has delegate declaration like this
#interface BookmarkViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
//Delegate to get the current page and tell to show a certain page. It can also be used to
// get a list of bookmarks for the current document.
NSObject<BookmarkViewControllerDelegate> *delegate;
}
#property (nonatomic, assign) NSObject<BookmarkViewControllerDelegate> *delegate;
#synthesize delegate;
As i m using ARC so declaration of delegate is like this
#interface BookmarkViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
id __unsafe_unretained <BookmarkViewControllerDelegate> delegate;
}
#property (unsafe_unretained) id <BookmarkViewControllerDelegate> delegate;
#synthesize delegate;
Is it correct cause when i m debugging i m getting
currentPage NSUInteger 0
delegate objc_object * 0x00000000
Heres a pattern to follow. The delegate is properly declared weak (an object but with no transfer of ownership or increase in retain count).
#protocol MyClassDelegate;
#interface MyClass : NSObject
#property(weak, nonatomic) id<MyClassDelegate>delegate;
#end
#protocol MyClassDelegate <NSObject>
- (void)myClass:(MyClass *)myClass didSomethingAtTime:(NSDate *)time;
- (CGSize)sizeofSomethingNeededByMyClass:(MyClass *)myClass;
// and so on
#end

Local declaration of "speed view" hides instance variable

So I have been searching in a few hours for why my iPhone app hates me. This is the error I get:
Warning: local declaration of 'speedView' hides instance variable.
Here is my .m file
#implementation MainViewController
#synthesize speedCount;
#synthesize speedView;
#synthesize popoverController;
- (void)setspeedView:(UILabel *)speedView
{
[speedView setText: [NSString stringWithFormat:#"%d",speedCount]];
speedCount = 0;
speedCount++;
}
.h file
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MainViewController : UIViewController <LoginDelegate,WEPopoverParentView,PopoverControllerDelegate,MainMenuDelegate,MKMapViewDelegate,UIActionSheetDelegate,UIAccelerometerDelegate, CLLocationManagerDelegate>
{
AppDelegate *appDelegate;
IBOutlet MKMapView *userMap;
IBOutlet UILabel *speedView;
CLLocationManager *locationManager;
}
#property (strong, nonatomic) IBOutlet UILabel *speedView;
#property(nonatomic) int speedCount;
I really don't understand why it says that I am hiding the instance variable.
You have a ivar (an instance variable) called speedView.
In your method
- (void)setspeedView:(UILabel *)speedView
speedView is a local variable whose name clashes with the ivar.
If you are using a modern version of the compiler just remove the #synthesize directive.
It will be automatically added by the compiler in this form
#synthesize speedView = _speedView
which will create the ivar _speedView, whose name doesn't clash anymore with the local variable.
Also note that declaring both the instance variable and the property is redundant. The ivar will be automatically created by the (implicit) #synthesize directive.
Here's a "modern" version of your class:
.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MainViewController : UIViewController <LoginDelegate,WEPopoverParentView,PopoverControllerDelegate,MainMenuDelegate,MKMapViewDelegate,UIActionSheetDelegate,UIAccelerometerDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) IBOutlet UILabel *speedView;
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) IBOutlet MKMapView *userMap;
#property (strong, nonatomic) AppDelegate *appDelegate;
#property (nonatomic) int speedCount;
.m
#implementation MainViewController
- (void)setspeedView:(UILabel *)speedView {
[speedView setText:[NSString stringWithFormat:#"%d", self.speedCount]];
self.speedCount = 0;
self.speedCount++;
}
Please note:
properties are nice: use them whenever you can
#synthesize is implicit
the implicit version of #sythesize declares a _ivar for the property ivar
always access variables through the getters/setters, i.e. self.ivar, a part from init methods. If you need to access the var directly use _ivar or self->_ivar
As a final remark, this looks a bit weird
self.speedCount = 0;
self.speedCount++;
and it could be replaced with
self.speedCount = 1;
Are you sure it's what you mean? Also, as noted in the comments by others, you are not using the method parameter speedView. That smells bad and you may want to double check your implementation.

Overridden properties from superclass

I have a superclass of UIViewController - MasterViewController which declares a property called itemsViewController. This declares a method called from the MasterViewController, and is wired up via a storyboard in IB.
I have a subclass of MasterViewController which redeclares this property as a specific iPad version, but I can't access the redeclared property from the parent class.
MasterViewController
#interface MasterViewController : UIViewController {
}
#property (nonatomic, strong) IBOutlet ItemsViewController *itemsViewController;
#end
#implementation MasterViewController
#synthesize itemsViewController;
-(void)viewDidLoad {
// I can access itemsViewController in viewDidLoad.
}
#end
MasterViewController_iPad
#interface MasterViewController_iPad : MasterViewController {
IBOutlet ItemsViewController_iPad *_itemsViewController;
}
#property (nonatomic, strong) IBOutlet ItemsViewController_iPad *itemsViewController;
#end
#implementation MasterViewController_iPad
#synthesize itemsViewController = _itemsViewController;
-(void)viewDidLoad {
[super viewDidLoad];
// when I call super viewDidLoad, itemsViewController is nil, as though the property hasn't been overriden
// _itemsViewController is not nil in viewDidLoad.
}
#end
Am I misunderstanding the way property inheritance works in Objective-C?
You can't change the type signature of a method when you override a superclass method.
MasterViewController has these methods:
(void)setItemsViewController:(ItemsViewController *)foo
(ItemsViewController *)itemsViewController
But you're trying to give MasterViewController_iPad these methods:
(void)setItemsViewController:(ItemsViewController_iPad *)foo
(ItemsViewController_iPad *)itemsViewController
Which you can't do: you can't overload the same method name but have different types for the arguments.
If ItemsViewController_iPad is a subclass of ItemsViewController, a quick solution would be to keep the same signature as in MasterViewController but simply use an ItemsViewController_iPad when you set the property.
You can use category if you'd like to override property. Here is example:
I have PDFFileChooserViewController with PDFFileModel and PDFFilesDataSource and some logic related to this properties.
#class PDFFileModel, PDFFilesDataSource;
#interface PDFFileChooserViewController : UIViewController
#property (nonatomic, strong) PDFFileModel* selectedModel;
#property (nonatomic, strong) PDFFilesDataSource*dataSource;
#end
Then I'd like to add specific ViewController for choosing files from Dropbox but my model have some additional fields for example dropboxPath and my DropboxDataSource gets files using another way. So I decided to create category and override this properties:
#import "PDFFileChooserViewController.h"
#class DropboxFileModel,DropboxDataSource;
#interface DropboxViewController : PDFFileChooserViewController
#end
#interface DropboxViewController (ModelCategory)
#property(nonatomic, strong) DropboxFileModel* selectedModel;
#property(nonatomic, strong) DropboxDataSource* dataSource;
#end
Notice that this category will be visible inside DropboxViewController only where I can manipulate with that properties but another classes see only super class interface

What’s the point of repeated class interface declaration in the implementation file?

In some of the Apple Iphone examples, some of the properties are declared in the header file and some properties in the implementation file. For example in the Siesmic XML examples
ParseOperation.h
#interface ParseOperation : NSOperation {
NSData *earthquakeData;
#private
NSDateFormatter *dateFormatter;
// these variables are used during parsing
Earthquake *currentEarthquakeObject;
Contact *currentContactObject;
NSMutableArray *currentParseBatch;
NSMutableString *currentParsedCharacterData;
BOOL accumulatingParsedCharacterData;
BOOL didAbortParsing;
NSUInteger parsedEarthquakesCounter;
}
#property (copy, readonly) NSData *earthquakeData;
#end
ParseOperation.m
#interface ParseOperation () <NSXMLParserDelegate>
#property (nonatomic, retain) Earthquake *currentEarthquakeObject;
#property (nonatomic, retain) NSMutableArray *currentParseBatch;
#property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
#property (nonatomic, retain) Contact *currentContactObject;
#end
What is the use of the additional interface declaration in the implementation file ?
That’s simply a difference between a public and a private class interface. The header describes the public interface, but some of the properties are only meant to be used by the class itself, not by its collaborators. These private properties are usually declared the way you described, as a category or a class extension inside the implementation file.
// Foo.h – the public interface
#interface Foo : NSObject {…}
// Collaborators can only read bar.
#property(readonly) int bar;
#property(readonly) int baz;
#end
// Foo.m
#import "Foo.h"
// Private interface
#interface Foo ()
// Inside class implementation we can also change bar.
#property(assign) int bar;
#property(assign) int other;
#end
#implementation Foo
#synthesize bar, baz, other;
…
#end

How to write covariant readwrite properties in class continuations?

Given the following example
// MyClass.h
#interface MyClass {
NSMutableArray *queue;
}
#property (readonly, retain) NSArray *queue;
#end
and
// MyClass.m
#interface MyClass ()
#property (readwrite, retain) NSMutableArray *queue;
#end
#implementation MyClass
#synthesize queue;
#end
I get a Property 'queue' type in 'MyClass' class continuation does not match class 'MyClass' property warning from the compiler. What is the best way to add "private" covariant setters to a class without writing them by hand?
You did it correctly. The issue here is NSArray isn't an NSMutableArray. If you make the private property a NSArray as well, and then write your own setter that accepts a NSMutableArray it should work as expected.