Objective-c setter/getter callback - objective-c

I have an interface with properties.
I would like to know the way to declare callback to reach its instance's setter or getter.
Is there a way to do it?
Sorry for my english and thx for your answers and time.

If you declared a #property for your instance variable, and then synthesized it in your implementation file, your getter and setter are automatically created for you. Example for a NSMutableArray
#interface ...
{
NSMutableArray *array;
}
#property (nonatomic, retain) NSMutableArray *array;
Then on your implementation:
#implementation ...
#synthesize array;
Once that's done, you can get and set your instance variable values by using:
Getter: self.array OR [self array]
Setter: self.array = ... OR [self setArray:...]

I am not sure if I understand your question correctly but if you are trying to get some code executed every time the setter or getter is invoked there are basically two ways to do that:
1) you can overwrite the synthesized getter and/or setter like this
Header:
#interface ...
{
NSString *example;
}
#property (nonatomic, copy) NSString *example;
Implementation:
#implementation ...
#synthesize aString
-(void)setExample:(NSString *)newExample
{
if (example != newExample)
{
[example autorelease];
example = [newExample copy];
// YOUR CODE HERE
}
}
...and similarly for the getter.
2) you can observe the variable via KVO and get a 'callback' whenever the variable changes. This, of course, only runs you code when the setter is invoked, not the getter.

Related

Variable losing value in delegation pattern

