UPDATE: I thought the default behavior under ARC is assign, but it is strong. So, don't bother reading the question, it is useless :)
Consider the following code:
#import "AppDelegate.h"
#interface TestClass: NSObject
#property (atomic) NSMutableArray *strings; //"assign" by default
#end
#implementation TestClass
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
TestClass *testObject = [TestClass new];
testObject.strings = [[NSMutableArray alloc] init];
//(*) Why isn't strings array deallocated here under ARC? There are no strong references to it.
[testObject.strings addObject:#"str1"];
[testObject.strings addObject:#"str2"];
[testObject.strings addObject:#"str3"];
NSLog(#"Strings: %#", testObject.strings);
return YES;
}
#end
Here strings property is declared as assign (by default). So, if I'm not mistaken, there are no strong references to this array in the code at all. So, from my point of view strings should be deallocated at (*). However, the code works and prints the array.
Why? My possible explanation is that there are some implementation details related to the NSMutableArray, so there are some internal references left to the strings array, so it remains valid. So it is just pure luck. Am I right? I've tricked the system into returning retainCount, it was equal to 2 at the point of NSLog.
If I change the property declaration to (atomic, weak), the array is nil as expected.
I use Xcode 7.1.1 (7B1005) with OS X 10.11.2 (15C30). I checked the DEBUG version in the iOS simulator.
I found a code like this on the internet, and expected it to crash, but it didn't. Hence the question.
Under ARC the default ownership is strong not assign, see the Clang ARC documentation for details.
Related
I'm trying to figure out why my NSTextFields are only retained in the first method sendVarsToButton but not in the updateTotal method. I need to access the values from the TextField set in the first method but I can't because it seems like my IBOutlets deallocate themselves after the sendVarsToButton method. Can you help me please!?
Here's my .h
#import <Cocoa/Cocoa.h>
#import "TransactionViewController.h"
#class TransactionButtonModel;
#interface TransactionButtonController : TransactionViewController
{
NSMutableArray *buttonsArrays;
TransactionViewController *transactionViewController;
TransactionButtonModel *transactionButtonModel;
}
#property(nonatomic,retain) IBOutlet NSTextField *nom;
#property(nonatomic,retain) IBOutlet NSTextField *descriptionText;
#property(nonatomic,retain) IBOutlet NSTextField *prix;
#property(nonatomic,retain) IBOutlet NSTextField *CPUField;
#property(nonatomic,retain) IBOutlet NSTextField *quantite;
#property(nonatomic,retain) IBOutlet NSTextField *total;
-(void)sendVarsToButton:(NSString *)name:(NSString *)description:(double)price:(double)CPU:(long)tag;
-(void)updateTotal:(int)newQuantity;
-(void)addQuantiteToExistingProduct:(long)tag;
-(IBAction)removeProductFromView:(id)sender;
Here's my .m
#import "TransactionButtonController.h"
#import "TransactionViewController.h"
#import "TransactionButtonModel.h"
#implementation TransactionButtonController
#synthesize prix;
#synthesize nom;
#synthesize descriptionText;
#synthesize CPUField;
#synthesize total;
#synthesize quantite;
//In this method, everything works fine
-(void)sendVarsToButton:(NSString *)name :(NSString *)description :(double)price :(double)CPU:(long)tag
{
[nom setTag:tag];
[descriptionText setTag:tag];
[prix setTag:tag];
[CPUField setTag:tag];
[quantite setTag:tag];
[total setTag:tag];
nom.stringValue = name;
descriptionText.stringValue = description;
[prix setDoubleValue : price];
CPUField.doubleValue = CPU;
total.doubleValue = [TransactionButtonModel calculateButtonTotal:quantite.intValue :prix.doubleValue];
NSLog(#"retain! :%lu",[[prix viewWithTag:tag] retainCount]); // returns 2
[transactionButtonModel release];
}
-(void)updateTotal:(int)newQuantity
{
NSLog(#"retain! :%lu",[[prix viewWithTag:2] retainCount]); //returns 0
[total setDoubleValue:[TransactionButtonModel calculateButtonTotal:newQuantity :prix.doubleValue]]; // value of prix = 0 and prix = null
NSLog(#"Updated! :%i",newQuantity);
}
-(void)dealloc
{
[nom release];
[quantite release];
[prix release];
[total release];
[descriptionText release];
}
Thanks in advance.
It sounds like you have a few problems here. In no particular order:
You're releasing transactionButtonModel in sendVarsToButton::::: even though you didn't create it there and there's no particularly obvious reason you'd want to. This seems like probably a memory management error, but it's hard to say without context.
You're looking to the reference counting mechanisms to see why a variable is null. Overreleasing an object does not set variables referencing that object to null — it would just be a junk pointer and probably crash your program. The most likely reasons for a variable to be unexpectedly null are a) methods running in a different order than you expect, or b) having two different instances of your class that you're treating as though they're the same one. My money would be on B in this case. You're probably creating two instances of this class, one of which is actually showing the view and the other being essentially "blank." Try logging self in the two methods and see if it's the same object.
In one method you're logging viewWithTag:tag and in the other you're logging viewWithTag:2 — it's not necessarily a safe assumption that tag is 2.
prix is an NSTextField — why are you asking it for subviews? NSTextField isn't generally expected to have any useful subviews. There seems to be something odd in this design.
Your method names are nuts with all those bare colons. It's hard to read and often leads to mistakes down the line (due to the likelihood of misreading the code when you're not "in the moment" and the fact that it violates the language's idioms).
You're depending on retainCount to track memory management. The value returned by retainCount is questionable at best and often downright deceptive because things get retained and autoreleased all the time, and retainCount won't give you enough information to account for that. If you have a memory management problem (which doesn't appear to be this case with your variable becoming nil), a good approach would be to use Instruments and the debugger to track it down, not random retainCount logging.
I have a pretty simple setup for this unit test. I have a class that has a delegate property:
#interface MyClass : NSObject
...
#property (nonatomic, weak) id<MyDelegateProtocol> connectionDelegate;
...
#end
and I set the delegate in my test:
- (void)testMyMethod_WithDelegate {
id delegate = mockDelegateHelper(); // uses OCMock to create a mock object
[[delegate expect] someMethod];
myClassIvar.connectionDelegate = delegate;
[myClass someOtherMethod];
STAssertNoThrow([delegate verify], #"should have called someMethod on delegate.");
}
But the delegate is not actually set on line 3 of my unit test, so #someMethod is never called. When I change it to
myClassIvar.connectionDelegate = delegate;
STAssertNotNil(myClassIvar.connectionDelegate, #"delegate should not be nil");
it fails there. I'm using ARC, so my hunch was that the weak property was being deallocated. Sure enough, changing it to strong makes the STAssertNotNil pass. But I don't want to do that with a delegate, and I don't understand why that makes a difference here. From what I've read, all local references in ARC are strong, and STAssertNotNil(delegate) passes. Why is my weak delegate property nil when the same object in a local variable is not?
This is a bug in the iOS runtime. The following discussion has more detail. In a nutshell, the iOS ARC runtime can't seem to handle weak references to proxies. The OSX runtime can.
http://www.mulle-kybernetik.com/forum/viewtopic.php?f=4&t=252
As far as I understand from the discussion a bug report has been filed with Apple. If anyone has a sensible idea for a workaround...
I don't really know what's happening here, but OCMock returns an autoreleased NSProxy-descendant from the mockForProtocol: method, which I think is right. Maybe ARC has problems with NSProxies? Anyway, I've overcome this problem by declaring the variable __weak:
- (void)testMyMethod_WithDelegate {
// maybe you'll also need this modifier inside the helper
__weak id delegate = mockDelegateHelper();
...
It really doesn't need to be __strong (the default) in this case, as it's autoreleased and you're not keeping it around...
A workaround is to use Partial Mocks.
#interface TestMyDelegateProtocolDelegate : NSObject <MyDelegateProtocol>
#end
#implementation TestMyDelegateProtocolDelegate
- (void)someMethod {}
#end
#implementation SomeTest {
- (void)testMyMethod_WithDelegate {
id<MyDelegateProtocol> delegate = [[TestMyDelegateProtocolDelegate] alloc] init];
id delegateMock = [OCMockObject partialMockForObject:delegate]
[[[delegateMock expect] someMethod]
myClassIvar.connectionDelegate = delegate;
[myClass someOtherMethod];
STAssertNoThrow([delegate verify], #"should have called someMethod on delegate.");
}
#end
I am no ARC expert but my guess is that mockDelegateHelper() is returning a weak object. As a result delegate is nil before the second line of code executes. I would venture to guess that either the mockDelegateHelper() is the culprit or that OCMock is getting in the way with how it manipulates and creates objects.
I have several ivar NSArrays that I initialize in my -viewDidLoad method. One contains strings, one contains IBOutlets. However, when I initialize, all of the objects in the array are out of scope, and the memory address is 0x0 (according to the Xcode debugger). However, when I have a local NSArray with the same objects, it works fine. Initializing an ivar NSString or NSDictionary both work fine.
The code:
//.h file
#import <UIKit/UIKit.h>
#interface myViewController : UIViewController
{
NSArray *myArray;
}
#end
//.m file
#import "myViewController.h"
#implementation myViewController
- (void)viewDidLoad
{
[super viewDidLoad];
myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
NSLog(#"myArray equals: %#.", myArray);
}
#end
When I try using the array, I get an EXC_BAD_ACCESS runtime error. Is this an Xcode bug, or am I missing something about NSArray? UPDATE: I am using ARC. After I turn ARC off and do a clean build, I no longer get this problem. Is this a bug in ARC?
I don't know what the problem was, but I switched to a stable version of Xcode (4.0.2) and I had NO problems at all. Thanks everyone for trying to help!
That you are using ARC is important to note when asking such a question.
How are you trying to use the array (show the code)? There is a known bug in certain versions of ARC (which can't be discussed on the iOS side, but the same bug is in the Lion release of ARC) where fast enumeration of a collection under ARC can cause a crash.
Sounds like the framework has not instantiated the IBOutlet instances yet. Can you hold off and populate the arrays in the viewWillAppear method? This will be called before the user sees anything. Otherwise pull them off of IB, just manage them manually and alloc them at whatever point you want.
Your sample code seems to be allocating a new (and local) version of myArray rather than setting the iVar you declare in your header file. Try changing:
NSArray *myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
to
myArray = [[NSArray alloc] initWithObjects:#"aString", #"another string", nil];
With ARC off, I would presume that any attempt you make to access myArray would do nothing because it would be set to nil and ignore all messages. I'm not sure why this code would work any differently with ARC enabled.
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.
I tend to use properties exclusively in my classes, especially now that you can declare properties in a class extension thanks to the modern Objective-C 2.0 runtime—I use this feature to create "private" properties.
My question is if there is any good reason to ever declare ivars in a class interface anymore. I prefer my public-facing interfaces to be as minimal and clean as possible, only revealing aspects of my class that are pertinent.
For example, I would tend to do the following:
MyClass.h:
#interface MyClass : NSObject
#property (nonatomic, copy) NSString * publicString;
#property (nonatomic, copy, readonly) NSString * readOnlyString;
#end
MyClass.m:
#interface MyClass ()
#property (nonatomic, copy, readwrite) NSString * readOnlyString;
#property (nonatomic, copy) NSString * privateString;
#end
#implementation MyClass
#synthesize publicString = publicString_;
#synthesize readOnlyString = readOnlyString_;
#synthesize privateString = privateString_;
- (void)init
{
self = [super init];
if (self != nil)
{
self.publicString = #"Public String";
self.readOnlyString = #"Read-Only String";
self.privateString = #"Private String";
}
return self;
}
- (void)dealloc
{
[publicString_ release];
[readOnlyString_ release];
[privateString_ release];
[super dealloc];
}
#end
Code style preferences aside, are there any issues with avoiding ivars entirely like this?
I may have found an answer that's suitable enough for me to explicitly back my properties with ivars. It doesn't appear as if the debugger will list any automatically synthesized ivars, so there's no way to just drill through self during debugging and check various values other than manually calling the property accessors, which is tedious. Unless they change this, this is probably more than enough reason for me to just go back to declaring ivars explicitly.
The main issue, if it bothers you at all, is that per Cocoa With Love, dynamic instance variables such as those you're using aren't supported by runtimes other than those for 64bit Intel/PowerPC (fixed per Chuck's comment below) and ARM (for iOS).
I'm not currently able to find an authoritative Apple document on the issue; note that restricting to the latest OS X, v10.6, is not sufficient since it is available for and supported on the 32bit Intel machines that Apple shipped immediately after switching from PowerPC.
Late extra thought: without knowing about any potential changes in Xcode 4, a good reason to declare otherwise private instance variables within the header file is to mark them as IBOutlets and wire them up graphically. That's really only relevant to a very specific type of class and member variable though, admittedly.
I have to agree with LucasTizma on the debugging issue.
When I began using XCode4, I started not explicitly declaring ivars and let them be created for me using #synthesize aVar = _aVar syntax. While trying to debug code, I noticed that I couldn't hover the cursor over the variable and see its value.
For me, this is just unacceptable. I guess it's back to declaring them explicitly.
Beyond Tommy's concern, declaring an ivar is certainly good practice, especially if your code might be reused or if you might come back to your code sometime.