the SET methods in iOS when I enable ARC - ios

Without ARC I write the set methods like this
- (void)setArr:(NSMutableArray *)arr
{
_arr = [arr retain];
}
but Xcode tell me "'retain' is unavailable: not available in automatic reference counting mode" when enable ARC.

_arr = arr; is all that is necessary. But practically, based on your code sample, there is no need to write the setter yourself. Simply declaring #property NSMutableArray *arr; in your header file is enough for the compiler to generate the setter and getter. When compiling with ARC enabled, retain and release calls are generated by the compiler for you.

When using arc, all you have to do is let the system handle the retain/release, this means:
- (void)setArr:(NSMutableArray *)arr
{
_arr = arr;
}
However, if your setter is as simple as this its just easier to let the compiler synthesize it.
Previously you had to use #synthesize, but now declaring it as a property on the header is enough.
#property (strong, nonatomic) NSMutableArray *arr;

Related

Apple code example, why are they accessing ivars directly here?

Looking at the Apple example application for MultipeerGroupChat (specifically MainViewController.m):
https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Listings/AdhocGroupChat_MainViewController_m.html#//apple_ref/doc/uid/DTS40013691-AdhocGroupChat_MainViewController_m-DontLinkElementID_8
The example contains the below code assigning properties:
#property (retain, nonatomic) NSMutableArray *transcripts;
#property (retain, nonatomic) NSMutableDictionary *imageNameIndex;
and then initializing them in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Init transcripts array to use as table view data source
_transcripts = [NSMutableArray new];
_imageNameIndex = [NSMutableDictionary new];
---SNIP---
Is there a reason they are assigning directly to the ivars _transcripts and _imageNameIndex? I thought proper convention dictates that you always use the properties to access the variables unless you are in an init method...indeed, a little further in the viewDidLoad method the author does use properties to assign other variables:
self.displayName = [defaults objectForKey:kNSDefaultDisplayName];
self.serviceType = [defaults objectForKey:kNSDefaultServiceType];
I know Apple example code has, in the past, sometimes strayed from good coding practices, so I'm wondering if this is just sloppy coding or if there is a valid reason to access those two ivars directly in a non-initialization method.
There is no definitive way for how properties have to be accessed. Seeing as this is just a demo project, the engineer who wrote this probably knew the scope of the project allowed for some shortcuts.
What's been done here isn't wrong, it just isn't recommended.

C-array as property in iOS

I am programming for iOS, and using ARC.
I am trying to use a c-array as property, but it reports error.
#property (strong, nonatomic)NSString *mappingTable[70][254];
The error is "Property cannot have array or function type NSString *[70][254]". How can I solve this problem? How can I declare c-array as property?
Note:
This is a two dimensional array, I think it is much easier to just use c-array, so I didn't use NSArray for it.
Surprised this hasn't been suggested already but you can store the c-array in an NSData object. I just used this method to store an array of frames.
#property (nonatomic) NSData *framesArray;
// Create and initialize your c-style frames array
CGRect frames[numberOfFrames];
...
self.framesArray = [NSData dataWithBytes:frames length:(sizeof(CGRect) * numberOfFrames)];
// To Access the property
NSUInteger arraySize = [self.framesArray length] / sizeof(CGRect);
CGRect *frames = (CGRect *) [self.framesArray bytes];
You can't declare it in that format. As the error message states you can't use C-style arrays in property declarations.
The new shorter syntax for arrays makes NSArray and NSMutableArray less of a pain. Instead of
[array objectAtIndex:3]
you can simply use
array[3]
I think in the long run the benefit of using Objective-C objects will outweigh the comfort of using C-style arrays.
you can not declare c/c++ arrays as properties, you could either use objective-c NSArray/NSMutableArray for property or you could declare c++ array.
#property (strong,nonatomic)NSArray *mappingTable;
or declare pure c style character array like this
char mappingTable[70][224];
If you are only going to use it as a private property of the class. Then keep it simple.
skip the YourClass.h file. And write it directly in the YourClass.m file like this.
//YourClass.m file
#import "YourClass.h"
#interface YourClass()
#property (strong,nonatomic)NSArray *mappingTable;
#end
#implementation YourClass
#synthesize mappingTable;
#end

Memory management, things to be clear

