Curly Braces in Objective-c - objective-c

Note: My question is based after checking this and the answers to it.
In some bigger methods, there are pieces of code that you only want to be alive for a certain period of time. An example:
1) I have a big method that sets my UI: UILabel's size, colour, positioning, UIView's gesture recognisers, etc. Knowing the above, does it makes sense to do something like this:
- (void)setUI
{
//setting other UI elements
{
// Add the Swipe Gesture to the swipeUpView
UISwipeGestureRecognizer *swipeGestureUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(animeViewWithSwipeGesture)];
swipeGestureUp.direction = UISwipeGestureRecognizerDirectionUp;
[_swipeUpView addGestureRecognizer:swipeGestureUp];
}
// setting other UI elements
}
Based on the above example, is this a valid way of lowering the memory footprint of an application?
Is there any relation with #autoreleasepool{}?

It's just plain C syntax. You use it to open a new scope as others mentioned. What this means (this is C feature) that you can use same names for stack variables again, as they are in different scope. Also, variables you declare inside that scope will not be accessible by outside scope.
There is no relation to memory footprint, only about code organization.

Based on the above example, is this a valid way of lowering the memory footprint of an application?
No. They're not even related. Neither are they related to #autoreleasepool - this usage of curly braces is the plain C way of opening a new scope.

What curly braces do is just to define a new scope, so you can define new variables with the same name than other outer scope variables.
The #autoreleasepool{} block is quiet similar, but also declares an autorelease pool at the beginning and drains it at the end, so it may be better from a memory footprint point of view because all the autoreleased objects declared there will be released when exiting that scope.

Related

Difference between using self.variable and _variable when init these variables [duplicate]

This question already has answers here:
Difference between self.ivar and ivar?
(4 answers)
When should I use the “self” keyword?
(6 answers)
Closed 8 years ago.
I know instance variable and property. I often see people init a UILabel like this
self.label = [[UILabel alloc] init]; //and
_label = [[UILabel alloc] init];
So, what's the difference between using self.label and _label to set a object?
The difference is simple: Using self.label = [[UILabel alloc] init] will actually invoke the method [self setLabel:[[UILabel alloc] init]], and using _label = [[UILabel alloc] init] will directly assign the value to the instance variable.
In practice what this means is that using the dot syntax is usually the best as the method invoked probably handles a lot of stuff for you, including:
Memory management: For example, if you declare a property with attribute 'strong' or 'retain', then the method invoked should retain the object assigned.
Key-Value Coding notifications: Maybe the class is key-value coding compliant for the property, which means the invoked method will notify the changes to observer objects.
Why would you not use the dot syntax? There are two potential reasons:
To avoid side effects: A good practice is to not use the dot syntax inside an initializer method. This is because we want to assign the value but don't want the other side effects of the invoked method for safety reasons.
Performance: This is probably rare, but maybe you are trying to implement a method with high performance and using instance variables directly can save the cost of invoking a method.
If you want to know more, I recommend reading this iOS guide which describes in more detail the ideas I mention here.
The difference is that:
the names with _variable are instance variables.
self.variable is calling a getter method on your object.
In your example, the instance variables are automatically generated and you don't need to synthesize your properties either.
The real important difference in your example comes into play if you are not using ARC-
self.variable will retain an object for you if you mark the property with retain or strong _variable does not address memory management at all
In your example, self.label would call the getter method 'label' on self -- this is equivalent to calling [self label]. _label is the backing store for the class instance property -- i.e. an instance variable, no different than accessing a standard variable directly. There is no getter method wrapped around it.
The difference is very, very important, because you are able to override the getter/setter methods for properties. You may wish to do this, for e.g., if you would like to bundle some behavior change with the state change of the variable. Calling the getter or setter maintains this behavior. Calling the getter also retains the variable.
Basically, unless you know why you're preferring to class _label in any particular instance, stick with the getter self.label. One case where you may want to use _label is during initialization, where you need to set a happy default w/o behavior the getter may bring with it.
The difference is that using _label is accessing the instance variable (ivar for short) directly, where as using self.label is actually calling [self setLabel:[[UILabel alloc] init]];.
Calling the setLabel: method does other things, such as possibly retaining the variable (depending on how the property was declared), but can also trigger other side effects as set up in your setLabel: method. Those side effects could be something like data validation, or could perhaps sync that value to a server.

Assigning an existing CGColor to a CGColor property works in iOS Simulator, not iOS device. Why?

