What does "#private" mean in Objective-C? - ios

What does #private mean in Objective-C?

It's a visibility modifier—it means that instance variables declared as #private can only be accessed by instances of the same class. Private members cannot be accessed by subclasses or other classes.
For example:
#interface MyClass : NSObject
{
#private
int someVar; // Can only be accessed by instances of MyClass
#public
int aPublicVar; // Can be accessed by any object
}
#end
Also, to clarify, methods are always public in Objective-C. There are ways of "hiding" method declarations, though—see this question for more information.

As htw said, it's a visibility modifier. #private means that the ivar (instance variable) can only be accessed directly from within an instance of that same class. However, that may not mean much to you, so let me give you an example. We'll use the init methods of the classes as examples, for simplicity's sake. I'll comment inline to point out items of interest.
#interface MyFirstClass : NSObject
{
#public
int publicNumber;
#protected // Protected is the default
char protectedLetter;
#private
BOOL privateBool;
}
#end
#implementation MyFirstClass
- (id)init {
if (self = [super init]) {
publicNumber = 3;
protectedLetter = 'Q';
privateBool = NO;
}
return self;
}
#end
#interface MySecondClass : MyFirstClass // Note the inheritance
{
#private
double secondClassCitizen;
}
#end
#implementation MySecondClass
- (id)init {
if (self = [super init]) {
// We can access publicNumber because it's public;
// ANYONE can access it.
publicNumber = 5;
// We can access protectedLetter because it's protected
// and it is declared by a superclass; #protected variables
// are available to subclasses.
protectedLetter = 'z';
// We can't access privateBool because it's private;
// only methods of the class that declared privateBool
// can use it
privateBool = NO; // COMPILER ERROR HERE
// We can access secondClassCitizen directly because we
// declared it; even though it's private, we can get it.
secondClassCitizen = 5.2;
}
return self;
}
#interface SomeOtherClass : NSObject
{
MySecondClass *other;
}
#end
#implementation SomeOtherClass
- (id)init {
if (self = [super init]) {
other = [[MySecondClass alloc] init];
// Neither MyFirstClass nor MySecondClass provided any
// accessor methods, so if we're going to access any ivars
// we'll have to do it directly, like this:
other->publicNumber = 42;
// If we try to use direct access on any other ivars,
// the compiler won't let us
other->protectedLetter = 'M'; // COMPILER ERROR HERE
other->privateBool = YES; // COMPILER ERROR HERE
other->secondClassCitizen = 1.2; // COMPILER ERROR HERE
}
return self;
}
So to answer your question, #private protects ivars from access by an instance of any other class. Note that two instances of MyFirstClass could access all of each other's ivars directly; it is assumed that since the programmer has complete control over this class directly, he will use this ability wisely.

It important to understand what it means when somebody says that you can't access a #private instance variable. The real story is that the compiler will give you an error if you attempt to access these variables in your source code. In previous versions of GCC and XCode, you would just get a warning instead of an error.
Either way, at run time, all bets are off. These #private and #protected ivars can be accessed by an object of any class. These visibility modifiers just make it difficult to compile the source code into machine code that violates the intent of the visibility modifiers.
Do not rely on ivar visibility modifiers for security! They provide none at all. They are strictly for compile-time enforcement of the class-builder's wishes.

Related

Why does a constant property may get initialized twice in a Swift class?