I need to get things clear about Objective-C memory management:
If I declare an object in the class header as ivar without #property:
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
and some where in the code for example in - (void)viewDidLoad I do :
tableController = [[TableViewController alloc] init];
so where is best way to release it. What if I make the instant object a property what will be the different? and how the memory management will be too
#interface MyFacebooDelegate : UIViewController
{
TableViewController *tableController;
}
...
#end
#property (nonatomic, strong) TableViewController *tableController;
What the following syntax do exactly for the object viewController:
.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) ViewController *viewController;
#end
.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
.....
#end
If I want to return an object through a method to another class, do I need to autorelease it in the method body first and then retain it in receiver side?
for example this method what exactly to do in the method body and in the receiver side too:
-(NSString *)getFriendId
{
NSArray *ar = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
return [ar objectAtIndex:0];
}
I know this a lot but I am really confused and need your help.
1) best way is in dealloc; or right before re-setting it.
2) a property does the retain/release for you. But WARNING! You keep mixing up things. You use "strong" here, which relates to ARC. If you really insist on using classic retain/release (you shouldn't) then use (nonatomic, retain) instead.
3) Your properties get deallocated on dealloc. Again, strong is wrong here.
4) Yes. Ideally you should. Another reason why ARC is awesome, it does this all for you, automatically.
tl;dr: Use ARC. Never go back. (But still learn manual memory management)
ARC is the answer for your all memory management question. Very import note on Strong and Weak property in addition to ,
iOS Strong property: So strong is the same as retain in a property declaration before ARC. For ARC projects I would use strong instead of retain, I would use assign for C primitive properties.
iOS outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because: Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

NSString working once then not working