I know how to fix the problem that I am about to outline, however, I am a bit baffled as to why the code scenario works in the iOS simulator but not on my iPad.
I have a method that checks various properties, and then sets the background color of a CALayer depending on the state of a property. The following code is similar to my method of color assignment:
//This will be the CALayer BGColor...
CGColor c = UIColor.blueColor.CGColor; //Blue is the default
switch (myState)
{
case state_one:
c = UIColor.greenColor.CGColor;
//... more code ...
break;
case state_two:
c = UIColor.redColor.CGColor;
//... more code ...
break;
case state_three: //multiple cases are like the state_three case.
//Other code, but I don't need to assign the color. Blue works...
}
myCALayer.backgroundColor = c; //Oh-noes!!! Here we get the dreaded EXC_BAD_ACCESS on iPad
//...more code dealing with the layer.
The code above works without trouble in the Simulator. However, when I run the application on my iPad, it crashes at the backgroundColor assignment.
I can fix this by getting rid of the CGColor variable and assigning the background color from directly within my switch/case statement, and that's what I'm planning on doing.
However, I am curious. Why would this work in one environment and not the other?
UPDATE
Couple things. First, it's worth mentioning that this is an ARC project, using Xcode 4.2, targeting iOS 5 devices. Also, my color assignement code isn't entirely what it looks like because I have a series of defines that I use to set these colors because they are referenced all throughout my application.
This is what a few of the #define statements looks like:
#define BLUE [UIColor colorWithRed:8.0/255.0 green:80.0/255.0 blue:150.0/255.0 alpha:1.0].CGColor
#define GREEN (UIColor.blueColor.CGColor)
//...and there are about 6 other colors
I tried to simplify my code because the compiler should replace the refs to my refs to my defines. Still, it's worth mentioning just in case.
Because of ARC the color is released too early in the end of the method.
i use: CGColorRetain
CGColorRef whiteColor = CGColorRetain([UIColor colorWithRed:1.0 green:1.0
blue:1.0 alpha:1.0].CGColor);
Here's my hunch: It's possible that the UIColor that created it (and held its only reference) has been destroyed before you pass the CGColor. Since CGColorRef's reference counting is not handled for you under ARC, the color would be a dangling reference if the UIColor that held it were destroyed before you use the CGColor.
ARC has an optimization where "autoreleased" objects may never be added to an autorelease pools, and instead, released after the objc object is no longer referenced. This is a combination of three things:
The compiler version and options you use. No surprise, the compiler adds the reference counting, and there are variations for this.
The ObjC Runtime. The runtime may utilize thread local data. Naturally, this can include your stack. If you read into the details of how an object may bypass an autorelease pool, this should be clearer.
The libraries you use (including system libraries and frameworks). As the compiler and runtimes are updated, the libraries may use ARC, or they may use different runtime calls to execute the program.
Knowing that, I suspect this program would rectify the problem:
UIColor * c = UIColor.blueColor; //Blue is the default
switch (myState) {
case state_one:
c = UIColor.greenColor;
//... more code ...
break;
case state_two:
c = UIColor.redColor;
//... more code ...
break;
case state_three: //multiple cases are like the state_three case.
//Other code, but I don't need to assign the color. Blue works...
}
myCGLayer.backgroundColor = c.CGColor;
//...more code dealing with the layer.
In more detail, there are number of ways the compiler and the objc runtime can interpret and execute your program. This means that this problem could affect you when you change compiler versions, or when the runtime (OS) is updated. It can also happen as the libraries you use are updated or built with different versions or compiler settings. For example: If the library switches to ARC along the way, it may use a different runtime calls, or the calls may utilize thread local data differently if the compiler injected calls are updated.
Details about the ARC spec as it relates to the runtime can be found here:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime
A similar problem was seen here:
EXC_BAD_ACCES drawing shadow
You don't say what your instance myCGLayer us derived from, but I'll take a shot and say it's not derived from CGLayer, because CGLayer doesn't have a backgroundColor property. So I'm guessing (again), the parameter passed should be of type UIColor and not CGColor. CGColor is derived from class CFType. UIColor is derived from NSObject. They shouldn't be interchangeable. If my guesses are right, I'm surprised it works in the simulator.
Don't slap me too hard if my guesses are wrong.

ARC: Memory does not get reclaimed?

