I have a simple ViewController derived from UIViewController to which I have added an enum property swipeDirection. In the code I normally refer to it as self.swipeDirection but in one instance I notice that I have mistakenly typed self.SwipeDirection.
If I jump to Definition I get the right variable and the code compiles and runs correctly so I am sure that the correct variable is being used.
.h file
enum EScrollDirection
{
E_SCROLL_DIRECTION_NONE = 0,
E_SCROLL_DIRECTION_LEFT,
E_SCROLL_DIRECTION_RIGHT,
E_SCROLL_DIRECTION_UP,
E_SCROLL_DIRECTION_DOWN
};
typedef enum EScrollDirection EScrollDirection;
#interface ProcessingViewController : UIViewController <UIScrollViewDelegate>
#property(nonatomic, assign)EScrollDirection swipeDirection;
#end
.m file
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGPoint offset = self.graphScrollView.contentOffset;
self.SwipeDirection = [self getScrollDirection:self.previousTouchPoint endPoint:self.graphScrollView.contentOffset];
// ...
}
In theory all properties are compiled into a setter method call with the following rule by default - setter name for property is setProperty: (note 1st letter of property name becoming uppercase). So both the following lines of code
self.SwipeDirection = ...
self.swipeDirection = ...
are compiled to the existing setter method
[self.setSwipeDirection:...]
and so equivalent from compiler point of view.
Note - the same does not work for (default) getter method and the following line will not compile:
NSLog(#"%d", self.SwipeDirection);
Related
Can someone please explain to me (in simple terms) why an instancetype is used in Objective-C?
- (instancetype) init {
self = [super init];
if (self) {
// Custom initialization
}
return self;
}
It's to increase type safety.
Back in the old days, initialisers just returned an object of type id (any object).
With normal initialisers (those that begin with "init", "alloc" or "new"), this wasn't usually a problem. The compiler would automatically infer the type that it returned and therefore restrict any method calls on the object to the instance methods of that class.
However, this was a problem with static convenience initialisers or "factory methods" that didn't necessarily follow the same naming convention - therefore it was unable to apply the same type safety.
This means that with a class like this:
#interface Foo : NSObject
+(id) aConvenienceInit;
#end
The compiler would accept code like this:
NSArray* subviews = [Foo aConvenienceInit].subviews;
Why? Because the returned object could be any object, so if you try and access a UIView property - there's no type safety to stop you.
However, now with instancetype, the result you get back is of type of your given instance. Now with this code:
#interface Foo : NSObject
+(instancetype) aConvenienceInit;
#end
...
NSArray* subviews = [Foo aConvenienceInit].subviews;
You'll get a compiler warning saying that the property subviews is not a member of Foo*:
Although it's worth noting that the compiler will automatically convert the return type from id to instancetype if your method begins with "alloc", "init" or "new" - but nonetheless using instancetype wherever you can is a good habit to get into.
See the Apple docs on instancetype for more info.
Imagine two classes:
#interface A : NSObject
- (instancetype)init;
#end
#interface B : A
#end
The init method from A is inherited to B. However, in both classes the method has a different return type. In A the return type is A and in B the return type is B.
There is no other way to declare the return type for initializers correctly. Note that most programming languages with classes don't even have return types for constructors, therefore they completely avoid the issue.
This is the reason why Obj-C needs instancetype but of course it can be used outside initializers, too.
It is important to use instancetype instead of id in Objective-C if you are also using this code in Swift. Consider the following class declaration:
#interface MyObject : NSObject
+ (id)createMyObject;
- (void)f;
#end
If you want to create a MyObject instance in Swift 5.3 with createMyObject and then call f for this object, you will have to do the following:
let a = MyObject.createMyObject()
(a as? MyObject)?.f()
Now replace id with instancetype in MyObject to have the following Swift code:
let a = MyObject.create()
a?.f()
As you can see now, you can use MyObject.create() instead of MyObject.createMyObject(). And you don't need to use (a as? MyObject) since a is defined as MyObject? and not as Any.
What's the difference between declaring a #private ivar in the header file and declaring the same ivar in the class extension without #private? As far as I understand it's the same thing.
Also, can you declare a private property in the header?
The concept is to declare in the header file only those things (methods, properties, etc) which are public. Declare all private items in the implementation file's class extension.
This provides the class users only information that is available for their use and hides all else. It also make it easier for a user of the class quickly see the functionality available to him. Writing code is all about readability and understandability to the developer.
This way a developer is free to change anything that is not exposed in the header files without making any externally visible changes.
In recent versions of Objective this is finally fully releasable via class extensions.
What's the difference between declaring a #private ivar in the header file and declaring the same ivar in the class extension without #private?
There are a few differences. In short, variables declared in the header file are visible to subclasses and class categories. Variables declared in the implementation are not.
1) Instance variables declared in a class's main #interface block are available to external class categories or extensions, even if those variables are declared #private. E.g.:
// YourClass.h
#interface YourClass : NSObject {
#private
int _yourPrivateIVar;
}
#end
// MyExtension.m
#implementation YourClass(MyExtension)
- (void)reset { _yourPrivateIVar = 0; } // This is allowed.
#end
Instance variables declared in the implementation are not available to external class categories.
2) A base class and its subclass cannot both declare the same ivar in their #interface, even if both ivars are #private. E.g., this is not allowed:
#interface Base : NSObject
{
#private
int _foo;
}
#end
#interface Subclass : Base
{
#private
int _foo; // Error: Duplicate member _foo
}
#end
If both ivars are declared in a class extension or implementation block then not only does it compile but it works as expected: both classes have their own separate _foo ivars that do not conflict with one another. On other words, both variables are truly private and separate:
#implementation Base {
int _foo;
}
#end
#implementation Subclass {
int _foo;
}
- (void)reset { _foo = 123; } // Does not affect base class's _foo
#end
Note: If the base class and subclass declare a "private" property or method with the same name it will compile without warning or error, but it will fail spectacularly at runtime as both classes unknowingly interfere with each other's private data.
I am at my wits end with a property declaration in a iOS class. In my .h file I have the following declaration :
#property (strong, nonatomic)NSString *sessionID;
In my .m file I have this code :
- (void)setSessionID:(NSString *)aSessionID
{
_sessionID = aSessionID;
// Custom code to set this in a global context
}
This is all fine and compiles with no issues. Now I need to have the sessionID return a default value if nothing is set, however the moment I add this line :
- (NSString *)sessionID
{
return _sessionID ? _sessionID : #"defaultSession";
}
then the first line in the setSessionID:
_sessionID = aSessionID;
causes an error with "Use of undeclared function _sessionID. Did you mean aSessionID", I am at my wits end to figure out what is causing it.. I have so many classes with variables and have never seen this before... what is causing this? I restarted Xcode, cleaned out the project and no luck.. If I remove the - (NSString *)sessionID method, then it stops complaining.. but the moment I add the method declaration the Xcode marks it as an error.
Anypointers accepted! :)
Edit: I also noticed, that in this class if I add any property accessor method it complains about the ivar.. e.g. I have another property declared
#property (strong, nonatomic) NSString *userEmail
The moment I add -(NSString *)userEmail, the ivar _userEmail usage above it all becomes undeclared.. :(
If you override both the setter and getter of a property, the compiler will not automatically synthesize the backing ivar for you. You need to do a manual synthesis,
#synthesize sessionID = _sessionID;
I would like to know what's the difference between declaring my instance variables like this:
// inside the implementation file (.m)
#interface MyCustomObject()
{
id _myIvar;
}
#end
#implementation MyCustomObject
...
#end
And like this:
// inside the implementation file (.m)
#implementation MyCustomObject{
id _myIvar;
}
...
#end
From the point of view of the USE, there is not difference.
From the point of view of the declaration, the first one is a Category:
#interface MyCustomObject()
{
}
so if you have a variable with the same name in the header file, your implementation file will see this one, but other classes whose import that header file, will see the other one.
This mechanism can be really useful to assign different attributes or properties to the same var, but differencing the exposed var, from the private internal var.
This question already has answers here:
Why is there another #interface inside the.m file? [duplicate]
(6 answers)
Closed 9 years ago.
As we know, normally we used to declare our class instance variables, properties, method declarations in class header file (.h).
But we can do the same things, in .m file, using blank category.
So my question is: what should be declared in .h file and what should be declared in .m file - and why?
Regards,
Mrunal
New Edit:
Hi all,
If you refer to newly added Apple examples over developer.apple.com - they are now declaring their IBOutlets and IBActions in .m file itself and that too with property declaration. But we can achieve the same thing by declaring those references in .h file in class private member section.
Then why are they declaring those in .m file and as properties, any idea?
-Mrunal
But we can do the same things, in .m file, using blank category.
A class continuation.
Normally, you choose to declare something in the header if it is intended to be public -- used by any client. Everything else (your internals) should typically go in the class continuation.
I favor encapsulation -- Here's my approach:
variables
Belongs in the class continuation or #implementation. Exceptions are very, very rare.
properties
Typically belongs in the class continuation in practice. If you want to give subclasses the ability to override these or to make these part of the public interface, then you could declare them in the class declaration (the header file).
method declarations
More in the class continuation than in the class declaration. Again, if it is meant to be used by any client it would belong in the class declaration. Often, you won't even need a declaration in the class continuation (or class declaration) -- the definition alone is adequate if it is private.
Basically, in the header file (.h) you declare your public API, while in the implementation file (.m) you declare your private API.
Visibility in Objective-C
You can also find the answer here
It's mostly up to you.
The .h file is like the description of your class.
It's smart to only put in the .h file what's really important to be visible from the outside of the class, especially if you're working with other developers.
It will help them to understand more easily what methods/properties/variables they can use, rather than having a whole list of things they don't.
Usually you want to use blank category in .m file for declaration of private properties.
// APXCustomButton.m file
#interface APXCustomButton ()
#property (nonatomic, strong) UIColor *stateBackgroundColor;
#end
// Use the property in implementation (the same .m file)
#implementation APXCustomButton
- (void)setStyle:(APXButtonStyle)aStyle
{
UIColor *theStyleColor = ...;
self.stateBackgroundColor = theStyleColor;
}
#end
If you try to access property declared in black category outside .m file, you will receive undeclared property compiler error:
- (void)createButton
{
APXCustomButton *theCustomButton = [[APXCustomButton alloc] init];
theCustomButton.stateBackgroundColor = [UIColor greenColor]; // undeclared property error
}
In most cases, if you want add new method/properties to an existing class without subclassing, then you want declare category in .h file and implementation of declared methods in .m file
// APXSafeArray.h file
#interface NSArray (APXSafeArray)
- (id)com_APX_objectAtIndex:(NSInteger)anIndex;
#end
// APXSafeArray.m file
#implementation NSArray
- (id)com_APX_objectAtIndex:(NSInteger)anIndex
{
id theResultObject = nil;
if ((anIndex >= 0) && (anIndex < [self count]))
{
theResultObject = [self objectAtIndex:anIndex];
}
return theResultObject;
}
#end
Now you can use "com_APX_objectAtIndex:" method wherever "APXSafeArray.h" is imported.
#import "APXSafeArray.h"
...
#property (nonatomic, strong) APXSafeArray *entities;
- (void)didRequestEntityAtIndex:(NSInteger)anIndex
{
APXEntity *theREquestedEntity = [self.entities com_APX_objectAtIndex:anIndex];
...
}