am working my way through the "Beginning iPad Development" Apress book and have noticed that sometimes when the author is assigning values to a property they will use:
self.variable = value;
and other times, they will use:
variable = [value retain];
In both cases variable is a property defined as:
#property (nonatomic, retain) TYPE variable;
I wondered if anyone knew why this is done to help me better understand
Thanks,
William
One place where you use the second form is if you're defining your own setter method. You can't assign to self.variable there, because you'll call the setter recursively. So for example this is wrong:
-(void)setVariable:(TYPE*)value {
if (value != variable) {
[variable release];
self.variable = [value retain]; // WRONG! Calls
// [self setVariable:[value retain]]
}
}
This is right:
-(void)setVariable:(TYPE*)value {
if (value != variable) {
[variable release];
variable = [value retain];
}
}
Does that help?
They are often equivalent memory-wise. The compiler turns self.variable = value into [self setVariable:value] which then calls the generated setter (if you're using #synthesize) and retains it for you.
Related
According to the notes in class, it is said that the local variables are always strong. I really wonder the reason for it. Why the local variables are always strong.
BTW, I am learning blocks. As far as I know, the local variables is supposed to be used and declared inside the method or blocks. However, the local variables I see when using blocks are declared outside the method, but used inside the block. Therefore, I am really curious about it.
The code where the local variable is outside the method looks like this:
_block BOOL stoppedEarly = NO; // this is the local variable
double stoppedValue = 52;
[aDictionary enumerateKeysAndObjectsUsingBlock: ^(id key, id value, BOOL *stop) {
NSLog (#"value for key %# is %#", key, value);
if ([#"ENOUGH" isEqualToString: key] || [value doubleValue] == stoppedValue) {
*stop = YES;
stoppedEarly = YES;
}
}];
1) it just makes sense for local variables to be strong, otherwise all objects would be released right after the creation or you would have to add the __strong attribute everytime yourself:
UIView *view = [[UIView alloc] init];
//if it wasn't a strong reference, view would be released here
//and you couldn't use the reference in the next line
view.backgroundColor = [UIColor blackColor];
2) the Block in your example gets called once for every key-value-pair in the dictionary. Since the intention of the code is to use one variable for all blockcalls, it is declared outside of it. Think of global variables and methods.
Edit: here are some basic examples on scopes of variables.
//global, visible in all methods
NSInteger globalVariable;
- (void)someMethod{
//local, visible only until the end of the method
NSInteger localMethodVariable;
void (^block)() = ^void() {
//local, visible only until the end of the block
NSInteger localBlockVariable;
//may use all 3 variables here
};
//may use globalVariable and localMethodVariable here
}
- (void)someOtherMethod{
//may use globalVariable here
}
I am passing an ivar (NSMutableArray) into some method. I was expecting that if I modify the object inside the function, it would be reflected outside the function, but in this case I need to set the object; something like the following:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
imAnIvar = [response objects];
//Some other stuff
}
But I noticed that the memory reference of imAnIvar inside the function changes when I set it, and given that, the actual ivar doesn't change. I understand that the problem is that I'm changing the reference of the object inside the method, so it stops pointing to the ivar and then it points to some other random memory direction.
I thought about one solution to this problem, and it can be to ensure that the ivar is not nil before calling the function and do something like this:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
NSMutableArray *data = [response objects];
[arrayForTableView removeAllObjects];
for(id element in data){
[imAnIvar addObject:element];
}
//Some other stuff
}
So I use the original object instead of setting it directly. The problem is that in order for this to work I need to ensure that the ivar is not nil, which I think is not clean, because I'll need to do something like this on every call to the method:
if(!_ivar){
//alloc it
}
So my question is: Is there a way to force the local scope variable to point to the original variable even if I'm setting it? if not, is there any cleaner way to make this work?
Do you mean this?
- (void)setFoo:(SomeClass **)objPtr
{
*objPtr = someOtherObject;
}
// call it as:
SomeClass *foo = someObject;
NSLog(#"Before: %#", foo);
[self setFoo:&foo];
NSLog(#"After: %#", foo);
Why not use a getter for the array so that you need not check for the array being nil while using it?
-(NSMutableArray *)iAmAnIvar {
if(_iAmAnIvar == nil) {
_iAmAnIvar = [NSMutableArray array];
}
return _iAmAnIvar;
}
And when you have to set a value to the array, as you mentioned in your question, you could use
[self.iAmAnIvar removeAllObjects];
[self.iAmAnIvar addObject:someObj];
I believe you can use the - (id)copy; function of NSObject
so your code might look like this:
- (void)someFunction:(NSString *)someArg
{
NSString *str = [someArg copy];
}
Is it possible to create an Objective-C class that can have an arbitrary number of dynamic properties at runtime?
I want to be able to call mySpecialClass.anyProperty and intercept this inside my class to be able to provide my own custom implementation that can then return an NSString (for instance) at runtime with raising an exception. Obviously this all has to compile.
Ideal would be if I could refer to my properties using something similar to the new literal syntax, e.g. mySpecialClass["anyProperty"].
I guess in a way I want to create something like a dynamic NSDictionary with no CFDictionary backing store, that executes 2 custom methods on property getting and setting respectively, with the property name passed in to these accessor methods so they can decide what to do.
There are at least two ways to do this.
Subscripting
Use objectForKeyedSubscript: and setObject:forKeyedSubscript:
#property (nonatomic,strong) NSMutableDictionary *properties;
- (id)objectForKeyedSubscript:(id)key {
return [[self properties] valueForKey:[NSString stringWithFormat:#"%#",key]];
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
[[self properties] setValue:object forKey:[NSString stringWithFormat:#"%#",key]];
}
Person *p = [Person new];
p[#"name"] = #"Jon";
NSLog(#"%#",p[#"name"]);
resolveInstanceMethod:
This is the objc_sendMsg executed by the runtime for all methods:
If you look at the bottom, you have the opportunity to resolveInstanceMethod:, which lets you redirect the method call to one of your choosing. To answer your question, you need to write a generic getter and setter that looks-up a value on a dictionary ivar:
// generic getter
static id propertyIMP(id self, SEL _cmd) {
return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}
// generic setter
static void setPropertyIMP(id self, SEL _cmd, id aValue) {
id value = [aValue copy];
NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
// delete "set" and ":" and lowercase first letter
[key deleteCharactersInRange:NSMakeRange(0, 3)];
[key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
NSString *firstChar = [key substringToIndex:1];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
[[self properties] setValue:value forKey:key];
}
And then implement resolveInstanceMethod: to add the requested method to the class.
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) hasPrefix:#"set"]) {
class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v#:#");
} else {
class_addMethod([self class], aSEL,(IMP)propertyIMP, "##:");
}
return YES;
}
You could also do it returning a NSMethodSignature for the method, which is then wrapped in a NSInvocation and passed to forwardInvocation:, but adding the method is faster.
Here is a gist that runs in CodeRunner. It doesn't handle myClass["anyProperty"] calls.
You're asking different things. If you want to be able to use the bracket syntax mySpecialClass[#"anyProperty"] on instances of your class, it is very easy. Just implement the methods:
- (id)objectForKeyedSubscript:(id)key
{
return ###something based on the key argument###
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
{
###set something with object based on key####
}
It will be called everytime you use the bracket syntax in your source code.
Otherwise if you want to create properties at runtime, there are different ways to proceed, take a look at NSObject's forwardInvocation: method, or look at the Objective-C Runtime Reference for functions to dynamically alter a class...
Guillaume is right. forwardInvocation: is the way to go. This answer gives some more details: method_missing-like functionality in objective-c (i.e. dynamic delegation at run time)
This has even more details: Equivalent of Ruby method_missing in Objective C / iOS
And these are some other lesser known Obj-C features that might help you: Hidden features of Objective-C
Enjoy!
To my understand self refers to the current class and when i use a dot after self is to use one of its properties. In the code here there's a use in self.popOperand that i don't understand if popOpernad is not a property. Another thing i don't understand is why
[self pushOperand:result]; works and [self.pushOperand:result]; doesn't.
#import "Calcbrain.h"
#interface Calcbrain()
#property (nonatomic,strong) NSMutableArray *operandStack;
#end
#implementation Calcbrain
#synthesize operandStack = _operandStack;
-(NSMutableArray *) operandStack
{
if(_operandStack == nil) _operandStack = [[NSMutableArray alloc]init];
return _operandStack;
}
-(double)popOperand
{
NSNumber *objectNum = [self.operandStack lastObject];
if (objectNum)[self.operandStack removeLastObject];
return [objectNum doubleValue];
}
/*-(void) setOperandStack:(NSMutableArray *)operandStack
{
_operandStack = operandStack;
}*/
-(void)pushOperand:(double)opernand
{
[self.operandStack addObject:[NSNumber numberWithDouble:opernand]];
}
-(double)performOperation:(NSString *)operation
{
double result=0;
if([operation isEqualToString:#"+"])
{
result = self.popOperand + self.popOperand;
}
else if ([#"*" isEqualToString:operation])
{
result = self.popOperand * self.popOperand;
}
[self pushOperand:result];
return result;
}
#end
Whilst the . notation is primarily used for properties, it can be used for paramaterless methods that return a value. Why? Because the synthesised getter for a property is in the same form.
-(double)calcValue {
....
return value;
}
Is equivalent to the property declaration:
#property (nonatomic, readonly) double calcValue;
Whilst there may be no property declaration, it doesn't mean the . notation cannot be used. The compiler will effectively change . notation to a method call when compiling, as . is a form of syntactic sugar. As so:
self.popOperand
// Translates to
[self popOperand];
This leads on to part 2, why does [self.pushOperand:result]; not work? The reason being is that . does not support the passing of parameters directly.
The only way to assign/push a parameter to a property is via self.pushOperand = result, but this wouldn't work, because there isn't a corresponding - (void)setPushOperand:(double)pushOperand; that the . notation assignment maps to.
[self pushOperand:result]; works because you're being explicit in calling a particular method, called pushOperand:.
Overall, keep . notation for properties only, and if you're using a method that isn't designed to be a 'property', be explicit.
Update: self is a reserved keyword, that represents a pointer to the instance we're working within at that time.
For example, I can create two instances of Calcbrain outside of Calcbrain, for example BrainViewController:
Calcbrain* instance1;
Calcbrain* instance2;
Now, Calcbrain has methods declared within it, let's use -(double)performOperation:(NSString *)operation as an example. Now, if I wanted to call that from BrainViewController, I would do:
[instance1 performOperation:#"+"];
[instance2 performOperation:#"+"];
Because we are calling a method which is part of another class, I have to determine the correct instance I've created to refer to it (i.e. instance1 and instance2). But how would I call that from within the class itself, and make sure it applies to the correct instance? The instance I've created is unaware of the other instances I've created. Use self. self allows you to reference yourself within methods. So if I wanted to performOperation within Calcbrain itself, I would need to use self:
[self performOperation:#"+"];
The "dot" is just a syntactic sugar, it can be used even if there isn't a declared property. The expression
a.someProperty
is equivalent to
[a someProperty]
and the expression
a.someProperty = c
is equivalent to
[a setSomeProperty:c]
Therefore, self.popOperand is just the same as [self popOperand]. And one could also use the "dot" in some absurd cases like [NSMutableArray alloc].init.
Using the "dot" syntax for non-properties are highly discouraged. If I were the maintainer of this code I would change all self.popOperand back to [self popOperand] to avoid confusion.
(BTW, it is not defined which side of the + will get evaluated first. Better change
result = [self popOperand] + [self popOperand]
to
double operand1 = [self popOperand]
double operand2 = [self popOperand]
result = operand1 + operand2;
This will be a trouble when you define - and /.)
Dot is used to access properties of the class. You can also access properties and methods without dots:
[classInstance someMethodOrProperty];
This code: [classInstance someProperty]; equals this code classInstance.someProperty; You cannot call methods with dots like properties.
I am a little curious about the last lines in the two examples presented below (i.e. planetName = [value ??????]) My understanding is that the 1st example with the copy is best as that takes a copy of the string object to protect against the original string object being changed elsewhere.
I am also a little confused by the last line in the 2nd example, again my understanding was that the value object was being passed into the method, I guess I am confused as value is being retained with no associated release? Can someone set me straight?
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value copy];
}
}
.
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value retain];
}
}
Given:
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value copy];
}
}
The -copy ensures that if someone passes in an instance of NSMutableString as value, then planetName won't change in your instances out from under you.
A good defensive programming pattern. Note that -copy is free on immutable strings; -copy just bumps the retain value and returns self (the instance of the string that was copied).
Now, consider your second example:
- (void)setPlanetName:(NSString *)value {
if (planetName != value) {
[planetName release];
planetName = [value retain];
}
}
Perfectly valid and works fine, just not as defensive.
In both cases, planetName must be released in your -dealloc method.
- (void) dealloc
{
[planetName release];
planetName = nil; // defensive
[super dealloc];
}
It doesn't matter if the string is copied, retained, or was originally a constant string that was passed into your setter. If you retain it (or implied retain it through copy), you must release it.
Note that you can think of this as "escape patterns". Whenever the existing value of planetName escapes your object, you must release it. That can happen when the object is deallocated or when a new value of planetName is set, hence the -release in the setters.
Or, if on Mac OS X, you could turn on garbage collection and be done with it. In any case, you should be using #property & #synthesize to automatically generate the getter/setter pair.
in the class's dealloc method, there should be another [planetName release] there, which will handle releasing of any instance variables. If that is present then there is no memory leak to worry about.
As for your other question, yes copy is used if you potentially have a mutable object that you don't want to allow other code to be able to modify what your class expects. You are expected to release any object that you call copy on, as it returns something with a retain count of 1. Also, in the case of immutable objects (like NSStrings vs. NSMutableStrings) copy just calls retain since there is no need to make a full copy of something that is immutable.