I think I'm being a bone head here...
The static analyzer complains in the -(void)doSomethingInteresting method:
Potential leak of an object stored into myAddressBook
Well, sure, that makes sense; I'm calling a create function without a matching release. But, I don't think I want to release myAddressBook at the end of -doSomethingInteresting because I want it to stick around for the life of the AwesomeObject instance.
AwesomeObject.h
#interface AwesomeObject : NSObject
#property (assign, nonatomic) ABAddressBookRef addressBook;
#end
AwesomeObject.m
#implementation AwesomeObject
- (void)doSomethingInteresting
{
CFErrorRef error = NULL;
ABAddressBookRef myAddressBook = ABAddressBookCreateWithOptions(NULL, &error);
[self setAddressBook:myAddressBook];
}
#end
EDIT: Screen shot attached
The analyzer doesn't understand you wish to assign a retained address book in your property, and thus not release it at the end of doSomethingInteresting.
Change your property to:
#property (strong, nonatomic) id addressBook;
and set it like so:
[self setAddressBook:CFBridgingRelease(addressBook)];
Now, when you want to use the addressBook property, use the (__bridge ABAddressBookRef) cast.
Also, you need to release a potential CFErrorRef object, which may have been created during ABAddressBookCreateWithOptions:
if(error)
{
CFRelease(error);
error = NULL;
}
You need to call it like this:
[self setAddressBook:CFBridgingRelease(myAddressBook)];
Related
Very simple question. Is it possible to create a class which is a list by it self? I mean:
I do
taskList *taskList1 = [[taskList alloc] init];
And than simply:
taskList1 addObject:[task1]
May seem stupid, but I'm totally new to O-C syntax
I'd need two methods:
-(instancetype) init;
which just initialize as an empty list
+(instancetype)taskList;
to allocate taskList instance
and last thing:
In interface i use:
#interface taskList : NSObject
or
#interface taskList : NSMuttableArray
I got stuck on something specific, didn't I? I'm sorry that I bother you with my programming level.
Alright, I gave up, just last question, because I have to finish it very soon.
I changed my approach I added
#property NSMutableArray *list;
Why does this:
taskList *TL1 =[taskList initTaskList];
task *task1 = [[task alloc] init];
task *task2 = [[task alloc] init];
TL1.list addObject:[task1];
doesn't work, I have "Expected identifier" Error
If you read the subclassing notes on NSArray / NSMutableArray you'll see that Apple recommend against subclassing them because they are a class cluster. (i.e. what you really get when you ask for one is an undocumented subclass, and the initialiser decides which undocumented subclass to return to you based on some undocumented qualifiers..
So just make an NSObject subclass which owns a (private) strong property of type NSMutableArray, and publish an api to access that array..
eg
#import "modelList.h"
//dont worry header is empty, its up to you to do that.. this is a subclass on NSObject
#interface modelList()
#property (strong, nonatomic) NSMutableArray *backingArray;
#end
#implementation modelList
#synthesize backingArray = _backingArray;
-(instancetype )init{
if (self = [super init]) {
[self setBackingArray:[[NSMutableArray alloc]init]];
}
return self;
}
//public API (the stuff that is prototyped in the header..)
-(id)objectAtIndex:(NSUInteger )index{
return [self.backingArray objectAtIndex:index];
}
-(BOOL )containsObject:(id)object{
return [self.backingArray containsObject:object];
}
-(void )addObject:(id)object{
//example application, qualifying object..
if ([object conformsToProtocol:#protocol(NSCoding)]) {
[self.backingArray addObject:object];
}
}
-(NSUInteger )count{
return [self.backingArray count];
}
//etc etc declare publicly the things you need to get the job done
#end
so far this is just a face for a mutable array obviously, but it gives you a place for whatever other model logic you need. good luck
I tried initializing the array :
In .h file
#property (nonatomic, retain) NSArray *accounts;
In .m file :
#synthesize accounts;
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *arrList = [acAccountStore accountsWithAccountType:accountType];
// This returns array
self.accounts = [NSArray arrayWithArray:arrList]; // I tried debug after
// this and it gives me data in debugger.
// Note array List have 3 data in it.
}
Now On button click i call a method:
- (IBAction) ButtonClicked :(id) sender {
NSLog(#" data : %#",[self.accounts objectAtIndex:0]); // Breaks at this point.
// When i tried with debug it gives me (no Objective-C description available)
}
Is the initialization of array correct Or If the code is not right please let me know.
Main concern is when i do debug in viewDidLoad, the self.accounts show me proper values. But after doing the click event its empty and throws EXEC_BAD_ACCESS error.
Thanks for help in advance
hm looks fine. A couple of questions then:
Where are you calling the self.accounts = [NSArray arrayWithArray:arrList];
I assume that the array is being setup before your button is being pressed?
There's no real reason that arc should be clearing out the variable. Have you set a strong reference to it or a weak one? If you're using self. on a variable, you should have :
#property (nonatomic, strong) NSArray *accounts;
or similar to that in the .h file and then
#synthesize accounts;
in the .m file.
If you've got weak instead of strong then ARC may possibly clear the memory but it still shouldn't.
Update:
Create a property for your account store as well. I had this exact issue recently and this fixed it.
#property (nonatomic, strong) ACAccountStore *accountStore;
Original Answer
Because you're using ARC, you need to change your property declaration from
#property (nonatomic, retain) NSArray *accounts;
to:
#property (nonatomic, strong) NSArray *accounts;
With the latest LLVM compiler, you don't need to synthesize properties either. So you can remove #synthesize accounts.
You should always use defensive coding as well, so in your - buttonClicked: method, you should do:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts) {
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
This makes sure that the pointer to the array is valid.
You can also check to make sure an item in an array exists before trying to read it by doing:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts.count > 0)
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
I have a doubt about initializing string with synthesize keyword.
In my Event.h class I have
#interface Event : NSObject {
NSString *title;
}
#property (nonatomic, retain) NSString *title;
and in Event.h I have
#synthesize title;
However when I want to set the title from my main class and I display the content in the console, I get null:
[self.currentEvent.title stringByAppendingString:#"hello"];
NSLog(#"%#", self.currentEvent.title); //this is null
Is because I don't properly initialize the title variable in Event? Isn't synthesize initializing it for me?
[self.currentEvent.title stringByAppendingString:#"hello"];
You call stringByAppendingString: on a null object (since it was never initialized), so it doesn't do anything. Plus, even if it were to return something, you're not storing the return value anywhere.
if(self.currentEvent.title==nil){
self.currentEvent.title = #"hello";
}
else{
self.currentEvent.title = [self.currentEvent.title stringByAppendingString:#"hello"];
}
#synthesize creates the setter and getter methods for you, but does not initialize
Fastest way to get up to speed with this stuff is to watch "Developing Apps for iOS" by Paul Hegarty / Stanford University, available free on iTunes.
You are not storing the result of your call into a variable. I also suggest using this method since it's a little bit cleaner because you do not need to have an if statement.
[self setTitle:[NSString stringWithFormat:#"hello %#", [self title]]];
I'm not sure I understood how alloc and retain work.
Recently I discovered that the NSString properties were not retained and I had to add [myString copy] when I set them. Which makes me wonder if I misunderstood the whole way of using retain/alloc
Please, may someone tell me if I'm doing it correctly? I read a lot and had a look on open source projects, this let me thing that I may have been wrong since the beginning.
Here is my way of doing it:
/**** VIEW.h *****/
#import "MyClass.h"
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
#property (nonatomic, retain) MyClass *myObject;
- (void)defineObject;
#end
.
/**** VIEW.m *****/
#import "VIEW.h"
#implementation MyViewController
#dynamic myObject;
- (void)viewDidLoad
{
[super viewDidLoad];
[self defineObject];
NSLog(#"My object's name is: %#", myObject.name);
}
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
.
/**** MyClass.h *****/
#interface MyClass : NSObject {
//not sure if this line is still necessary
NSString *name;
}
#property (nonatomic, retain) NSString *name;
- (id)initPersonalised;
- (void)setName:(NSString *)name;
- (NSString *)name;
#end
.
/**** MyClass.m *****/
#import "MyClass.h"
#implementation MyClass
#dynamic name;
(id)initPersonalised{
self = [super init];
name = #"Undefined";
}
- (void)setName:(NSString *)name{
self.name = [name copy];
}
- (NSString *)name{
return [self.name copy];
}
#end
I hope you can bring a bit of light, after months of programming this way, I'm less and less sure of doing it well.
This is indeed a topic that every Objective C programmer stumbles upon. There are a few things one needs to know:
Instance variable vs. property access
Within MyViewController,
myObject = xxx;
and
self.myObject = xxx;
are two different things. The first directly assigns to the instance variable and does neither release to old referenced insance nor retain the newly assigned instance. The latter one uses the property setter and thus releases the old and retains the new value.
Deallocation
Even when you have declared an implemented a property that takes care of retaining and releases the values, it won't take care of deallocation when your object (MyViewController in your case) is released. So you must explicitly release it in dealloc:
-(void) dealloc {
[myObject release];
[super dealloc];
}
Now to your code:
The snippet:
myObject = [[MyClass alloc] initPersonalised];
is perfectly okay. When you create an object, you use the pair of alloc and initXXX. The always create an instance with the reference count set to 1. So by directly assigning it to the instance variable, you create a clean constellation. I don't see no other way of creating the instance.
In MyClass you could use #synthesize name instead of #dynamic. Then the compiler would implement name and setName: automatically and you wouldn't need to do it yourself.
Finally, your missing dealloc.
Update:
If you use:
self.myObject = [[MyClass alloc] initPersonalised];
then you have a memory leak because initPesonalised sets the reference count to 1 and the setter of myObject increases it to two. If you want to use the setter, then I has to be:
MyClass* mo = [[MyClass alloc] initPersonalised];
self.myObject = [[MyClass alloc] initPersonalised];
[mo release];
It would be different if you weren't using initXXX to create a new instance. The class NSString for example has many methods called stringXXX, which create a new instance (or return a shared one) that has (conceptually) a reference count of 1 that will later automatically decreased by one. Then you better use the setter:
self.name = [NSString stringWithFormat: #"instance %d", cnt];
If you want to use copy instead of retain for your string property (which is good practice), then you can simply declare your property like this:
#property (nonatomic, copy) NSString *name;
When you then use #synthesize to implement the getter and setter, the compiler will generate them using copy instead of retain.
And NSString *name; is necessary even if you use #property and/or #synthesize to implement the property.
Alloc and init are methods that always go hand-in-hand. alloc allocates space for your object, and init initializes your object to some value. When you call alloc, you are responsible for freeing that object later. If you call copy, you are also responsible for releasing that object later. It's considered good practice to always initialize your objects right after you allocate them.
Now, to answer the questions I found in your code.
#interface MyViewController : UIViewController {
//Is the following line really necessary?
MyClass *myObject;
}
So is that line necessary? That depends. Does it make sense that your object has a MyClass as a property? This is a question only you can answer based on your design. I recommend you to study Object-Oriented Programming in more depth.
- (void)defineObject
{
//Here particularly, Why doesn't it work without both alloc and init
//shouldn't "#property (nonatomic, retain) MyClass *myObject;" have done that already?
myObject = [[MyClass alloc] initPersonalised];
[myObject setName:#"my name"];
}
Not necessarily. You are just providing a pointer to an object of the specified kind. The moment you set your property, depending on the property modifiers, your class will know what to do with MyObject.
In that way, there's no need to call [yourObject copy]. In this way your properties will be copied instead of being retained. Just don't forget to release it later in your -dealloc method, like you would with retain properties.
All in all, this is what I recommend you to study a bit more:
Object-Oriented Programming (not related to your issue, but I can tell you are not comfortable using it. Objective-C is heavily object oriented, so you want to understand OOP).
iOS Memory Management.
You can have a look at the Memory Management Guide. It will help you to better understand the alloc & retain concepts; hope this helps you.
If I create a new object that includes two object pointers (see below) when the object is created the pointers are set to point to nil;
#interface rocketShip : NSObject {
NSString *name;
NSNumber *thrust;
}
If (for some unexpected reason) I don't assign these pointers and later release them in my dealloc method is that ok, I am pretty sure it is, just wanted to check?
- (void)dealloc{
[name release];
name = nil;
[thrust release];
thrust = nil;
[super dealloc];
}
gary
Sending a message to nil won't cause an error, so this is fine. You need to make sure the pointers are actually nil though - sending a message to a garbage pointer will likely cause errors.
Yes, you can do this because you can safely send a message (such as release) to nil and it will just do nothing.
If you use the accessor property to create your get/set methods:
#interface rocketShip : NSObject {
NSString *name;
NSNumber *thrust;
}
#property (retain, nonatomic) NSString *name;
#property (retain, nonatomic) NSNumber *thrust;
And in your .m file:
#synthesize name;
#synthesize thrust;
You can then just set your variable to nil in dealloc. This will in fact call your setter and decrement the reference count by one and clean things up.
It’s important in Objective-C to distinguish between objects and variables. You cannot release a pointer (the value of a variable), you can only release an object (the object to which a pointer refers). A nil pointer refers to no object, so messages sent to nil do nothing (which is generally safe).