Declare properties in .h interface or in an extension in .m file? - objective-c

In Objective-C, is it best practice to:
Declare objects such as buttons in the .h and then synthesize in the .m
.h
#interface SomeViewController : UIViewController
#property (strong, nonatomic) UIButton *someButton;
#end
.m
#implementation SomeViewController
#synthesize someButton = _someButton;
#end
or declare them as ivars in the .m
#interface SomeViewController ()
#property (strong, nonatomic) UIButton *someButton;
#end
I notice that in a lot of Apple code, specifically their Breadcrumbs sample code, many of their properties are declared in the interface. Is there a difference between the two? I also noticed that when properties are declared in the #interface, they are automatically synthesized with an underscore prefix, making the someButton = _someButton synthesis useless.

First, as of Xcode 4.4 there is no longer a need to #synthesize(unless you change both the setter and getter method), either when the #property is declared in the #interface or #implementation.
If the #property is only accessed from within the class then declare the #property in a class extension in the .m file. This provides encapsulation and make it easy to see that the #property is not used from another class.
If the #property is used by other classes, by design, then define it in the #interface in the .h file.

Related

Declare instance variable in Objective-C and set in Swift

I want to be able to set the value of an instance variable from my Objective-C class in my Swift class. In my Swift class, I want to be able to say something like cameraViewController.ingestViewController = self and have that set the value of ingestViewController in my Objective-C class. Here is some code to demonstrate:
PhotoViewController.swift:
class PhotoViewController : UIViewController {
let cameraViewController = // reference to the CameraViewController
cameraViewController.ingestViewController = self
}
CameraViewController.h:
#interface CameraViewController : GSKCameraViewController
#end
CameraViewController.m:
#interface CameraViewController ()
#property (nonatomic, strong) UIView *toolbar;
#property (nonatomic, strong) UIButton *cameraButton;
#property (class, nonatomic, strong) UIViewController *ingestViewController;
#end
#implementation CameraViewController
UIViewController *ingestViewController
// rest of implementation
#end
I continue to get the error Value of type 'CameraViewController?' has no member 'ingestViewController'.
#property (class, nonatomic, strong) UIViewController *ingestViewController;
This is a class property, not instance variable property.
So just remove class attribute.
You've declared the ingestViewController property as a class property, not an instance property.
Remove the class attribute of the #property.
#property (nonatomic, strong) UIViewController *ingestViewController;
Once that is fixes, you need to make the property public. Move it to the .h file:
#interface CameraViewController : GSKCameraViewController
#property (nonatomic, strong) UIViewController *ingestViewController;
#end
All of the properties in the .m are private.
Lastly, remove the unnecessary line:
UIViewController *ingestViewController
from the .m file. That is actually declaring a global variable and is not in any way associated with the property of the same name.

Why do I have to declare a property specified by a protocol in the header, and not the class extension (implementation)

So I have a protocol, which requires a property to be declared:
#protocol MyProtocol <NSObject>
#property MyView *myView;
#end
and an object who conforms to it:
#interface MyViewController : NSViewController <MyProtocol>
#end
However, if I declare the property (specified in the protocol) inside of the implementation file (the class extension):
#interface MyViewController()
#property MyView *myView;
#end
I get this error:
Illegal redeclaration of property in class extension
'MyViewController' (attribute must be 'readwrite', while its primary
must be 'readonly')
There appear to be two main SO threads that address this:
attribute must be readwrite while its primary must be read only
and
Can't declare another window
The first answer doesn't explain anything
The second answer says that you can actually circumvent this error by declaring the property inside of the header; and alas
Header
#interface MyViewController : NSViewController <MyProtocol>
#property MyView *myView;
#end
Implementation
#interface MyViewController()
#end
This builds with no errors.
I also know that when you declare a #property inside of a protocol, it doesn't automatically get synthesized.
So if I wanted to keep the #property declaration inside of the implementation, I would have to #synthesize it. And this also works.
So my question is, why does declaring the #property inside of the header vs the implementation file matter if the #property was initially declared inside of a protocol?
Without the protocol, I thought the only difference was making the #property public or private. But clearly there are other things that happen/don't happen if you declare a #property in the header vs the implementation file
Don't declare there property anywhere in your class. It's already declared in the protocol.
Don't put #property MyView *myView; in either the MyViewController.m or MyViewController.h files.
To fix the warning about "auto property synthesis", you simply add:
#synthesize myView = _myView;
to the MyViewController implementation or add explicit getter and setter methods as needed.

Objective-C equivalent of internal(set) in Swift

The internal(set) access modifier in Swift allows a property to be changed within the same module, but not from the outside. I'm curious about whether it has an Objective-C equivalent, and how I can implement it.
AFAIK, there is no equivalent in Objective-C.
But you can hide the setter outside from the module (Framework). For example:
MyObject.h: as Public header
#import <Foundation/Foundation.h>
#interface MyObject : NSObject
// `readonly` for public
#property (strong, nonatomic, readonly) NSString *myProp;
#end
MyObject-Internal.h: as Project header
#import "MyObject.h"
#interface MyObject ()
// NOT `readonly` for internal
#property (strong, nonatomic) NSString *myProp;
#end
Then, you can use MyObject-Internal.h in .m codes inside the module.

Friend classes in Objective-C

Is there any way to create something like friend classes in Objective-C?
First declare a "private property" using the standard class extension method:
// VisualNotePlayer.h
#interface VisualNotePlayer : NSObject<NotePlayer>{
#private
UIView *_currentView;
}
// VisualNotePlayer.m
#interface VisualNotePlayer()
#property (nonatomic, retain) UIView *currentView;
#end
#implementation VisualNotePlayer
#synthesize currentView=_currentView;
...
#end
Then recreate the properties in a category:
// VisualNotePlayer+Views.h
#interface VisualNotePlayer(Views)
#property (nonatomic, retain) UIView *currentView;
#end
This interface is only accessible to those who import VisualNotePlayer+Views.h
There is no such thing as a friend class in ObjC.
And to access a private variable of another class you don't even need to be declared as a friend. For example, you can use the runtime functions
id the_private_ivar;
object_getInstanceVariable(the_object, "_ivar_name", &the_private_ivar);
to get the_object->_ivar_name, bypassing compiler checks.

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.