I have read in iOS Programming Fundamentals by Matt Neuberg that instance variables are protected, meaning that other classes, except for subclasses of this one, can't see them.
I have a parent class A where I define an ivar list.
(A.m)
#interface A ()
#end
#implementation A
{
NSArray *list;
}
#end
Class B extends A (B.h)
#import "A.h"
#interface B:A
#end
(B.m)
#interface B ()
#end
#implementation B
list =
...
#end
I want to use ivar list in child class B but the compiler doesn't see that the was reference declared in the parent class. I have tried explicitly using #protected but that doesn't work. I don't want to expose ivar list on the public interface. It's an internal structure that is a common element of all subclasses. How can I do this?
Instance variables that are declared outside a class's public interface (in other words, the #interface section) are private by default. You can add a visibility modifier to the declaration to change the visibility of one or more ivars like so:
#implementation A
{
NSNumber *_ivarWithDefaultVisibility;
#protected
NSArray *_list;
NSString *_anotherIvarWithProtectedVisibility;
}
(Note that according to Apple's Cocoa coding guidelines, ivar names should be prefixed with an underscore.)
This is not the usual way to implement protected properties in ObjC. (#protected and #private are very seldom used in Cocoa.) First, use a property, not an ivar. It will make it much cleaner. Declare it this way:
A.h
#interface A : NSObject
// Public interface goes here
#end
A.m
// Declare the property in a class extension inside the implementation file.
// This is the idiomatic way to create a "private" property.
#interface A ()
#property (nonatomic, readwrite, strong) NSArray *list;
#end
A+protected.h
// Enumerated any methods that should be accessible to subclasses here.
// Properties are just a special way of defining methods.
// The word "Protected" is just a category name.
#interface A (Protected)
#property (nonatomic, readwrite, strong) NSArray *list;
#end
B.h
#import "A.h"
#interface B : A
...
#end
B.m
#import "B.h"
#import "A+protected.h"
// ... now you can use self.list ...
This approach allows you to create any kind of "protected" method, not just properties, and is also the technique used to create "friend" classes.
Related
Let's say I have a class:
#interface MyClass
#end
#implementation MyClass {
myType *_myIvar;
}
#end
And I'd like to expose it for testing. I see two ways to do this:
expose the ivar as a property:
#interface MyClass
// Pragma mark - Expose for testing
#property myIvar;
#end
#implementation MyClass
#end
Use key value coding:
-(void)myTest {
myType *myIvar = [myClass valueForKey:#"_myIvar"];
}
Which method is preferred?
First - you don't test private methods or state of a class for unit-testing, as per TDD best practices.
Having that said, however, sometimes it's the only way to observer possible side-effects. I personally always wrap any ivar of a class with a property. For data which is not supposed to appear in the public interface I put it in the extension inside of the implementation file:
// Implementation file
#interface TDWClass ()
#property (strong, nullable) NSString *tdw_p_message;
#end
P.S. This also helps to maintain certain semantic for the given property (you immediately can see not just storage modifier, but other attributes the property is supposed to follow: like, being read-only, nullability, etc..)
When it comes to testing such a property, this approach helps to conveniently read any "private" property (or accessing a private method) by re-declaring it in a category:
// XCTest file
#interface TDWClass (XCTest)
#property (strong, nullable) NSString *tdw_p_message;
#end
I'm new to objective-C, so apologies if this is repeated somewhere. I have a category(?) that is something like:
inside SomeClass.h:
#interface SomeClass (SomeCategory) <SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#property (nonatomic, retain) id someProperty; // <-- i want to move this to "private"
#end
and now in my SomeClass.m, all i have is:
#implementation SomeClass (SomeCategory)
// dynamic setters/getters here for someProperty.
#end
I think the someProperty is public. how do i make this "private"? (in other words, how do i syntactically put this in the .m file? i tried to use
#interface SomeClass (SomeCategory) {
#property (nonatomic, retain) somePrivateProperty;
}
#end
but it just complains that i have duplicate definition of the category. how do i do this correctly?
In your .h file, you should not give the category. Just use:
#interface SomeClass : SomeBaseClass < SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#end
In your .m file, define your private property inside a class extension:
#interface SomeClass ()
#property (nonatomic, retain) id somePrivateProperty;
#end
A class extension is not a like category in that it allows you to extend an interface as well as add new storage to your class.
In a class category, you can define new properties, but no storage will be allocated for it, so you have to do it by hand:
#interface SomeClass (SomeBaseCategory)
#property (nonatomic, retain) id somePrivateProperty;
#end
#implementation SomeClass {
id _somePrivateProperty;
}
- (void)setSomePrivateProperty:(id)property {
_somePrivateProperty = property;
}
- (id)somePrivateProperty {
return _somePrivateProperty;
}
#end
Otherwise your app will crash.
In any case, keep in mind that given the dynamic nature of Objective-C, your property will never be fully private, since you can always send a message to an Objective-C object through objc_msgsend and thus set or read the property value.
EDIT:
If you do not have the source code for a class implementation, you cannot define a class extension (as per source linked above).
In this case, you could use object association to define properties.
Just add the category definition in the .m file OUTSIDE the implementation block
Like so:
#interface MyClass (MyCategory)
#property (assign) BOOL myPrivateProperty;
#end
#implementation MyClass
...
#end
Categories are best used for adding capability to code you do not own and cannot change. Adding properties via categories is not impossible, but is much more difficult.
Class Extensions are best used for keeping properties your object needs, but are not intended to be public.
If you do truly need to add properties to this object, the way to do it is with the Objective-C runtime's associated objects
There's an excellent writeup of when/how to use them here
Normally we use
#interface interface_name : parent_class <delegates>
{
......
}
#end
method in .h file and in .m file we synthesis the properties of variables declared in .h file.
But in some code, this #interface.....#end method is kept in the .m file also. What does it mean? What is the difference between them?
Also give some words about getters and setters for the interface file that is defined in .m file...
It's common to put an additional #interface that defines a category containing private methods:
Person.h:
#interface Person
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)person;
#end
Person.m:
#interface Person () //Not specifying a name for the category makes compiler checks that these methods are implemented.
-(void)startThinkOfWhatToHaveForDinner;
#end
#implementation Person
#synthesize name = _name;
-(NSString*)makeSmallTalkWith:(Person*)person
{
[self startThinkOfWhatToHaveForDinner];
return #"How's your day?";
}
-(void)startThinkOfWhatToHaveForDinner
{
}
#end
The 'private category' (the proper name for a nameless category is not 'private category', it's 'class extension') .m prevents the compiler from warning that the methods are defined. However, because the #interface in the .m file is a category you can't define ivars in it.
Update 6th Aug '12: Objective-C has evolved since this answer was written:
ivars can be declared in a class extension (and always could be - the answer was incorrect)
#synthesize is not required
ivars can now be declared in braces at the top of #implementation:
that is,
#implementation {
id _ivarInImplmentation;
}
//methods
#end
The concept is that you can make your project much cleaner if you
limit the .h to the public interfaces of your class, and then put
private implementation details in this class extension.
when you declare variable methods or properties in ABC.h file , It
means these variables properties and methods can be access outside the
class
#interface Jain:NSObject
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)jain;
#end
#Interface allows you to declare private ivars, properties and
methods. So anything you declare here cannot be accessed from outside
this class. In general, you want to declare all ivars, properties and
methods by default as private
Simply say when you declare variable methods or properties in ABC.m
file , It means these variables properties and methods can not be
access outside the class
#interface Jain()
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)jain;
#end
you can even create other classes in .m file,
for instance other small classes which inherit from the class declared in .h file but having some slight different behaviour.
You could use this in a factory pattern
I'm going through a tutorial on core-data in Objective-C and can't understand the following syntax:
#interface RootViewController : UITableViewController <CLLocationManagerDelegate> {
NSMutableArray *eventsArray;
NSManagedObjectContext *managedObjectContext;
CLLocationManager *locationManager;
UIBarButtonItem *addButton;
}
#property (nonatomic, retain) NSMutableArray *eventsArray;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) UIBarButtonItem *addButton;
#end
We have four properties here that are declared in the implementation file, which to my understanding means they are private. What exactly is happening within the curly brackets? Why are these variables put there? And also, is it a class extension? I see () are missing here so probably it is not. What is this kind of syntax called then?
Its not a category.Its just a class named RootViewController which extends UITableViewController and implements protocol CLLocationManagerDelegate.
Coming to your braces ->
generally if you don't create iVars in curly braces, by default they are created with underscore as prefix to them. This is done by compiler.
But here, you explicitly said, that the ivar should be without underscore(_).
Any you should synthesize them as below or else it will give a warning.
#synthesize eventsArray= eventsArray;
It's just a regular definition of a RootViewController class, the #interface doesn't necessarily have to be in a header file, private classes (that shouldn't/don't need to be accessible elsewhere) can also be defined directly in the .m file.
The definitions in the curly braces are just regular instance variables of the RootViewController class.
What you have is called the class interface. It is just the .h file of your program files. .If you want a class category, just do
#interface RootViewController (CategoryName)
and for an extension, inside the .m type
#interface RootViewController ()
#end
#implementation
Variables between curly braces:
{
NSMutableArray *eventsArray;
NSManagedObjectContext *managedObjectContext;
CLLocationManager *locationManager;
UIBarButtonItem *addButton;
}
are just usual variables.
For variable, defined with #property base word:
#property (nonatomic, retain) NSMutableArray *eventsArray;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) UIBarButtonItem *addButton;
created accessor and mutator methods. And also you can define options for those variables in bracers. Plus you can get local synonym for them, using base word #synthesize in .m file, like
#synthesize addButton = myLovelyButton;
then you can use myLovelyButton in .m file instead addButton
Both of these definition do not belong to the category.
For define category just type code like:
#interface <#className#> (<#categoryName#>)
{
//optional variables here
int i;
NSString *s;
}
//optional variables here
#property NSString *str;
//optional methods here
-(void)doSomething;
#end
Then you can implement your methods and use these variables:
#implementation <#className#> (<#categoryName#>)
-(void)doSomething
{
int i = 0;
str = #"blah";
s = #"wow";
NSLog(#"%i - %# - %#",i,str,s);
}
#end
Use that for add your methods to existing classes.
Variables inside brackets directly after the #interface or #implementation are instance variables. These are variables associated with each instance of your class, and thus accessible anywhere in your instance methods.
If you don't put the brackets, you declare global variables. Any variable declared outside of any bracket block will be a global variable, wether these variables are before or after the #implementation directive. And global variables are evil and needs to be avoided at all costs (you can declare global constants, but avoid global variables), especially because they are not thread-safe (and may thus generate bugs that are a mess to debug).
#interface YourClass : ParentClass
{
// Declare instance variables here
int ivar1;
}
// declare instance and class methods here, as well as properties (which are nothing more than getter/setter instance methods)
-(void)printIVar;
#end
// .m
int someGlobalVariable; // Global variable (bad idea!!)
#implementation YourClass
int someOtherGlobalVariable; // Still a bad idea
-(void)printIVar
{
NSLog(#"ivar = %d", ivar1); // you can access ivar1 because it is an instance variable
// Each instance of YourClass (created using [[YourClass alloc] init] will have its own value for ivar1
}
Only modern compilers let you declare instance variables (still in brackets) also inside either your class extension (#interface YourClass () in your .m implementation file) or in your #implementation, in addition to the possibility to declare them after the #interface in your .h. The benefits being to hide those instance variables from external users of your classes, by declaring them in the .m file and not in the .h file anymore, because users of your class don't need to be aware of the internal coding details of your class, but only needs to know the public API.
One final advice: instead of using instance variables, Apple more and more recommends to use #property directly, and let the compiler (explicitely using the #synthesize directive, or implicity with modern LLVM compilers) generate the internal backing variable. So that at the end you generally won't need to declare instance variables at all, and thus omit the empty { } after the #interface directive:
// .h
#interface YourClass : ParentClass
// Declare methods and properties here
#property(nonatomic, assign) int prop1;
-(void)printProp;
#end
// .m
#implementation YourClass
// #synthesize prop1; // That's even not needed with modern LLVM compiler
-(void)printProp
{
NSLog(#"ivar = %d", self.prop1);
}
I have 2 classes that I want to be able to access each others properties, but I don't want those properties accessed from anywhere else. Is there a way to do this? Is the only way to accomplish this through subclassing? Is there no way to establish a "special" relationship between two classes?
If I understand your question, you effectively want class A and class B, which are unrelated by inheritance, to be aware of more innards than are publicly advertised?
Say A has a property called innardsForB that only instances of B should access. You can use class extensions to declare the non-public interface to A.
A.h
#interface A:NSObject
... regular class goop here ...
#end
A-Private.h
#interface A()
#property(nonatomic, strong) Innards *innardsForB;
#end
A.m
#import "A.h"
#import "A-Private.h"
#implementation A
// because "A-Private.h" is #import'd, `innardsForB` will be automatically #synthesized
...
#end
B.m
#import "B.h"
#import "A-Private.h"
#implementation B
...
- (void)someMethod
{
A *a = [self someASomewhere];
a.innardsForB = [[Innards alloc] initForMeaning:#(42)];
}
Protocols are designed for that purpose. You cannot stop 3rd party classes from implementing or using a protocol. Methods within these are public but not nessecarily part of the public interface.
Communication between objects:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html
I you want that classA access classB's properties and classC does't you simply déclare a reference of classA in ClassB and don't declare it in ClassB.
Example :
#interface ClassA
#property (nonatomic, strong) UILabel *label1;
#end
#import "ClassB.h"
#import "ClassA.h" // To access public properties and all methods declared in ClassA.h
#implementation ClassB
ClassA *classA = ....;
classA.label1.text = ...;
#end
From this example, ClassB can access all public(déclared in CalssA.h) proerties and methods of ClassA.
You can use delegate also to do this.
On obj-c properties are protected. That means you can only accede properties by inheritance.
#interface A : NSObject{
NSObject* prop;
}
#end
#implementation A
#end
#interface B : A
#end
#implementation B
- (void)protected{
self->prop; // yep
}
#end
#interface XXX : NSObject
#end
#implementation XXX
- (void)test{
A* a = [[A alloc] init];
a->prop; // wrong, will not compile
}
#end
If you want to access hidden properties via method, you can use simply category hidden in implementation, or bridge. But there is no way to establish a "special" relationship between two classes. But you can enforce this relationship with your code design.