I'm trying to learn about delegation in Objective-C, and am having a minor issue with a variable losing it's data in the transfer process. I have Class1 that contains an NSMutableArray. The array gets populated, then I would like to transfer the array's values to Class2, and display it. Here is the relevant code in Class1:
//Class1.h
#class Class1;
// define the protocol for the delegate
#protocol Class1Delegate
#required
-(void)sayHello:(Class1 *)customClass withAntArray:(NSMutableArray *)antArray;
#end
#interface Class1 : MySuperClassName
#property (nonatomic, assign) id delegate;
-(void)helloDelegate;
#end
//Class1.m:
#interface Class1 ()
#property (nonatomic, strong) NSMutableArray *antOccurenceTimes;
#end
#implementation Class1
#synthesize antOccurenceTimes;
-(void)helloDelegate
{
// send the message to the delegate
[_delegate sayHello:self withAntArray:self.antOccurenceTimes];
}
Now, this is what I have in Class2:
#import "Class1.h"
#interface Class2 : UIView <Class1Delegate>
#end
// Class2.m:
- (void)appropriateTimeToCallMethod {
Class1 *initAntMarks = [[Class1 alloc] init];
initAntMarks.delegate = self;
[initAntMarks helloDelegate];
}
-(void)sayHello:(Class1 *)customClass withAntArray:(NSMutableArray *)antArray {
NSLog(#"Hello! %#", antArray.description);
}
The antArray.description reads as "NULL". Now, I figured that obviously it will be null, because I just created an entirely new instance of the class right before calling upon the needed method. I feel like I may have something mixed up, and being so new to delegation, I'm not sure exactly what. Does anyone know what I need to tweak to utilize Delegation?
I forgot to add that I did initialize it in Class1, and it gets populated just fine. It's only in class2 that this is occurring.
I initalize antOccuranceTimes in a separate method in ClassA in the snippet below, and the NSLog fires twice...
NSLog(#"Array initalized in class A");
antOccurenceTimes = [NSMutableArray new];
Change this line:
#property (nonatomic, assign) id delegate;
to:
#property (nonatomic, weak) id <Class1Delegate> delegate;
assign should only be used for C primitives, not Objective-c object references. You should also be checking if your object actually conforms to the delegate before messaging the delegate.
Edit:
I think you may be confused about the purpose of delegation.
Class1 *initAntMarks = [[Class1 alloc] init];
initAntMarks.delegate = self;
[initAntMarks helloDelegate];
Why are you calling a method on an object which in turn calls a delegate method when you could simply create a method that returns the NSMutableArray? The way you have your code currently set up requires that before the call to -helloDelegate you have to have filled the array with the appropriate objects. The purpose of delegation in MVC is to inform an object about an event that took place inside of another object. You are "delegating" the task off to another object, or you could say, that another object if responsible for the fulfillment of the task. Read the Apple Docs on Delegation. Delegation in your code is not the correct pattern to implement, as I stated you can simply return that array with a method call.
Edit 2:
There are two ways you can achieve this, through property methods or through an explicit method that returns your array. If you choose to use property methods, the property declaration must be in the public interface i.e. the .h file so that your class can all the accessors when the object is being implemented.
//Inside the .h
#property (nonatomic, strong) NSMutableArray *antOccurenceTimes;
This will automatically provide you with two accessor methods for the antOccurenceTimes property. These are the getter -antOccurenceTimes and setter -setAntOccurenceTimes: methods. Now after you initialize the class and fill your array you can call -antOccurenceTimes to return the array.
You can also create an explicit method that return the array:
- (NSMutableArray *)hello{
//Do something here
return _antOccurenceTimes;
}
You have not yet initialized the antOccurenceTimes. Of cause it is nil. There are many options depending on what you need. You can, for example, initialize it in a init function:
- (instancetype)init {
self = [super init];
if( self ) {
antOccurenceTimes = [NSMutableArray array];
[antOccurenceTimes addObject:#"Hello World"];
}
}
Or maybe initialize it before you call the delegate the function.
-(void)helloDelegate
{
// send the message to the delegate
self.antOccurenceTimes = [NSMutableArray array];
[self.antOccurenceTimes addObject:#"Hello World"];
[_delegate sayHello:self withAntArray:self.antOccurenceTimes];
}
I think you get my point.

Confusing Objective-C class structure

Here's a (reduced) class declaration from an example on apple's developer:
#interface myController : UITableViewController {
NSArray *samples;
}
#property (nonatomic, retain) NSArray *samples
What is the purpose of declaring
{
NSArray *samples;
}
when you declare it again as a property? If you leave out:
{
NSArray *samples;
}
you can still use #synthesize in your .m and get a reference to it!
I'm a little confused as to the purpose of the first declaration.
Thanks
Properties are just a handy way to declare accessors to you data. It usually leads to some member variable but not necessarily. And that member var can have different name:
#interface myController : UITableViewController {
NSArray *mSamples;
}
#property (nonatomic, retain) NSArray *samples
#end
#implementation
#synthesize samples = mSamples;
#end
Or you can use properties without vars at all:
#interface myController : UITableViewController {
}
#property (nonatomic, retain) NSArray *samples
#end
#implementation
-(NSArray*) samples {
//you can for example read some array from file and return it
}
-(void) setSamples:(NSArray*) arr {
//write that array to file or whatever you want
}
#end
With new compiler you can use properties without ivars at all, compiler will generate them for you implicitly.
With a property declaration, there is no purpose or benefit in explicitly declaring the backing instance variable. It's just leftovers from habit.
Edit: For iOS or Mac 64-bit Intel, explicitly declaring ivars was never needed for properties. But they were needed for other Mac work — hence the examples.
Also, I did find a difference. When an ivar is explicitly declared, unless you state otherwise, it is a protected ivar, available to subclasses. But when an ivar is implicitly created for a property, subclasses don't have access to the ivar.

#property and setters and getters

If I create a #property and synthesize it, and create a getter and setter as well like so:
#import <UIKit/UIKit.h>
{
NSString * property;
}
#property NSString * property;
--------------------------------
#implementation
#synthesize property = _property
-(void)setProperty(NSString *) property
{
_property = property;
}
-(NSString *)property
{
return _property = #"something";
}
Am I correct in assuming that this call
-(NSString *)returnValue
{
return self.property; // I know that this automatically calls the built in getter function that comes with synthesizing a property, but am I correct in assuming that I have overridden the getter with my getter? Or must I explicitly call my self-defined getter?
}
is the same as this call?
-(NSString *)returnValue
{
return property; // does this call the getter function or the instance variable?
}
is the same as this call?
-(NSString *)returnValue
{
return _property; // is this the same as the first example above?
}
There are a number of problems with your code, not least of which is that you've inadvertently defined two different instance variables: property and _property.
Objective-C property syntax is merely shorthand for plain old methods and instance variables. You should start by implementing your example without properties: just use regular instance variables and methods:
#interface MyClass {
NSString* _myProperty;
}
- (NSString*)myProperty;
- (void)setMyProperty:(NSString*)value;
- (NSString*)someOtherMethod;
#end
#implementation MyClass
- (NSString*)myProperty {
return [_myProperty stringByAppendingString:#" Tricky."];
}
- (void)setMyProperty:(NSString*)value {
_myProperty = value; // Assuming ARC is enabled.
}
- (NSString*)someOtherMethod {
return [self myProperty];
}
#end
To convert this code to use properties, you merely replace the myProperty method declarations with a property declaration.
#interface MyClass {
NSString* _myProperty;
}
#property (nonatomic, retain) NSString* myProperty
- (NSString*)someOtherMethod;
#end
...
The implementation remains the same, and works the same.
You have the option of synthesizing your property in your implementation, and this allows you to remove the _myProperty instance variable declaration, and the generic property setter:
#interface MyClass
#property (nonatomic, retain) NSString* myProperty;
- (NSString*)someOtherMethod;
#end
#implementation MyClass
#synthesize myProperty = _myProperty; // setter and ivar are created automatically
- (NSString*)myProperty {
return [_myProperty stringByAppendingString:#" Tricky."];
}
- (NSString*)someOtherMethod {
return [self myProperty];
}
Each of these examples are identical in how they operate, the property syntax merely shorthand that allows you to write less actual code.
return self.property – will call your overridden getter.
return _property; – accesses the property's instance variable directly, no call to the getter.
return property; – instance variable.
EDIT: I should emphasize that you will have two different NSString variables -- property and _property. I'm assuming you're testing the boundaries here and not providing actual production code.
above answer elaborate almost all the thing , i want to elaborate it little more.
// older way
#interface MyClass {
NSString* _myProperty; // instance variable
}
- (NSString*)myProperty; // getter method
- (void)setMyProperty:(NSString*)value;//setter method
#end
the instance variable can not be seen outside this class , for that we have to make getter and setter for it.
and latter on synthesis it in .m file
but now
we only used
#property(nonatomic) NSString *myProperty;
the #property is an Objective-C directive which declares the property
-> The "`nonatomic`" in the parenthesis specifies that the property is non-atomic in nature.
-> and then we define the type and name of our property.
-> prototyping of getter and setter method
now go to .m file
previously we have synthesis this property by using #synthesis , now it also not required it automatically done by IDE.
little addition : this `#synthesis` now generate the getter and setter(if not readonly) methods.