The project I'm working on is a mix of Swift and Objective-C. Here's the snippet:
// ViewController.m
#interface ViewController ()
#property (nonatomic, strong) MyModel *model;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.model = [[MyModel alloc] initWithIntValue:10];
}
// MyModel.swift
fileprivate class SomeProperty {
init() {
print("SomeProperty init")
}
}
class MyModel: BaseModel {
private let property = SomeProperty()
}
// BaseModel.h
#interface BaseModel : NSObject
- (instancetype)initWithIntValue:(int)intValue;
- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue;
#end
// BaseModel.m
#implementation BaseModel
- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue {
if (self = [super init]) {
}
return self;
}
- (instancetype)initWithIntValue:(int)intValue {
return [self initWithIntValue:intValue doubleValue:0];
}
#end
Interestingly, I find when MyModel instance is initialized, SomeProperty init will be printed twice, which means two SomeProperty instances are created. What's worse, Debug Memory Graph shows that there is a SomeProperty object memory leak. So why is this and how can I fix it?
Rewrite BaseModel.h like this:
- (instancetype)initWithIntValue:(int)intValue;
- (instancetype)initWithIntValue:(int)intValue doubleValue:(double)doubleValue NS_DESIGNATED_INITIALIZER;
Note the NS_DESIGNATED_INITIALIZER marker at the end of the second initializer. (You may have to scroll my code in order to see it.)
This marker, aside from what it does within Objective-C (in its role as a macro), tells Swift that both initializers are not designated initializers; rather, Swift concludes, the first one is a convenience initializer. And that is correct; it calls another initializer, namely — in this case — the designated initializer.
Without that NS_DESIGNATED_INITIALIZER markup, Swift interprets the situation incorrectly because of the (already rather complicated) relationship between Swift initializers and Objective-C initializers. It thinks both initializers are designated initializers and you get this curious double initialization from Swift.

Share singleton from Objective C to Swift

I am trying to access an Objective C singleton from Swift, however I only seem to get the initial value created in the init function of the singleton. The flightControllerState object exposed is updated in a delegate function and I can see that the value is properly updated on the Objective C side.
I have followed a few different posts here on SO and also this article on how to call the shared object from Swift. (I should also mention this is running inside a react native project if that may have any impact?)
EDIT updated swift code - I added the wrong line to the init method to grab shared instance - issue is still the same
Objective-C Singleton
#import DJISDK;
#interface RCTBridgeDJIFlightController : RCTEventEmitter<DJIFlightControllerDelegate> {
DJIFlightControllerState *flightControllerState;
}
#property(nonatomic, readonly) DJIFlightControllerState *flightControllerState;
+ (id)sharedFlightController;
#end
#implementation RCTBridgeDJIFlightController
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
+ (id)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (id)init {
// I also tried this to make sure the shared instance was returned but no luck
//if (sharedFlightControllerInstance != nil) {
// return sharedFlightControllerInstance;
//}
if (self = [super init]) {
flightControllerState = nil;
}
return self;
}
-(void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
flightControllerState = state;
}
#end
Swift class calling singleton and accessing values
class VirtualStickController {
var flightControllerSharedInstance: RCTBridgeDJIFlightController
override init() {
self.flightControllerSharedInstance = RCTBridgeDJIFlightController.sharedFlightController()
}
func getFlightControllerState() {
if let state = flightControllerSharedInstance.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)") // always null
} else {
print ("NULL")
}
}
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
There is no need to use #synthesize for properties in (modern) Objective-C except in special circumstance.
The property flightControllerState is an instance property and will be synthesised (with or without the #synthesize) using a hidden instance variable for its storage.
The variable flightControllerState is a global variable, it happens to have the same name as the property but has no connection whatsoever with it.
At a guess you are changing the global variable in Objective-C and expecting to see the result in Swift via the property, you won't.
Remove the global variable and then check the rest of your code.
Apart from that your code produces a valid shared instance which can be shared between Objective-C and Swift and changes made in one language will be visible in the other.
HTH
Regarding the titular question about how to access an Objective C singleton from Swift, I would recommend an alternative. Modern convention is to declare your sharedFlightController as a class property and declare init as NS_UNAVAILABLE:
#interface RCTBridgeDJIFlightController : NSObject
...
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
The implementation would implement a getter for this class property:
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
...
#end
Now, your Swift code can reference RCTBridgeDJIFlightController.shared, as is the convention with Swift singletons.
Regarding why you are receiving a nil for the status, there are one of two possible problems:
You Objective-C code has confusing combination of explicitly defined ivars, manual synthesis, and global variables. (See below.)
I would also suggest that you confirm whether flightController:didUpdateState: is ever getting called at all. (I don't see you ever setting the delegate of the flight controller.) Add a breakpoint or NSLog statement in that method and confirm.
On the first issue, above, I would suggest:
You should not use those commented lines in your init method. If you want to make sure that your singleton object is used, then declare init as NS_UNAVAILABLE.
Given that all your init method is doing is updating flightControllerState to nil, you can remove it entirely. In ARC, properties are initialized to nil for you.
You should not declare explicit ivar in your #interface. Let the compiler synthesize this automatically for you.
You should not #synthesize the ivar in your #implementation. The compiler will now automatically synthesize for you (and will use an appropriate name for the ivar, adding an underscore to the property name.
You should not declare that global in your #implementation.
If you want to use this sharedFlightController from Swift, you should define it to be a class property, not a class method. I know that that article suggested using a class method, but that really is not best practice.
Thus:
// RCTBridgeDJIFlightController.h
#import <Foundation/Foundation.h>
// dji imports here
NS_ASSUME_NONNULL_BEGIN
#interface RCTBridgeDJIFlightController : NSObject
#property (nonatomic, readonly, nullable) DJIFlightControllerState *flightControllerState;
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
NS_ASSUME_NONNULL_END
And
// RCTBridgeDJIFlightController.m
#import "RCTBridgeDJIFlightController.h"
#interface RCTBridgeDJIFlightController ()
#property (nonatomic, nullable) DJIFlightControllerState *flightControllerState;
#end
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
NSLog(#"State updated");
self.flightControllerState = state;
}
#end
The end result is that you can now use it like so:
class VirtualStickController {
func getFlightControllerState() {
if let state = RCTBridgeDJIFlightController.shared.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)")
} else {
print("NULL")
}
}
}
Note, because the sharedFlightController is now a class property, Swift/ObjC interoperability is smart enough so the Swift code can just reference shared, as shown above.

