So what is actually the difference between these two versions:
#interface Foo : NSObject
// A guy walks into a bar.
#property(nonatomic, copy) NSString *bar;
#end
// Implementation file
#interface Foo ()
#property(nonatomic, retain) NSArray *baz;
#end
and
#interface Foo : NSObject
// A guy walks into a bar.
#public
#property(nonatomic, copy) NSString *bar;
#private
#property(nonatomic, retain) NSArray *baz;
#end
As far as my understanding goes, putting the #property in the .m basically means that it is private. Correct me if I am wrong? Also which is the best implementation then? Is it just a coding style/practice?
The compiler can warn you about things that it knows about.
When I import your header the compiler can see that Foo has a method called bar and setBar:. This means I can use them both
[instanceOfFoo setBar:#"some string"];
NSLog(#"%#", [instanceOfFoo bar]);
whereas because I only imported the header - the compiler can only see the header it is not aware that there are also methods baz and setBaz: available, so doing the following will cause the compiler to barf
[instanceOfFoo setBaz:#"some string"];
NSLog(#"%#", [instanceOfFoo baz]);
I can however still access these properties if I know they exist by using KVC like this without the compiler barfing
[instanceOfFoo setValue:#"some string" forKey:#"baz"];
NSLog(#"%#", [instanceOfFoo valueForKey:#"baz"]);
You are correct in your understanding. Putting the #property in an #interface in the .m is making it "private". What that means is you'll get compiler warnings if you try to access that property from another class that includes the .h that doesn't include the #property declaration. This doesn't mean that you can't access the property, just that the compiler will yell at you.
As for best, neither one is best. You should implement the one that makes sense for you object, which could include items in both the .h and .m (read only proper in .h with full property in .m). Basically if the #property shouldn't ever be accessed outside of your class put it in the .m.
Related
You can declare a variable like this.
Case1:
#interface MyClass : NSObject
{
NSString *str;
}
#end
Also, if you want to set its property, you can do
Case2:
#interface MyClass : NSObject
{
NSString *str;
}
#property (nonatomic, strong) NSString *str;
#end
And in the .m,
#synthesize str;
My understanding with the difference between Case 1 and Case 2 is that synthesized and propertied variables in Case 2 can be accessed from another class when this another class instantiates this class.
What are other differences between Case 1 and 2? Say when these variables are just used only in its .m file. The fact that you are setting 'str' properties probably makes a difference, but how? If you don't set property, how are they going to be released with ARC?
The differences are fairly confusing in this case because of the way it is set up.
Also it is using what is now old practises.
The new suggested way of doing this (suggested by Apple) is to do this...
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, strong) NSString *str;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
#end
You no longer need the #synthesize as Xcode (since 4.5) will auto generate these for you.
Doing this sets up the property called str and an iVar called _str.
You now no longer need to worry about defining multiple ivars and properties etc... Just use the property and that's it done.
An example setter method for the property str would look like this...
- (void)setStr:(NSString*)str
{
_str = str;
}
I can build the following code for debug, but not for release for an OS X target in xcode:
myclass.h:
#interface myclass : NSObject
#property (nonatomic,copy) NSString *name;
#end
myclass.m:
#implementation myclass {
NSString *_name;
}
#synthesize name = _name;
#end
Any ideas why? On iOS it builds for both release and debug.
I understand that moving the instance variable declaration to the .h will work, but my intention is to hide the implementation details (of course the real class is more complex than this example). I've tried this on the latest xcode version (4.6, build 4H127) on Mountain Lion when building 64-bit apps.
One approach is to use a class extension. In your .m file, write
#interface myclass ()
{
NSString *_name;
}
#end
above your #implementation for myclass.
On the other hand, you don't actually need to declare the instance variable which is backing your property. If you just write
#property (nonatomic, copy) NSString *name;
in your class declaration (the code beginning with #interface myclass : NSObject and ending with #end), a backing NSString * ivar _name will be generated for you automatically, and you'll be able to access this ivar inside the instance methods of myclass. You don't even need to write #synthesize name = _name;. Furthermore, if you want to use a variable name other than _name to back your property name, you needn't declare the ivar; instead you can just use #synthesize
#synthesize name = m_Name;
inside your class' #implementation block.
Instance variables in the implementation block requires the modern Objective-C runtime. See Objective-C Feature Availability Index. It could be that the release is targeting a version of OS X which does not support the modern runtime.
you have not specified object type of the property "name" here:
#property (nonatomic,copy) name;
use this
#property (nonatomic,copy) NSString *name;
Sorry for the simple question.
When I see a definition of a property inside the h file, but outside of the class #interface scope, what does it mean ?
#property (nonatomic, readonly) RMMapContents *mapContents;
Here is the code:
#class RootViewController;
#class RMMapContents;
#interface MapTestbedAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
//MAIN VIEW
//==============
RootViewController *rootViewController;
// NETWORK DATA
// =============
NSMutableArray *photoTitles; // Titles of images
NSMutableArray *photoSmallImageData; // Image data (thumbnail)
NSMutableArray *photoURLsLargeImage; // URL to larger image
NSMutableData *receivedData;
NSURLConnection *theConnection;
NSURLRequest *request;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
#property (nonatomic, readonly) RMMapContents *mapContents;
#end
Inside a function I see this line:
- (void)foo:(xyz *)abc{
..
RMMapContents *mapContents = [self mapContents];
..
}
So, taking it from C++, the mapContents seem like it is not a global scope var (after all, that's why they call them properties, right?), but isn't defining the same name again inside the function weird a bit?
I hope someone can clarify a little here.
Thanks!
The scope of the #interface block extends upto the #end keyword and is not restricted to the braces {}.
So the #property declaration lies very much inside the scope of the #interface and like cli_hlt rightly answered, it acts like a substitute to setter and getter methods for the mapContents property.
so a property named mapContents, would have setters and getters which look like this :
- (void)setMapContents; //setter
- (RMMapContents *)mapContents; //getter
and would can be accessed from within the class using these methods:
[self setMapContents:newContents];
AND
RMMapContents *contents = [self mapContents];
Well, a property is not just a variable. A property is a variable plus its setter and getter methods. A property is usually said to be backed by a variable, which usually(but not always) has the same name as the property itself.
So there are basically three scenarios:
The developer has redefined the backing variable, look for something like:#synthesize mapContents=mapContents_, at the beginning of the implementation -> no problem here.
The compiler defined the variable to be something you don't now but not equal to mapContents - > no problem.
The property backing variable is indeed called "mapContents", so then the local definition hides the global definition (look for a compiler warning here). But by calling [self mapContents] you will not access the global variable but call the getter, which in turn will access the class variable (because then the local mapContents is out of scope)
Hope this helps.
global var mapContents is readonly,in foo function , create a new pointer,then you can change the value of inner var.
Look for a method in your class with a name mapContents that will return a initialization to your RMMapContents class.
Basically this line RMMapContents *mapContents = [self mapContents]; says that initializing an instance of RMMapContents called mapContens using the method mapContents.
I've kind of been confused about properties. Some people say to always use setters and getters for ivars, even within the ivar's class. So if "name" is an ivar, when referring to it, one should always use "self.name". Always. Even if you're in the same class that "name" is declared in.
First, is that correct advice?
Second, what if I wish to reap the automatic memory management that comes with declaring "name" as a property and synthesizing it, but I don't want to give other classes access to change "name"? I guess it would be sort of a private property?
Thanks!
Yes, you should always try to use the property accessors when possible. Using ARC alleviates these concerns somewhat, but it's still good style. As for your second question, you can declare the property as readonly in the public header file and redefine it in a class extension:
In MyClass.h:
#interface MyClass : NSObject
#property (strong, readonly, nonatomic) id foo;
#end
In MyClass.m:
#interface MyClass()
#property (strong, readwrite, nonatomic) id foo;
#end
#implementation MyClass
#synthesize foo = _foo;
// The rest of your code goes here.
#end
This will allow you to call [self setFoo:foo] all day inside of MyClass’s implementation, but not other classes.
For ivars which are accessed externally, I generally use properties to access the ivar from within the class, for ivars which are only used internally (usually BOOL, NSUInteger, NSInteger, etc), I use the ivar directly. I do however access an consistently within the class (i.e. if I'm using a property to access it, I always use a property).
For the second part of your question. You can create a readonly property in the class's interface definition and within the same file as the implementation create a category with the read-write property. For example:
MyClass.h
#interface MyClass : NSObject
{
NSString * name;
}
#property (nonatomic, readonly) NSString * name;
#end
MyClass.m
#interface MyClass()
#property (nonatomic, retain) NSString * name;
#end
#implementation MyClass
#synthesize name;
-(void)dealloc
{
[name release];
[super dealloc];
return;
}
#end
Keep in mind, that although another class accessing the method -setName: may cause compile warnings or errors, another class may still call -(id)performSelector:withObject: with without an error.
For instance:
MyClass * test = [[MyClass alloc] init];
test.name = #"David";
is functionally the same as:
MyClass * test = [[MyClass alloc] init];
[test performSelector:#selector(setName:) withObject:#"David"];
Hey all i have been trying to figure out why i am getting this warning:
'TxtAppDelegate' may not respond to '-TCN'
'TxtAppDelegate' may not respond to '-TID'
when i try to use this code:
// .h file
#interface RootViewController : UITableViewController <UIActionSheetDelegate> {
NSString *theCompanyName;
NSString *theID;
}
#property (nonatomic, retain)NSString *theCompanyName;
#property (nonatomic, retain)NSString *theID;
// .m
NSString *theCompanyName;
NSString *theID;
#synthesize theCompanyName;
#synthesize theID;
TxtAppDelegate *customObjInstance = [[TxtAppDelegate alloc] init];
theCompanyName = [customObjInstance TCN];
theID = [customObjInstance TID];
I've added the header for the .h file that has the two functions in them. The code works but i really would like to solve the warning problem.
Any help would be great to solve this problem of mine :)
David
While it would have been more helpful to see the header file where TxtAppDelegate is declared, I'm guessing the method declarations must be off. They should look like this:
- (NSString *)TCN;
- (NSString *)TID;
If this is not the cause of the problem, please post the header file here so we can examine it.
How are declared these two functions in your header?
They should belong to a category of TxtAppDelegate class or to a protocol. If you choose the protocol, TxtAppDelegate interface should mention that it conforms to that protocol.