Is declaring strong actually needed for Objective-C properties? - ios

My understanding so far is that (retain) increases the reference count of a property and is essentially the exact same as (strong). Since all properties are set to retain by default (unless specified otherwise), is adding (strong) needed at all:
#property(nonatomic, strong) NSString *name;
Is the same as:
#property(nonatomic) NSString *name;
Both the above are the same, right?

Since ARC was introduced, "strong", "atomic", and "readwrite" are set by default.
These properties are equivalent:
#property NSArray *name;
#property (strong, atomic, readwrite) NSArray *name;
Source: http://useyourloaf.com/blog/default-property-attributes-with-arc.html

From the documentation:
By default, both Objective-C properties and variables maintain strong
references to their objects.
So both forms are the same.

Related

Is the NSObject code below redundant?

I am following the tutorial here: http://blog.soff.es/archiving-objective-c-objects-with-nscoding
to create an NSObject that can save my match data in a turn based game.
However I get this warning in my .m file:
Autosynthesized property 'title' will use synthesized instance variable '_title', not existing instance variable 'title'
So my Qustion is if (in the code below) I delete the code in between the brackets will I be losing something important?
#interface Note : NSObject <NSCoding> {
NSString *title;
NSString *author;
BOOL published;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *author;
#property (nonatomic) BOOL published;
#end
You shouldn't explicitly declare ivars since the properties will auto-synthesize their own ivars with slightly different names. The explicit ivars are pointless and won't be used by the properties. Having them is just going to lead to bugs when you use your ivars by mistake when you meant to set a property.
The warning is pointing this out by letting you know there will be two similar ivars.
Your code should simply be:
#interface Note : NSObject <NSCoding>
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *author;
#property (nonatomic) BOOL published;
#end
This avoid bugs such as:
title = #"Some Title"; // sets your ivar, not the property
as opposed to:
_title = #"Some Title"; // directly sets the property's ivar
Of course you should use the property:
self.title = #"Some Title"; // uses the property methods
You would be losing instance variables, but you don't really want them as the properties will create them for you (with slightly different names) and it's safer to access these (kind of hidden) auto-generated instance variables via the property accessor methods.
Yes it is and likely you get something you do not want.
If you do not have an instance variable neither with the identifier identifier nor with the identifier _identifier, manual synthesize and automatic synthesize will create one with the name _identifier.
If you already have an instance variable with the identifier _identifier, manual synthesize and automatic synthesize will use it. (Therefore in most cases it is meaningless to declare such an instance variable.)
But if you have an instance variable with the identifier identifier (without underscore) manual synthesize will use it, while automatic synthesize will not use it and instead create a new one with the identifier _identifier. Therefore you have two instance variables identifier and _identifier. Typically you do not want this.

Xcode is making me use temporary variable...?

Newbie to Objective-C....
I have a real simple .h file:
#interface IdentityManager : NSObject
#property (nonatomic, weak) NSString *username;
#property (nonatomic, weak) NSString *password;
#property (nonatomic, weak) NSString *connString;
#property (nonatomic, weak) NSString *token;
#end
And I want need to grab text from some text fields in another object to load into an Identity object:
self.identity.username = self.usernameTextField.text;
self.identity.password = self.passwordTextField.text;
Yep, it's a login page. Problem is that the username would not be set. After hours trying to find out why, I found that putting the value of self.usernameTextField.text into a local variable and passing the value of that to the Identity object worked:
NSString *tempUsername = self.usernameTextField.text;
self.identity.username = self.tempUsername;
self.identity.password = self.passwordTextField.text;
I have no idea why this would be. I can only guess that all my messing around has somehow left a trace of some old references in Xcode's cache somewhere.
More likely, I'm an idiot. Better, I'm still learning.
Should I be using NSMutableString?
I think something similar is happening again elsewhere. Use of a temp variable helping to achieve my goal.
Any thoughts anyone?
I don't really think your second solution actually fixes the issue. It's probably something else.
Though it's really wrong for a manager class to have a weak reference. In OOP, classes and especially managers should own the object they have as property.
So you should use strong references instead of weak:
#property (nonatomic, strong) NSString *username;
Additionally, you don't want any changes outside the class to modify the variable, so you should be passing a copy of the object:
NSString *username = self.usernameTextField.text;
self.identity.username = [username copy];
Alternatively, you can declare the property as copy instead of strong and you don't have to worry about copying the string every time you set it. (Credit to albertamg)
#property (nonatomic, copy) NSString *username;

Xcode requiring me to redeclare properties as instance variables

