arrow operator in objective-c - ios

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.

Related

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.

Assign value to NSUInteger in object after init

I have a subclass of NSObject. In this class I have an NSUInteger declared like this:
#interface Deck : NSObject
- (id)initDecks:(NSUInteger)decks;
- (Card *)drawCard;
#property (nonatomic, assign) NSUInteger drawnCards;
In my implementation file, I have the following code:
#import "Deck.h"
#implementation Deck
- (id)initDecks:(NSUInteger)decks {
self = [super init];
if (self) {
self.drawnCards = 1;
}
return self;
}
- (BJCard *)drawCard {
self.drawnCards++;
return nil;
}
The number assigned to the NSUInteger (drawnCards) in the init method is set correctly, however, I am not able to change it later. I get no warnings or crashes in Xcode, but the number remains unchangeable. I have tried to do self.drawnCards++ and self.drawnCards = 10 etc, but nothing works. Any idea what might be wrong with my code? I am checking the value with:
NSLog(#"Value: %tu", self.drawnCards);
I believe the problem here is the objectiveC dot syntax. Because you are doing . with an object this is the equivalent to doing [self drawnCards]++ method call. The result of the method is incremented, not the drawnCards ivar. To do what you want you'll either need to do _drawnCards++ (access the iVar directly), or you'll need to do [self setDrawnCards:self.drawnCards++].

setting up enum in a singleton within an ios app such that it can be accessed throughout the app

I would like to set up my global constant values within a Constants Singleton class in my iOS app such that any class importing the constants can use those values.
However, after playing around for a few hours with this idea, I am still unable to make it work.
In my Constants.m file
#interface Constants()
{
#private
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
}
#end
#implementation Constants
static Constants *constantSingleton = nil;
//Categories of entries
typedef enum
{
mapViewAccessoryButton = 999
} UIBUTTON_TAG;
+(id)getSingleton
{
.....
}
I have another class MapViewController where I have a reference to the Constants singleton and Im trying to access the enums like this
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
However, this is not working. Im not able to access the UIBUTTON_TAG inside the mapviewcontroller
ANybody have any suggestions?
Thanks
If you want the enum available throughout the app, put the enum definition in the .h file, not the .m file.
Update:
Objective-C doesn't support namespaces and it doesn't support class level constants or enums.
The line:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
should be:
myDetailButton.tag = mapViewAccessoryButton;
assuming you define the UIBUTTON_TAG enum in some .h file.
When you compile an Objective-C app, all values of all enum must have unique names. This is a result of Objetive-C being based on C.
Update 2:
There is one way to get what you want but not with enums. Something like this should work:
Constants.h:
#interface UIBUTTON_TAG_ENUM : NSObject
#property (nonatomic, readonly) int mapViewAccessoryButton;
// define any other "enum values" as additional properties
#end
#interface Constants : NSObject
#property (nonatomic, readonly) UIBUTTON_TAG_ENUM *UIBUTTON_TAG;
+ (id)getSingleton;
// anything else you want in Constants
#end
Constants.m
#implementation UIBUTTON_TAG_ENUM
- (int)mapViewAccessoryButton {
return 999;
}
#end
#implementation Constants {
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
UIBUTTON_TAG_ENUM *_uiButtonTag;
}
#synthesize UIBUTTON_TAG = _uiButtonTag;
- (id)init {
self = [super init];
if (self) {
_uiButtonTag = [[UIBUTTON_TAG_ENUM alloc] init];
}
return self;
}
// all of your other code for Constants
#end
Now you can do:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
I'm not sure if there is a point to this though.
One way to do this is simply stick it in your precompiled header (.pch) if you aren't going to be changing the enum a lot.

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

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.

Resources