NSArray #property backed by a NSMutableArray

I've defined a class where I'd like a public property to appear as though it is backed by an NSArray. That is simple enough, but in my case the actual backing ivar is an NSMutableArray:
#interface Foo
{
NSMutableArray* array;
}
#property (nonatomic, retain) NSArray* array;
#end
In my implementation file (*.m) I #synthesize the property but I immediately run into warnings because using self.words is the same as trying to modifying an NSArray.
What is the correct way to do this?
Thanks!
I would declare a readonly NSArray in your header and override the getter for that array to return a copy of a private NSMutableArray declared in your implementation. Consider the following.
Foo.h
#interface Foo
#property (nonatomic, retain, readonly) NSArray *array;
#end
Foo.m
#interface Foo ()
#property (nonatomic, retain) NSMutableArray *mutableArray
#end
#pragma mark -
#implementation Foo
#synthesize mutableArray;
- (NSArray *)array
{
return [[self.mutableArray copy] autorelease];
}
#end
Basically, put the NSArray property in a category in your header file and the NSMutableArray property in the class extension in your implementation file. Like so...
Foo.h:
#interface Foo
#end
#interface Foo (Collections)
#property (nonatomic, readonly, strong) NSArray *someArray;
#end
Foo.m
#interface Foo ()
#property (nonatomic, readwrite, strong) NSMutableArray *someArray;
#end
Simple:
1) Don't use a property when it ain't one.
2) Code simplifies to:
- (NSArray *)currentArray {
return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray - otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it.
}
- (void)setArray:(NSArray *)array {
[mutableArray setArray:array];
}
When the object is alloced create the array, when it dies, dealloc the array.
When large effects happen at the mere use of a '.' operator, its easy to overlook hugely inefficient code. Accessors are just that. Also - if someone calls aFoo.array - the contract is to get access to foo's array members - but really its just a copy at the time of the call. The difference is real enough that it caused bugs in the other implentations posted here.
Update: this answer is not valid anymore. Use one of suggested solutions below.
These days you can do the following:
Foo.m:
#implementation Foo {
NSMutableArray* _array;
}
#end
Foo.h:
#interface Foo
#property (readonly, strong) NSArray* array;
#end
You can still address mutable _array by ivar from the inside of implementation and outside it will be accessible via immutable property. Unfortunately this doesn't guarantee that others can't cast it to NSMutableArray and modify. For better protection from idiots you must define accessor method and return immutable copy, however that might be very expensive in some cases.
I would actually agree with one of the comments above that it's better to use simple accessor methods if you need to return some read-only data, it's definitely less ambiguous.
That's because your property must match the actual ivar's class type.
A possible solution/workaround:
//Foo.h:
#interface Foo
{
NSMutableArray* mutableArray;
}
#property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
#end
//Foo.m:
#interface Foo ()
#property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
#end
#implementation
#synthesize mutableArray;
#dynamic array;
- (NSArray *)array {
return [NSArray arrayWithArray:self.mutableArray];
}
- (void)setArray:(NSArray *)array {
self.mutableArray = [NSMutableArray arrayWithArray:array];
}
#end
You're adding a private mutableArray property in a class extension and making the public array simply forward to your private mutable one.
With the most recent language extensions of ObjC I tend to remove the
{
NSMutableArray* mutableArray;
}
ivar block entirely, if possible.
And define the ivar thru the synthesization, as such:
#synthesize mutableArray = _mutableArray;
which will generate a NSMutableArray *_mutableArray; instance for you.
Simplest answer: your property type (NSArray) doesn't match your instance variable type (NSMutableArray).
This is yet another good reason that you shouldn't define your own backing variables. Let #synthesize set up your instance variables; don't do it by hand.