Initialize generic parameters in iOS

I want to create object of generic property with default initializer. I know this is possible in swift by adding an init method in MyProtocol. Is there any way I can achieve this functionality in objective c
Interface
#interface CustomClass<__covariant T:id<MyProtocol>> : NSObject
#property(nonatomic) T result;
-(void) update;
#end
Implementation
#import "CustomClass.h"
#implementation CustomClass
-(void) update
{
//self.result = initialize result here
[self.result myMethod];
}
#end
Objective-C is late binding and dynamic typing. Neither you need templates (ooops, they call it generics) nor the compiler bind to it. Simply use id.
#interface CustomClass : NSObject
#property(nonatomic) id result;
-(void) update;
#end
-(void) update
{
//self.result = initialize result here
[self.result myMethod];
}
If you want to have a constraint on the type for whatever reason, simply use the protocol:
#protocol CustomProtocol
- (void)myMethod;
#end
#interface CustomClass : NSObject
#property(nonatomic) id<MyProtocol> result;
-(void) update;
#end
-(void) update
{
//self.result = initialize result here
[self.result myMethod];
}
After discussion the subject in the comments:
If you want to instantiate a generic type, you do not do this at compile time, but simply pass the type to the initializer at runtime.
Instead of …:
var concrete = CustomClass<Concrete>()
… you write:
CustomClass *concrete = [[CustomClass alloc] initForType:[Concrete class]];
Personally I prefer new allocators, but this is another discussion. You can pass the types name as string, too. Personally I do not like that, too. :-)
The initializer looks like this:
- (instancetype)initForType:(Class)type
{
if( (self = [super init] )
{
self.result = [type new];
}
return self;
}
Lightweight generics in Objective-C are a purely compile-time construct to aid in type checking. They are erased after type-checking and do not exist in the compiled code. There is no T at runtime. You cannot write any code that needs to know what T is.

arrow operator in objective-c

I have a question, here's the code:
#interface MyFoo : NSObject {
NSString *nameStr;
}
#end
#implementation MyFoo
- (id)init {
self = [super init];
if (self) {
self->nameStr = [#"some value of the string that is set right into the private ivar" copy];
}
return self;
}
#end
The question is: ignoring all the C++ rules, ignoring memory dump vulnerability, why exactly I shouldn't use such arrow operator syntax?
Is there somewhere in Apple documentation a rule which says that it's incorrect because in future class may be represented differently than a pointer to a struct in runtime etc. ?
Thanks in advance!
The use of self->someIvar is identical to someIvar. It's not wrong but it's not needed either.
The only time I use the arrow notation is in an implementation of copyWithZone: so I can copy each of the ivars that don't have properties.
SomeClass *someCopy = ...
someCopy->ivar1 = ivar1; // = self->ivar1
someCopy->ivar2 = ivar2; // = self->ivar2
Where are you seeing anything that says you shouldn't use such arrow operator syntax?
Using arrow notation on just the ivar name to access a property will not guarantee they will be retain, assign or etc ...
Because you are directing accessing an ivar and not calling and setter ou getter method used in properties.
Example:
#interface MyFoo : NSObject {
}
#property(nonatomic,retain) NSString *nameStr;
#end
#implementation MyFoo
- (id)initWithString:(NSString *)name {
self = [super init];
if (self) {
self->nameStr = name; // will not be retained
}
return self;
}
#end
For ivar variables as already be answer there`s nothing wrong.
Using the arrow notation isn't incorrect, and there is a difference between using the arrow and the dot notation.If you use the arrow operator you access to the instance variable, if you use the dot operator you access to the property.
It doesn't work like C structs where you use the arrow notation to access to a member of a struct pointed, and dot notation to access the struct members.
So I would make a significative example:
#property (nonatomic, strong) NSString *text;
In .m file:
- (void)setText:(NSString *)string {
NSLog(#"Accessing property");
self->text = string; // let's suppose that text is not synthesized
}
If you use the dot notation , then you access to the property and it would print "Accessing property".But this hasn't to do with C structs syntax.

How to call method from one class in another (iOS)

This a very basic question but I've searched all over and been unable to find an answer that explains well enough for me to get my head around it.
What I want to do is create a method in one class of my iOS app and then call that method from other classes in my app. Could someone explain exactly what I need to do to achieve this? Any help would be greatly appreciated as all my attempts so far have failed!
Thanks.
Objective-C:
You have to import the header of the class that contains the method you want to use (ClassYouWantToUse.h) into the class you want to use it at (TargetClass).
Inside the TargetClass.h or TargetClass.m (depending on the scope you want to give it):
#import "ClassYouWantToUse.h"
Then create an instance of the class you want to use inside the target class either as a property like this:
#property (nonatomic,strong) ClassYouWantToUse *classObject;
Or as an instance variable like this:
ClassYouWantToUse *classObject;
Make sure you initialize it! (usually inside ViewDidLoad):
classObject = [[ClassYouWantToUse alloc] init];
Now you can call any public methods from that class like this:
[classObject theClassMethodWithParam:param1 andSecondParam:param2];
Note: The ClassYouWantToUse class must have the methods that you want to make accessible to others by declaring them in the header file:
- (void)theClassMethodWithParam:(UIImage*)someImage andSecondParam:(NSString*)someText;
Otherwise you won't be able to see these methods.
Swift:
Theres really nothing special about it in swift, just adding this as a reference.
In swift you simply create an instance of the class you want to use:
let classObject = ClassYouWantToUse()
And use it directly:
classObject.theClassMethodWithParam(param1, andSecondParam:param2)
You have two basic options. You can either create or pass-in an instance of the first class to the second class, or you can add a static method to the first class and call it directly using the class object.
For instance, say you have:
#interface ClassA : NSObject {
}
//instance methods
- (int) addNumber:(int)num1 withNumber:(int)num2;
//static/class methods
+ (int) add:(int)num1 with:(int)num2;
#end
#implementation ClassA
- (int) addNumber:(int)num1 withNumber:(int)num2 {
return num1 + num2;
}
+ (int) add:(int)num1 with:(int)num2 {
return num1 + num2;
}
#end
Then you can do:
#import "ClassA.h"
#interface ClassB : NSObject {
ClassA* adder;
}
//constructors
- (id) init; //creates a new instance of ClassA to use
- (id) initWithAdder:(ClassA*)theAdder; //uses the provided instance of ClassA
//instance methods
- (int) add2To:(int)num;
//static/class methods
+ (int) add3To:(int)num;
#end
#implementation ClassB
- (id) init {
if (self = [super init]) {
adder = [[ClassA alloc] init];
}
return self;
}
- (id) initWithAdder:(ClassA*)theAdder {
if (self = [super init]) {
adder = theAdder;
}
return self;
}
- (int) add2To:(int)num {
return [adder addNumber:2 withNumber:num];
}
+ (int) add3To:(int)num {
return [ClassA add:3 with:num];
}
#end
Note that in most cases, you would use instance methods rather than static methods.
You have to use the concept of delegation.
https://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html

Resources