This is my first time using this site and I am quite new to Objective-c. I'm sure this is a simple question but for some reason I am having a lot of issues. The app is designed to have the user enter a string via textfield, then it will pick the rest of the sentence and display it. The issue appears to be that my *name will be retained after the keyboard method and work once in the changelabel method. Then if i press the button again, invoking the changelabel method, the name appears to have been released and crashes the app.
#import
#import "Array.h"
#interface RandomBoredViewController : UIViewController {
UILabel *label;
UIButton *button;
UITextField *textField;
Array *array;
NSString *name;
NSString *description;
NSMutableString *whole;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) IBOutlet UIButton *button;
#property (nonatomic, retain) IBOutlet UITextField *textField;
#property (nonatomic, retain) Array *array;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSMutableString *whole;
-(IBAction) keyBoard;
-(IBAction) changeLabel;
#end
and my .m
#import "RandomBoredViewController.h"
#implementation RandomBoredViewController
#synthesize label;
#synthesize checker;
#synthesize button;
#synthesize textField;
#synthesize array;
#synthesize name;
#synthesize description;
#synthesize whole;
-(IBAction) changeLabel {
NSLog(#"Button being pushed");
description = [array getString];
NSLog(#"%#",description);
NSLog(#"%#",name);
name = [NSString stringWithString:name];
whole = [NSMutableString stringWithString:name];
NSLog(#"%#",name);
NSLog(#"%#",whole);
[whole appendString:description];
NSLog(#"%#",name);
NSLog(#"%#",whole);
label.text = whole;
NSLog(#"%#",name);
}
-(IBAction) keyBoard {
name = [NSString stringWithString:textField.text];
NSLog(#"%#",name);
label.text = [NSString stringWithString: name];
[textField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
array = [[Array alloc]init];
[array createArray];
NSLog(#"%i",[array arrayCount]);
whole = [[NSMutableString alloc]init];
name = [[NSString alloc]init];
}
- (void)dealloc {
[super dealloc];
[label release];
[button release];
[textField release];
[array release];
//[name release];
[description release];
}
#end
You are setting name to an autoreleased instance of NSString, this is probably what's causing your app to crash.
Use
self.name = [NSString stringWithString:textField.text];
Your synthesized mutator will retain the NSString and prevent it from being released.
Taking one thing in microcosm, the code you've posted creates two things named name — an instance variable and a property.
Instance variables are directly accessed storage. They have no behaviour.
Properties are named attributes accessed via getters and setters. So they may have arbitrary behaviour. They may report the values of instance variables or values calculated from instance variables or values calculated or obtained by any other means. Relevantly, the setters may retain, assign or act in any other way.
Instance variables may be accessed only by the instance of a class they belong to. Properties are usually intended to be accessed by anyone.
Since retaining is a behaviour and you've ascribed it to your name property, setting something to it would result in a retain. Instance variables can't have behaviour, so setting a value to it doesn't result in a retain or anything else.
As a result, this line:
name = [NSString stringWithString:name];
Creates a new string and returns a non-owning reference. Which means it'll definitely last for the duration of this autorelease pool (ie, you explicitly may pass it as an argument or return it safely, assuming you haven't taken manual control of your autorelease pools).
You store that reference to your instance variable. Instance variables don't have behaviour so the reference is stored but you still don't own that object. It's still only safe to use for the duration of that autorelease pool.
So when you access it in that method it's safe. When you access it later it's unsafe.
If instead you'd gone with:
self.name = [NSString stringWithString:name];
Then you'd have set that string to be the new value of the property. Because your property has the retain behaviour, you'd subsequently have completely safe access to the string object, until you say otherwise.
Because you've got a property with exactly the same name as an instance variable, you could subsequently access it either as just name or as self.name. Similarly you could have stored directly to the instance variable rather than via the property if you'd ensured you had an owning reference manually.
As suggested above, use of ARC is a way to get the compiler to figure all this stuff out for you.
That issue is what causes your code to crash — you end up trying to access a reference that has ceased to be valid. If you'd taken ownership of it then it would have continued to exist at least as long as you kept ownership.
try using self.name
sometimes this stuff confuses me as well and for that you might want to consider using arc in which case most of this stuff can be avoided.
when using properties you should always use self.propertyName vs propertyName (only), it uses the accessors (get propertyName, set propertyName) as opposed to directly accessing that pointers value.
take in mind there are 2 exceptions to the rule, init and dealloc which should NOT use self.
self.name = [NSString stringWithString:name];
you technically should also have an init method
to initialize your variables, and i believe you should call [super dealloc] last not first in your dealloc method, but thats not your problem and might not matter (just what I do when I dont use arc)
When you change your instance variable in changeLabel, you should release the previous value and retain the new one. You may use the accessors to perform the memory management stuff for you. Also, I think you should invoke [super dealloc] after releasing the instance variables in your implementation of dealloc.
If you're not familiar with Cocoa memory management (and even if you are), the best is to enable ARC (Automatic Reference Counting) and let the compiler deal with it.

iOS: Should I release object in dealloc function?

Im very confused with the memory management.
Declared variable allNoticeArray in .h file:
#interface NoticeViewController : UITableViewController
{
NSMutableArray *allNoticeArray;
}
#property (nonatomic, retain) NSMutableArray *allNoticeArray;
#end
Alloc and init the variable in .m file:
#implementation NoticeViewController
#synthesize allNoticeArray;
- (void)viewDidLoad
{
[super viewDidLoad];
self.allNoticeArray = [[[NSMutableArray alloc] init] autorelease];
}
- (void)dealloc
{
[super dealloc];
/*
***should I release allNoticeArray here or not?***
*/
//[allNoticeArray release];
}
Should I release the allNoticeArray in dealloc function or not?
Thank you in advance!
It looks like you're manually managing your memory rather than using ARC.
If you're using IOS5 it might be easier for you to convert your project to ARC, then you won't have to worry about dealloc in this context.
If you don't want to use ARC you do need to release it in this context because you alloc'd it in viewDidLoad. You might also be interested in this article about dealloc.
Yes, you have to release the object. You could do the below in your dealloc method which will release your object.
self.allNoticeArray = nil;
REASON:
Although you have autoreleased the array you have declared your property as retain. So the object will be retained and used. So to totally remove the object from memory you should again call release over it.
You can learn everything about memory management here https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
When not using ARC you should use the release in the dealloc. You are right in saying that your retain in the .h file increases the retain count by one. The Alloc/Init creates an object with a retain count of one. The auto release would counter that retain, however your dealloc release would count the retain in the .h.
Setting self.allNoticeArray=nil; is not the same as a release, but the link from sElan is a great link.
The rule of pre-ARC memory management is this: if you alloc, retain, or copy an object you must release it later or it will leak. Your property is set to retain, so whatever value it holds will be retained. You must balance it with a release in dealloc.
The short answer is NO, but you are doing a few things that are not recommended. In your code you are retaining your array twice in the single line. In the code below it is only retain once.
You should initialize the array like so -
NSMutableArray *array = [[NSMutableArray alloc] init] autorelease];
self.allNoticeArray = array;
I would recommend you look into using ARC, it makes memory management a non issue in IOS 5.

Resources