Extending properties generated using #synthesize in Objective-C

Suppose I have an #property declared like this:
#property (readwrite,retain) NSObject *someObject;
And I synthesize it like this:
#synthesize someObject = _someObject;
This generates getters/setters for me. Also, according to the docs, the setter will have built in thread safety code.
Now, suppose I want to add some code to the setSomeObject: method. Is there any way that I can extend the existing on from #synthesize? I want to be able to reuse the the thread safety code that it autogenerates.
You can define a synthesized "private" property, (put this in your .m file)
#interface ClassName ()
// Declared properties in order to use compiler-generated getters and setters
#property (nonatomic, strong <or whatever>) NSObject *privateSomeObject;
#end
and then manually define a getter and setter in the "public" part of ClassName (.h and #implementation part) like this,
- (void) setSomeObject:(NSObject *)someObject {
self.privateSomeObject = someObject;
// ... Additional custom code ...
}
- (NSArray *) someObject {
return self.privateSomeObject;
}
You can now access the someObject "property" as usual, e.g. object.someObject. You also get the advantage of automatically generated retain/release/copy, compatibility with ARC and almost lose no thread-safety.
What #synthesize does is equivalent to:
-(void)setSomeObject:(NSObject *)anObject {
[anObject retain];
[someObject release];
someObject = anObject;
}
or
-(void)setSomeObject:(NSObject *)anObject {
if(someObject != anObject) {
[someObject release];
someObject = [anObject retain];
}
}
so you can use this code and extend the method.
However, as you said, this code might not be thread-safe.
For thread safety, you might want to take a look at NSLock or #synchronized (thanks to unwesen for pointing this out).