Can the memory management of a property change if it's redefined in a class extension? - objective-c

If I have a property like this:
//test.h
#interface test
#property (nonatomic, readonly, weak) NSObject x;
#end
redefined in the implementation file to be read/write:
// test.m
#interface test ()
#property (nonatomic, readwrite) NSObject x;
#end
I used weak in .h, but I said nothing in the extension, will the property keep the 'weak' specifier, or will it change to 'strong'?
Will the keywords strong/assign/weak be overwritten when the property is redefined?

A simple test with Xcode 5.1.1 shows the weak attribute is kept. The same is true for the assign and strong attributes - you can specify them in the .h and omit them in the .m, if you do include them in the .m the two must match.
Having said that, I do not know if this is documented anywhere. But then the semantics of Objective-C are not formally defined anywhere either. So use at your own risk.
Recommendation: just repeat it.

Related

Can a property be declared with #synthesize only without #property?

I found the following code in my project (using ARC). It looks like there is no property adDisplay declared. Only a synthesized statement and the compiler doesn't complain about it.
#interface MyClass() <MyProtocol> {
AdDisplay * _adDisplay;
}
#end
#implementation
#synthesize adDisplay = _adDisplay;
...
#end
Do I need to add
#property (nonatomic, strong) AdDisplay * adDisplay;
if I want to make sure adDisplay is strongly retained?
thanks
Update: it is declared in the protocol indeed. Does that count as instance variable?
A property in Objective-C is a name for a pair of methods: a setter and a getter. Declaring a property means saying that these two methods exist (for readwrite properties).
There does not have to be any real storage or memory management. Historically, under manual memory management, assign, retain, or copy would affect how the methods are synthesized.
However, under ARC, this is no longer needed. When you declare a property as strong or weak, it does not affect the body of the method, but only the type of the ivar.
Since you have an ivar declared manually
AdDisplay * _adDisplay;
(as strong)
then strong or weak inside the #property declaration won't have any real effect, only as a documentation for the API consumers.
The following would also work:
#interface MyClass() <MyProtocol>
#end
#implementation
#synthesize adDisplay;
#end
Also note this used to be a common memory leak problem when migrating projects from MRC to ARC. A property declared as weak, with an ivar declared as strong.

What does this objective-c property synthesis warning mean?

Since upgrading to Xcode 5.1, I'm starting to see the following warning in some code my project uses. I'm trying to figure out what it means.
Warning: Auto property synthesis will not synthesize property 'responseHeader' because it is 'readwrite' but it will be synthesized 'readonly' via another property
The code where it's occurring, in the .m file:
#interface S3Response ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
The previous declaration of the property, in the .h file:
#property (nonatomic, readonly) NSDictionary *responseHeader;
There is no #synthesize statement for that property, nor are responseHeader or setResponseHeader defined as methods. There is however an explicit definition of an ivar named responseHeader.
Seems pretty straightforward to me: property is declared as read-only for users of the class, but read-write locally so the class can set it.
What does this warning mean, and what should I do about it?
That code seems to be from the AWS SDK for iOS,
and S3Response is a subclass of AmazonServiceResponse.
The public AmazonServiceResponse interface defines a read-only property
#interface AmazonServiceResponse:NSObject
// ...
#property (nonatomic, readonly) NSDictionary *responseHeader;
#end
which is redefined as read-write in a class extension in the implementation file:
#interface AmazonServiceResponse ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
Now the subclass S3Response also wants read-write access to this property,
and therefore also defines in the class extension of its implementation file:
#interface S3Response ()
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
#end
The compiler complains because – when compiling "S3Response.m" – it does not know
that a setter for the property exists in the superclass (it does not read
the implementation file of the superclass at that point). Also the compiler cannot
simply synthesize a setter in the subclass, because it cannot not know that the
property is backed-up by an instance variable in the superclass.
But you know that a setter will be generated, so you can remove the warning by
adding a #dynamic declaration to the subclass implementation:
#implementation S3Response
#dynamic responseHeader;
...
#dynamic is a "promise" to the compiler that all necessary accessor methods will
be available at runtime.
The problem here is as follows.
By default, if don't write ownership (weak/retain/strong/assign) explicitly, xCode will check the type automatically. So in case of NSDictionary it will be strong. Thus, in interface you will have
#property (nonatomic, readonly, strong) NSDictionary *responseHeader;
Then it will be contradict you private implementation definition
#property (nonatomic, readwrite, retain) NSDictionary *responseHeader;
Compilator doesn't match strong and retain under property synthesizing though it is formally the same thing.
To cure situation you can write retain in both cases, or more correct, you should not write retain at all. It will be strong by default in both definitions.

How to create a new instance of Foo class without exposing its constructor in the interface?

This isn't strictly related to NewsstandKit.framework, but there's a live example of this approach in there:
addIssueWithName:date: instance method of NKLibrary creates a newsstand issue — NKIssue — and adds it to the content library and in case of non–failure returns it. At this point NKIssue instance has name and date properties are set to values passed to addIssueWithName:date: initially.
If you check that out, NKIssue class doesn't have a specific initialization method (other than plain init inherited from NSObject) nor #public instance variables (e.g. _name and _date) and its properties are all readonly.
So I don't get how this whole thing could work internally? I would love to mimic this approach in one of my libraries, but can't really figure out how...
Thanks.
Apple's code has access to Apple's code. The readonly properties of NKIssue are likely re-declared as readwrite in a class extension. Have a look at "Property Redeclaration" in TOCPL.
In the header, you see:
#interface Carborundum : NSObject
#property (readonly, copy, nonatomic) NSString * whiskers;
#property (readonly, retain, nonatomic) NSDate * inception;
#end
In the implementation file (or other private file) there's a class extension that has the same properties, with the exact same attributes (this is required) except readonly.
#interface Carborundum ()
#property (copy, nonatomic) NSString * whiskers;
#property (retain, nonatomic) NSDate * inception;
#end
They're synthesized as usual:
#implementation Carborundum
#synthesize whiskers;
#synthesize inception;
#end
This allows code that can see the class extension (i.e., other UIKit code) to use the properties as read-write, while your code, which only has access to the header, is limited to the read-only version. This is enforced by the compiler, not the runtime.
There's a fair number of SO questions that go into this: https://stackoverflow.com/search?q=%5Bobjc%5D+redeclare+property