I have an object called SCPFAd and it is declared in its header file as follows:
#interface SCPFAd : NSObject
#property (strong, nonatomic) NSArray *imageURLs;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *price;
#property (strong, nonatomic) NSString *longDescription;
#property (strong, nonatomic) SCPFLocation *location;
#property (strong, nonatomic) SCPFCategory *category;
#property (strong, nonatomic) NSArray *properties;
#property (readonly, strong, nonatomic) NSString *sellerID;
#property (readonly, strong, nonatomic) NSString *timePosted;
- (id)initWithRawData:(NSDictionary *)rawData;
- (BOOL)displaysPrice;
#end
In the implementation file, I have an SCPFAd extension declared this way:
#interface SCPFAd ()
{
NSMutableDictionary *_rawData;
NSMutableArray *_imageURLs;
NSString *_title;
NSString *_price;
NSString *_longDescription;
SCPFLocation *_location;
SCPFCategory *_category;
NSMutableArray *_properties;
}
#property (strong, nonatomic) NSDictionary *rawData;
#property (strong, nonatomic) NSString *sellerID;
#property (strong, nonatomic) NSString *timePosted;
#property (strong, nonatomic) NSString *adID;
#end
I deliberately redeclared the properties rawData, imageURLs, and properties as instance variables because I want external objects to access or assign them as immutable types, but I'll be changing them internally.
What I don't understand is why, when I override the setters, I get a compiler error that says it can't find the variables _title, _price, _longDescription, _location, and _category. The error goes away when I redeclare title, price, longDescription, location, and category as above, but I see it as unnecessary--nothing in the class extension changes their external declarations.
This is how I'm overriding setTitle, for example:
- (void)setTitle:(NSString *)title
{
_title = title;
_rawData[#"name"] = title;
}
- (NSString *)title
{
if (!_title) {
_title = _rawData[#"name"];
}
return _title;
}
If I comment out NSString *_title; in the extension, the compiler says it can't find _title in the first line of the setter, and wherever it occurs in the getter. The getter used to work just fine, though, even without the redeclaration.
If you declare a property and then override both the getter and setter, it won't auto-synthesize the property. But you can just add a line to synthesize it to your implementation:
#synthesize title = _title;
As for having a property be an immutable type, and its backing instance variable be mutable, you're going to have an issue when from outside your class the immutable type is assigned to it, and you treat it as the mutable version, because it won't respond to the methods to mutate it. For example, you assign an NSArray to a variable, then try to treat it as an NSMutableArray, it won't work.
If you implement a getter, the compiler doesn't automatically create an ivar.
This is for a good reason. The property may (and, in my experience, usually is) created on request and returned, so in that case no instance variable is needed to store it and it would add a significant memory overhead to classes with a large number of such properties if every getter had an associated ivar.
One other comment. This:
NSMutableDictionary *_rawData;
// ...
#property (strong, nonatomic) NSDictionary *rawData;
May cause you problems. If rawData is set with an immutable dictionary, it will raise an exception when you attempt to mutate it later. Make sure you copy it on assign using -mutableCopy. (I assume you aren't copying it because it's marked strong, not copy. If you are, it's fine)
When you override the setter and getter (not just the getter), Xcode assumes you want complete control and doesn't create the backing store (the _title). You have to do it yourself with
#synthesize title = _title
If you implement a getter and a setter for a read-write property, or a getter for a read-only property then Clang (Xcode) will not synthesise the backing instance variable - see Apple's Encapuslating Data, note in the section You Can Implement Custom Accessor Methods.
You are implementing both the setter and the getter so you must provide your own instance variable if needed.

Use transient property on any Core Data attribute that doesn't need to be saved?

I'm trying to understand transient properties. I have this object:
#interface AddressAnnotation : NSObject <MKAnnotation>
#property (nonatomic, copy) NSString *address;
#property (nonatomic, copy) NSString *city;
#property (nonatomic, strong) NSNumber *latitude;
#property (nonatomic, strong) NSNumber *longitude;
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, strong) NSString *state;
#property (nonatomic, strong) NSString *street;
#property (nonatomic, strong) NSString *zip;
#property (nonatomic, strong) NSString *name;
I use this to show my annotation on a MKMapView. I want to save these pins in some Route entity. A Route would just let the user name the route. For my app, the only thing really important is the latitude and longitude. The other properties I can always recalculate with a reverse geocoder since I have the lat/long. To save space, I was thinking that if I want to make this object a Core Data entity, can I make all the properties that are not latitude and longitude transient properties? I read some examples where transient was used for a prperty that was calculated based on other non-transient properties. Is this a proper use of transient? Thanks.
That would be the case you can apply transient property in your example. From my perspective, you can still keep some properties in the core data to avoid redundant query for later use. For example, the address you can use reverse geocode to get the real address by longitude and latitude. But you will let user to wait for query address every time when you use reverse geocoder. I will prefer to keep the properties in the core data if that property needs a bit of calculation or wait for connection.

unknown property attribute 'atomic'

When i use FMDB test demo in xcode4.2, everything is good. But when I run the demo in xcode 3.2.6, it gives the error:"unknown property attribute 'atomic'"
__unsafe_unretained id _delegate;
NSUInteger _maximumNumberOfDatabasesToCreate;
}
#property (atomic, retain) NSString *path;
#property (atomic, assign) id delegate;
#property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate;
How can I fix this error ?
As far as I remember "atomic" attribute supported only by clang. When using gcc every property not declared as "nonatomic" is "atomic" by default.
atomic and __unsafe_unretained were introduced with LLVM 3.0. If you are using Xcode 3.2.6, you are using an older version of the compiler that does not support those keywords.
You can safely remove the atomic keyword, since a property is atomic by default; and also remove __unsafe_unretained, since approximately it is equivalent to assign in a property declaration.
You can use Clang's preprocessor macros to determine whether atomic is available in your compiler. If the atomic keyword is not supported, it should be safe to omit it, since atomic is the implicit behavior anyway.
#if __has_feature(objc_atomic)
#property (atomic, retain) NSString *path;
#property (atomic, assign) id delegate;
#property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate;
#else
#property (retain) NSString *path;
#property (assign) id delegate;
#property (assign) NSUInteger maximumNumberOfDatabasesToCreate;
#endif

Resources