I am working on an iPad (only) app and I stumbled across a weird problem. The app gets terminated after a memory warning on iPad 1 but works fine on iPad 2.
I am using ARC and targeting iOS 5. I use nibs and most of my assets are displayed using UIImageViews. I also have a few hundred buttons and plenty of gesture recognizers... I re-watched the WWDC11 videos (sessions 323 and 322) on ARC and I don't seem to be doing anything special.
The app is UIImage intensive, I am doing lots of animations using UIImage. I am however using the initWithContentsOfFile constructor rather than the imageNamed call. I'm trying to prevent the images from being cached by the system.
I'm also using GCD to schedule sound effects and to animate views. I'm always doing this on the main thread.
The app uses a UINavigationController that never has more than 1 UIViewController on it's stack. I can confirm that this is true because the didReceiveMemoryWarning only gets called on the current view controller (I'm logging the call).
The thing I don't understand is why Instruments is reporting high numbers (as if the view controllers don't get deallocated) in both the Allocations and VM Tracker instruments. The Allocations instrument shows a small drop when I navigate from one view controller to another (which is what I expect) but the VM Tracker Instrument shows that the Dirty Size is not dropping when I do the same thing. Eventually the app uses too much memory and gets terminated (on iPad 1). When I get memory warnings on the iPad 2 the app does NOT get terminated though...
It feels as if my images, sounds or views don't get destroyed and the memory does not get reclaimed... My object hierarchy is very basic and there should not be any retain cycles of any sort. I don't even have simple delegates...
Do you have any suggestions? I really don't want to release this app only for the iPad 2 or newer... It's an app for kids and it would be a pitty... I'd be so much happier to learn that I'm doing something wrong, as I really want to make sure this app is the best it can be...
Cheers,
Nick
There are ways to say, 'optimise' your objects by setting their properties to nil when certain things aren't needed -- so while you can't write a dealloc method anymore, you can do self.object = nil (when pertinent) which ends up doing something like this in a non-ARC world for an 'retain' (i.e., strong) property:
- (void)setObject:(id)newObject
{
[object release]; // send release message to current object
object = newObject; // set reference to newObject
[object retain]; // send retain message to newObject
}
Now while in ARC you don't/can't write retain/release yourself in your code, the compiler inserts these calls for you, meaning that in practise, setting a property to nil would do this in the above example:
[object release]; // send release message to current object
object = nil; // set reference to nil
[object retain]; // send retain message to nil (no effect)
Moreover, this is just the tip of the iceberg -- you should make sure that there are no retain cycles in your code, which could be resulting in objects leaking without recourse to their destruction. This means, that there may be places where you're using strong references to a property (i.e., an object), when you should be using a weak property. The difference being, that strong references are retained, and weak references are assigned, the former having its retainCount incremented and the latter resulting in a property assignment that looks like this if handwritten:
- (void)setObject:(id)newObject
{
object = newObject;
}
I don't like answering my own question but I figured it could be helpful to future googlers. I implemented my own UIImage based animation and I no longer use the animationImages property. Turns out my memory issues are now gone as I no longer need to store all the images in memory and I load them as they are required using a timer.
I actually think that rolling out a custom animation is beneficial since it allows for callbacks and more powerful customisation options.
Once I'm happy with it and I feel like it's ready to be shared I will post the class(es) on GitHub.

Accessing an object outside scope in a controller class

In my controller class, I initialize two instances of a model class (whose header is properly imported into controller class) with an NSButton. The model is really simple, just 4 members and one method - attack(). Making a silly text game!
- (IBAction)startGame:(id)sender {
Combatant *hero = [[Combatant alloc] init];
Combatant *enemy = [[Combatant alloc] init];
[console insertText:#"You have created a hero! An enemy approaches...\n"];
}
So now I have these two objects sitting there. Or do I? Because this other button, the one that's supposed to make them fight, has no idea what hero and enemy are, or that they have a class method that makes em' fight!
- (IBAction)attack:(id)sender{
[hero attack:enemy]; //Use of undeclared identifier, blah blah.
[console insertText:#"You attack the enemy! Woah!\n"];}
I get that if I initialized those objects in the attack method, then I could use them, so I gather this is something to do with scope. But I don't like the idea of sending model objects to controller methods, that seems silly.
Let me apologize: yes, this is a stupid, high-level question about the structure of Cocoa. Sorry. But I figure one of you will know exactly what I am not doing and tell me to do it!
In short, what is the Cocoa way of doing things in this situation? Thanks in advance.
-Alec
When you declare a variable in a method, it is a local variable, which means it only exists in that method. The same goes for variables you declare in functions.
If you want the variable to exist in all instance methods in the class, you need to make it an instance variable, which you do by declaring it in that { … } section in the class's #interface.
Note that any objects you store in instance variables, the instance should own. This means three things:
You'll need to either retain the object (and thereby own it) or make a copy (which you will then own) before assigning it to the instance variable.
Since you own it, you'll need to release it in the instance's dealloc method.
If you decide to replace it with a different object, you'll need to release the former object (since you still own it) and retain or copy the new object (in order to own it).
See the Objective-C Programming Language and the Memory Management Programming Guide for more information.

retain C array in objective-c

i create a c array in the firstClass (this is created in the interface):
BOOL taken[25];
i then go to a different view and come back to the first one, except my c array is reset to 0,
how do i retain my array when i go back an forth between views?
You cannot send retain messages to plain C arrays. Normal C memory management applies. I.e. local stack variables will fade away when out of scope, global variables will live, etc. Use dynamically allocated memory (malloc or new in C++) if you need "long-living" memory, but you are responsible to free it when you're done with it.
The lifetime of an immediate array like "BOOL taken[25]" is the same as the lifetime of the object that it is in. If the surrounding object gets deallocated the array goes with it; conversely, if the surrounding object is retained then so is the array. So to keep this array around, make sure the view is not deallocated, and make sure it's the same view object as you used last time.
In terms of iOS view control in particular (which is I think what you're asking about), try to write your business logic in your "view controller", and have it re-use UIView objects. Or, if you don't re-use the same view, at least have it initialize the new view (including the array) to the correct value.