"Expected a type" error pointing to the return type of a method

I've attempted to compile, but every time I do, one method throws a strange "expected a type" error. I have a method in the header:
-(ANObject *)generateSomethingForSomethingElse:(NSString *)somethingElse;
The error points at the return type for this method. I've imported ANObject into the header using #import "ANObject.h" and ANObject is compiling fine..
Why is this happening?
This is to do with the order that the source files are compiled in. You are already probably aware that you can't call a method before it is defined (see below pseudocode):
var value = someMethod();
function someMethod()
{
...
}
This would cause a compile-time error because someMethod() has not yet been defined. The same is true of classes. Classes are compiled one after the other by the compiler.
So, if you imagine all the classes being put into a giant file before compilation, you might be able to already see the issue. Let's look at the Ship and BoatYard class:
#interface BoatYard : NSObject
#property (nonatomic, retain) Ship* currentShip;
#end
#interface Ship : NSObject
#property (nonatomic, retain) NSString* name;
#property (nonatomic, assign) float weight;
#end
Once again, because the Ship class has not yet been defined, we can't refer to it yet. Solving this particular problem is pretty simple; change the compilation order and compile. I'm sure you're familliar with this screen in XCode:
But are you aware that you can drag the files up and down in the list? This changes the order that the files will be compiled in. Therefore, just move the Ship class above the BoatYard class, and all is good.
But, what if you don't want to do that, or more importantly, what if there is a circular relationship between the two objects? Let's increase the complexity of that object diagram by adding a reference to the current BoatYard that the Ship is in:
#interface BoatYard : NSObject
#property (nonatomic, retain) Ship* currentShip;
#end
#interface Ship : NSObject
#property (nonatomic, retain) BoatYard* currentBoatYard;
#property (nonatomic, retain) NSString* name;
#property (nonatomic, assign) float weight;
#end
Oh dear, now we have a problem. These two can't be compiled side-by-side. We need a way to inform the compiler that the Ship* class really does exist. And this is why the #class keyword is so handy.
To put it in layman's terms, you're saying, "Trust me man, Ship really does exist, and you'll see it really soon". To put it all together:
#class Ship;
#interface BoatYard : NSObject
#property (nonatomic, retain) Ship* currentShip;
#end
#interface Ship : NSObject
#property (nonatomic, retain) BoatYard* currentBoatYard;
#property (nonatomic, retain) NSString* name;
#property (nonatomic, assign) float weight;
#end
Now the compiler knows as it compiles BoatYard, that a Ship class definition will soon appear. Of course, if it doesn't, the compilation will still succeed.
All the #class keyword does however is inform the compiler that the class will soon come along. It is not a replacement for #import. You still must import the header file, or you will not have access to any of the class internals:
#class Ship
-(void) example
{
Ship* newShip = [[Ship alloc] init];
}
This cannot work, and will fail with an error message saying that Ship is a forward declaration. Once you #import "Ship.h", then you will be able to create the instance of the object.
I found this error hapenning when there is circular dependency on the headers. Check if the .h file where you declare this method is imported in ANObject.h
You basically add
#class ANObject;
before #interface!
So, for some reason I was getting this error while trying to set a method with an enum type in the parameters. Like so:
- (void)foo:(MyEnumVariable)enumVariable;
I had previously used it like this and never had an issue but now I did. I checked for circular dependency and could find none. I also checked for typos multiple times and no dice. What ended up solving my issue was to adding 'enum' before I wanted to access the variable. Like so:
- (void)foo:(enum MyEnumVariable)enumVariable;
{
enum MyEnumVariable anotherEnumVariable;
}
Usually when I see an error like this it's because I have a typo on a previous line, such as an extra or missing parenthesis or something.
It may sound stupid, but wrong shelling or wrong use of uppercase/lowercase letterwrong case this.
I got this message, when the variable type was misspelled. See below this below
e.g.
-(void)takeSimulatorSafePhotoWithPopoverFrame:(GCRect)popoverFrame {
instead of.....
-(void)takeSimulatorSafePhotoWithPopoverFrame:(CGRect)popoverFrame {
Strangely enough, changing the order of my imports has fixed this in the past... Try moving the import to the bottom after all your other imports.
I solved it by adding #class class_name to the .h file

Objective-C accessor declarations (readonly, readwrite, etc)

In the book, "Cocoa Design Patterns," the author sometimes declares a property in the #interface as readonly:
// .h
#property (readonly, copy) NSArray *shapesInOrderBackToFront;
and then later adds an unnamed category to the implementation (.m) file like this:
// .m
#interface MYShapeEditorDocument ()
#property (readwrite, copy) NSArray *shapesInOrderBackToFront;
#end
Any idea as to why? It's unclear to me how this approach is better than, or more necessary than, initially declaring the property as "readwrite".
Externally the property will be readonly. While inside the class it will have both the accessor, and the setter.
The setter will not be visible by the compiler outside of the implementation(.m) file.