Declaring private variables in header file vs declaring variables in class extension - ios

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.

Related

Why is instancetype used?

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.

How to access protected variables in Objective-C

I want to have a variable that will only be visible to its subclasses which is exactly similar to what protected variables are in Java.
I tried like this on the parent's implementation file
#interface ParentClass (){
NSArray *_protectedVar
}
but unfortunately the protectedVar is not visible as soon as I call super.protectedVar
Correct my if I am wrong but I don't wanna use #properties to that variable since it will make that variable public.
And this is my subclass's header file looks like #interface SubClass : ParentClass
The code you posted declares an instance variable in an Objective-C class extension, and therefore the variable's default visibility is private. You can use a visibility modifier to change the ivar's visibility, as shown below:
#interface ParentClass ()
{
#protected
NSArray *_protectedVar
}
If you have this
ParentClass.h
#interface ParentClass : NSObject{
NSArray *_protectedVar
}
Then just like how you access normal ivar, use _protectedVar directly.
But I suggest you use property with private header
Parent.h
#interface Parent : NSObject
#property id publicProperty;
#end
Parent_Protected.h
#interface Parent (Protected)
#property id protectedProperty
#end
So normal class only #import "Parent.h", they can't see protected property.
But subclass can #import "Parent_Protected.h" and use protected property with self.protectedProperty

iOS instance variable declaration

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.

ObjectiveC: Need suggestion for my way of having protected method? [duplicate]

This question already has answers here:
Protected methods in Objective-C
(9 answers)
Closed 9 years ago.
Simply put, I need a way to have some private methods in a class that are only exposed for its subclasses, and it is difficult (maybe impossible) to do this in Objective-C.
What I did so far:
// MyClass.h
#protocol MyClassProtectedMethodsProtocol
- (void)__protectedMethod;
#end
#interface MyClass : NSObject
- (void)publicMethod;
- (id<MyClassProtectedMethodsProtocol>)protectedInstanceForSubclass:(id)subclass;
#end
Then:
// MyClass.m
#import "MyClass.h"
#interface MyClass() <MyClassProtectedMethodsProtocol>
#end
#implementation MyClass
- (void)publicMethod
{
// something
}
- (id<MyClassProtectedMethodsProtocol>)protectedInstanceForSubclass:(id)subclass
{
if ([subclass isKindOf:MyClass.class] && ![NSStringFromClass(subclass.class) isEqualToString:NSStringFromClass(MyClass.class)])
{
// the subclass instance is a kind of MyClass
// but it has different class name, thus we know it is a subclass of MyClass
return self;
}
return nil;
}
- (void)__protectedMethod
// something protected
{
}
#end
Then the subclass of MyClass can just:
id<MyClassProtectedMethodsProtocol> protectedMethodInstance = [self protectedMethodForSubclass:self];
if (protectedMethodInstance != nil)
{
[protectedMethodInstance protectedMethod];
}
This way does not break OO (compared to calling the private method and ignoring the compiler warning, or even guessing the private method name as only .h is known), but a protocol is needed for the available protected methods and once this is exposed, in a big project that we only deliver interface and static library to client, client can actually know the private methods and try to call them regardless of warning. And the bigest problem is from outside of the subclass, user can as well call this method to get the protectedInstance. Can anyone advice?
Thanks
Check this: Protected methods in Objective-C
Simply put, there is no way to prevent a method from being called in Objective-C, since ultimately, the client can still call performSelector on any object.
A standard way to handle this scenario is to include the internal methods in a separate header, like MySuperClass_Internal.h. Use a class extension: #interface MySuperClass (Internal). Do not install MySuperClass_Internal.h at /usr/local/include or in the framework, or however you're delivering the library to your clients.

When to declare something in category .m file or in header .h file? [duplicate]

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];
...
}

Resources