I have written a library to automatically generate NSUserDefaults accessors based on #dynamic properties that you declare in a 'preferences' class (see PAPreferences). You write the property in a .m file like this:
#property (nonatomic, assign) BOOL hasSeenIntro;
and then add this to the .h file:
#dynamic hasSeenIntro;
This works fine but if the user accidentally forgets to put in the #dynamic line, then the compiler will automatically generate an equivalent #synthesize line instead. There will be no warnings but of course my code won't be invoked for that property.
I'd like to know if there's a way to disable automatic property synthesis just for this class.
Update:
Thanks to Nikolai's answer, I remembered that it's possible to promote LLVM warnings to errors and wrapping the declaration with that error achieves the effect I was looking for (an error will be raised if the user forgets to specify the #dynamic line):
// Ensure we get an error if we forget to add #dynamic for each property
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wobjc-missing-property-synthesis"
#interface Preferences : PAPreferences
#property (nonatomic, assign) BOOL hasSeenIntro;
#property (nonatomic, assign) NSInteger pressCount;
#end
#pragma clang diagnostic pop
There's no way to do this via code.
There's a compiler warning (controlled via Xcode's build setting "Implicit Synthesized Properties", CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS) but you have to manually set this on the implementation file, so for your case that's not really helpful.
Here's another idea: Change your implementation to add the properties using a category on PAPreferences instead of a subclass. Then the compiler can't synthesize the accessors and will emit a warning if the #dynamic is missing.
#interface PAPreferences (SynthesizedProperties)
#property int foo;
#end
#implementation PAPreferences (SynthesizedProperties)
#end
Result:
> warning: property 'foo' requires method 'foo' to be defined - use #dynamic or provide a method implementation in this category
Additionally (or instead) you can introspect the property during runtime to detect accidentally synthesized accessors and emit a warning in this case.
AFAIK there is no way to do that, as in previous Xcode versions there was no way to flag wether you had forgotten to #synthesize a specific property. Only at runtime you'd have a Unrecognize selector error being raised.
Related
That line gives me a warning in the source code
#property (nonatomic) dispatch_queue_t queue;
No 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed
My project is non-ARC and I cannot change it at the moment.
How do I fix the warning? (nonatomic, assing)?
The system needs any of those 3 attributes, if none is present, it will assume it's assign the chosen one. If you write (nonatomic, assign) it will work exactly the same way it's working now so, if it's working o, go ahead and write assign.
I add a property to my category and use #dynamic in my category implementation.
It seems work well.
#interface aClass (Properties)
#property (nonatomic, readonly) NSString *p;
#end
#implementation aClass (Properties)
#dynamic p;
#end
I know #dynamic means tell The compiler not to automatically synthesize the getter and setter methods.but why above code could work without my getter/setter implementation?
is it provides by runtime?
This would compile (and link, provided you had an actual #interface and #implementation and not just a category). But it would crash at runtime if you tried to use the getter.
When you passed #dynamic, you promised that the getter would be available at runtime (not the setter; this is readonly). If you try to use the getter at runtime, you will discover that your promise was not fulfilled and you'll crash with an "unrecognized selector" message. #dynamic says "trust me." You then have to be trustworthy.
(Note that classes should always start with an uppercase letter.)
Ah, you mentioned it was CALayer. That changes everything. CALayer is magic. Well, not magic, but definitely special. CALayer catches unhandled selectors and turns them into dynamic properties (storing the values in a dictionary I think), just like you're seeing. It was a very clever idea, and back in 10.5, some of us thought maybe this would be the "new way." (It's a natural extension of how Core Data works.) I think Apple figured out that it's too clever because they haven't spread it to other classes.
I actually talked with some of the Apple devs about it a few years ago at WWDC. I wanted to know if it was safe to rely on. The folks I talked to didn't realize it was in there and didn't recommend it.
I've been doing Objective-C programming for a few years now. I was listening to a podcast the other day which mentioned something about how Apple has made it easier over the years, and I thought I heard mention of there being no need to manually add instance variables now. Is this true? Here's how I do it currently:
.h:
#interface Class : UIView
#property (nonatomic, strong) NSString *testString;
#end
.m:
#interface Class () {
NSString *_testString;
}
#end
#implementation Class
#synthesize testString = _testString;
Is this work necessary?
This is all you need now
.h:
#interface Class : UIView
#property (nonatomic, strong) NSString *testString;
#end
.m:
#implementation Class
#end
#property will automatically create an instance variable now, and #synthesize is automatically added unless you specify otherwise. So yes, just a #property is enough.
Nope, it will auto-synthesize in Xcode 4.4+
You can read more about it here.
Although you don't need to type that boilerplate code for non-#dynamic properties since LLVM 4.0 (Xcode 4.4+), it's a good thing to know that it is a compiler feature, not part of the language (Objective C), nor the runtime system. The runtime system still rely on instance variables and getters/setters generated by the #synthesize directive. It's the compiler who is able to generate the code for you, pretty much like it is able to follow conventions and generate calls to retain and release in ARC code.
So, it is important to notice that, if you are going to share your project with other developers using older versions of Xcode (specifically, older versions of the Clang/LLVM compiler), you must synthesize your variables or the project will not compile in their machines or will fail at runtime.
All of that work is unnecessary.
Just declare the property, it will automatically default to creating an instance variable with the underscore convention. Though, self.property may tickle your fancy as well.
You can do the same for private properties by declaring them in an interface extension in the .m file.
#synthesize-ing is no longer necessary. #dynamic is still necessary if I understand correctly
If I have a #property which I didn't want to have backed via an ivar I simply omitted the #synthesize and had manual getters which returned a calculated value.
However, now since Xcode 4.4 if I don't specify #synthesize do compiler will automatically generate it. Does that mean it will also generate an ivar even do I don't need/use it?
I could eventually force to not auto-synthesize by using dynamic. However that would be wrong, since #dynamic is supposed to be used for turning off warnings if getter and setter are implemented somewhere else or during runtime.
In my working with this, I've noticed the following behavior.
If you have a readwrite property, don't have a #synthesize, have a getter and don't have a setter, then it will generate the iVar.
If you have a readwrite property, don't have a #synthesize, don't have a getter, and have a setter, then it will generate the iVar.
If you have a readwrite property, don't have a #synthesize and have both a getter and a setter, then it will not generate the iVar.
If you have a readonly property, don't have a #synthesize and don't have a getter, then it will generate the iVar.
If you have a readonly property, don't have a #synthesize and have a getter, then it will not generate the iVar.
From this, I think the general rule is that if you don't have a #synthesize, and have all the methods needed to fully implement the property, then it's assumed to be dynamic and doesn't generate the iVar.
At any rate, if you want to ensure that an iVar is not generated then declare it as #dynamic.
Clarification on #dynamic
From Declared Properties in The Objective-C Programming Language:
You use the #dynamic keyword to tell the compiler that you will fulfill the API contract implied by a property either by providing method implementations directly or at runtime using other mechanisms such as dynamic loading of code or dynamic method resolution.
To me this reads like it OK to mark a property as #dynamic even when you are directly implementing the getter and setter.
If you mark the property as readonly and implement the getter yourself, it seems that iVar will not be created.
Interface declaration:
#property (nonatomic, readonly) BOOL myBoolProp;
Impementation:
- (BOOL)myBoolProp {
return true;
}
Trying this:
- (void)viewDidLoad {
[super viewDidLoad];
_myBoolProp = true;
}
will generate an error: Use of undeclared identifier '_myBoolProp'
Removing the custom getter method also removes the error, appearing to demonstrate that the iVar has now been generated.
Yes - iVars are still generated by clang (not Xcode, as it is the IDE, clang is the complier that really matters).
If you really don't want iVars, and don't want an implementation, there is the somewhat archaic #dynamic keyword that will do what you want, or you can specify the property in a protocol, which doesn't make it auto-synthesized:
// .h
#property (nonatomic, retain) NSObject *someProp;
//.m
#dynamic someProp; // no iVars generated
// other solution
#protocol MyObjectProtcol<NSObject>
#property (nonatomic, retain) NSObject *someProp;
#end
// now, when you implement the MyObjectProtocol protocol, the property won't auto-synthesize.
Finally I'm transitioning to ARC. Sounds too late but all my projects have retrocompatiilty to 3.0 (any news about App Store unsupporting?) so I can't use it. But now I'm working in a new project with base deployment in iOS 5 so I'm using ARC.
My question is so simple. I'm used to declare private instance variables and public properties. For example:
#interface MyClass : NSObject {
#private
Var *aVar_;
Var *anotherVar_;
}
#property (nonatomic, readonly) Var *aVar;
#end
#implementation MyClass
#synthesize aVar = aVar_;
#end
Inside the class I work with instance variables, not properties.
But now I'm trying to avoid instance variables because I think there are not neccessary and redundant if I use proeprties, and I read time ago that is better to use properties instead of instance variable, but I'm not sure. That class now seems like that
#interface MyClass : NSObject
#property (nonatomic, readwrite, strong) Var *aVar;
#end
#interface MyClass()
#property (nonatomic, readwrite, strong) Var *anotherVar;
#end
#implementation MyClass
#synthesize aVar = aVar_;
#synthesize anotherVar = anotherVar_;
#end
In this case I'm still using instance variables (underscored) to manage my data because is less verbose and ARC takes into account all memory issues, but I don't know if that is correct.
Also I have another question. The property of aVar in the first chunk of code is readonly but if I use only properties I have to make that property readwrite. If I want to make the public property readonly, do I have to declare a public readonly property in the #interface and a private readwrite in private #interface?
Thank you so much.
The answer to your questions is somewhat complex, but generally you have the swing of it.
Since ARC does all the memory management for you, its often simpler to just use an ivar (private to the class, declared in the implementation) for your internal needs. In that case all usages just use the name.
With properties, you can as of Xcode 4.4 let Xcode synthesize the setter and getter, as well as the ivar. Auto-synthesized ivars are created with a leading "_" character.
You can define a property as readonly in the implementation, leave it as readonly, and set it in your code as '_foo = ....'. [Many on this site would consider this a bad practice, my point is you can do it.]
Xcode 4.4 has a warning titled "Implicit Synthesized Properties" with a default of NO. This creates a warning if you do not provide a #synthesize statement for each property, even though it will do the synthesis anyway.
Personally, I use ivars whenever I can, and only define properties when I need to either make something public to other classes, or I have categories declared in multiple files (in which case I put the interface declaration of the class extension in its own file along with properties defined in it.)
With the new Objective-C update you don't even need to synthesize the property. All you need to do is declare #property (strong, nonatomic) Var *aVar; and the compiler will automatically add the synthesizing, backing the self.aVar property with an _aVar instance variable.
If you declare a property, your implementation should generally use that property even though ARC reduces some of the memory management errors.
In init some prefer to avoid using properties because doing so might trigger KVO on an object (self) that is only partially initialized.