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
}
Related
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];
}
I have a method that creates a dictionary from NSJSONSerialization class. I then enumerate the json, and create objects to store state for each instance.
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSArray *moviesJson = [json objectForKey:#"movies"];
for(NSDictionary *element in moviesJson)
{
RABMovie *myMovie = [[RABMovie alloc] initWithJson:element];
// RABMovie *myMovie = [RABMovie new];
// myMovie.json = element;
[_myMovieNames addObject: myMovie];
myMovie = nil;
}
[self.movieTableView reloadData];
}
Problem: I want to create my object by passing in element in the allocator, however when I do this, my UITTableView rows all contain the same movie data. It is always the last item, leading me to believe I am working with only one memory address, and the last update affects the whole collection.
If I uncomment the code to use the json as a property instead of a alloc param, I no longer have the issue and all works well. I've also tried creating a completely new NSDictionary via a deep copy of element to no avail.
Does someone know why this is happening? BTW, I am using ARC. -Thanks for the time.
Edit: Added more code. I've included a property movieName to illustrate how I use the ivar _json.
#implementation RABMovie
NSDictionary *_json;
- (id) initWithJson: (NSDictionary*) jsonAsDictionary
{
if (self = [super init])
{
_json = jsonAsDictionary;
}
return self;
}
- (NSString*) movieName
{
return [_json objectForKey:#"title"];
}
I think you meant to declare _json as an instance variable. Instead it's a globally visible (at least within that class) variable - not 100% sure on the scoping rules, but regardless, it's not an instance variable - it's a single variable shared by all instances! Try this instead:
#implementation RABMovie {
NSDictionary *_json;
}
/* ...rest of class */
#end
Putting it inside the curly braces after the #implementation directive makes it an instance variable. Hope this helps!
EDIT: Do you have a property called json on RABMovie already? Then you can skip the instance declaration altogether and the compiler will generate the an instance variable for you. That's probably happening already actually, which is why it works when you go through the property - it's accessing the ivar rather than the "global".
I've got a class method that uses dispatch_once to create a static object. Inside the dispatch_once block I use [self class] and was wondering if I need to use a weak reference to self to avoid a retain cycle?
+ (NSArray *)accountNames{
static NSArray *names = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
names = [[[self class] accounts] allKeys];
names = [names sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
});
return names;
}
If I use a weak reference to self I get a warning:
+ (NSArray *)accountNames{
static NSArray *names = nil;
static dispatch_once_t predicate;
__weak TUAccount *wself = self;
dispatch_once(&predicate, ^{
names = [[[wself class] accounts] allKeys];
names = [names sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
});
return names;
}
Incompatible pointer types initializing 'TUAccount *__weak' with an expression of type 'const Class'
Because I get a warning I don't think I need to use a weak reference to self in this case but I wanted to see what you guys thought.
There is no reason to worry about a retain cycle here, because it's meaningless to retain or release a class object -- retain and release simply have no effect.
Your attempt at making a weak reference is wrong, because you are taking a class object self and trying to cast it to an instance of TUAccount. The two are completely different things.
Also, you can simplify:
names = [[[self class] accounts] allKeys];
Since self is already a class, [self class] == self, so do this instead:
names = [[self accounts] allKeys];
I have checked one more time iOS SDK Docs and found next:
Objective-C Objects
In a manually reference-counted environment, local variables used within the block are retained when the block is copied. Use of instance variables within the block will cause the object itself to be retained. If you wish to override this behavior for a particular object variable, you can mark it with the __block storage type modifier.
If you are using ARC, object variables are retained and released automatically as the block is copied and later released.
Note: In a garbage-collected environment, if you apply both __weak and __block modifiers to a variable, then the block will not ensure that it is kept alive.
If you use a block within the implementation of a method, the rules for memory management of object instance variables are more subtle:
If you access an instance variable by reference, self is retained;
If you access an instance variable by value, the variable is retained.
The following examples illustrate the two different situations:
dispatch_async(queue, ^{
// instanceVariable is used by reference, self is retained
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
// localVariable is used by value, localVariable is retained (not self)
doSomethingWithObject(localVariable);
});
Conclusion: I assume that there is no problem with using self in block. It will be retained and after execution released.
Moreover, you are not storing block in memory and using it directly. So it is copied to the heap, executed and pushed from it. I don't see any retain cycles.
Hope I am right!
I have some logic that test if an object is nil or not, how can I set it to nil?
Like:
// in some method
if (true){
[self myObj] = [[myObj alloc]init];
} else{
[self myObject] = nil; //??? How to do this with Automatic Ref. Counting
}
// elsewhere
if([self myObj]){
}
Your code is incorrect.
You need to use properties and assign values to them, like [self setMyObject:nil]; or [self setMyObj:[[myObj alloc] init]];.
Your code is wrong. Try:
self.myObject=nil;
//or
[self setMyObject:nil];
Also, make sure that myObject is a property in your class, otherwise using self won't work.
[self myObj] is not assignable, as it is not a lvalue. To fix this, either reference the underlying variable, e.g. self->myObj, or use [self setMyObj:] if you are using properties.
You are using a getter as a setter. That isn't going to work. It should be
[self setMyObj:[myObj alloc]init]];
And
[self setMyObj:nil];
Assuming you have implemented the setters. Under ARC you don't really need to if you are just accessing an ivar - you can access it directly and the reference counting will be done for you:
myObj = [MyObj alloc] init];
And
myObj = nil;
Will set and remove everything for you.
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.