I using Test Driven Development in Objective-C for iOS and Mac OS X development, and I want to be able to write tests that can verify that objects I create with class factory methods return autorelease objects.
How can someone write a test that verifies a provided object is autorelease?
In short, you can't. There is no way to know the autorelease state of an object.
In some cases, you can infer whether an object was placed in an autorelease pool. The idea is declaring a pointer to an object, instantiating it within an #autoreleasepool block, and then verifying that it had dealloc called after the end of the block.
Through whatever combination of swizzling or overriding dealloc you choose, you must first provide a way to verify that dealloc has been called. I wrote an NSObject category with the following interface and implementation, that provides a deallocationDelegate property that will receive a message of handleDeallocation: when the object is deallocated.
#interface NSObject (FunTimes)
#property (nonatomic, assign) id deallocationDelegate;
#end
#implementation NSObject (FunTimes)
+ (void)load
{
Class klass = [NSObject class];
SEL originalSelector = #selector(dealloc);
Method originalMethod = class_getInstanceMethod(klass, originalSelector);
SEL replacementSelector = #selector(funDealloc);
Method replacementMethod = class_getInstanceMethod(klass, replacementSelector);
if(class_addMethod(klass, originalSelector, method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod)))
{
class_replaceMethod(klass, replacementSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else
{
method_exchangeImplementations(originalMethod, replacementMethod);
}
}
- (void)funDealloc
{
if (self.deallocationDelegate)
[self.deallocationDelegate performSelector:#selector(handleDeallocation:) withObject:self];
[self funDealloc];
}
static char myKey;
- (void)setDeallocationDelegate:(id)deallocationDelegate
{
objc_setAssociatedObject(self, &myKey, deallocationDelegate, OBJC_ASSOCIATION_ASSIGN);
}
- (id)deallocationDelegate
{
return objc_getAssociatedObject(self, &myKey);
}
#end
I ran some test code in my application delegate just to see if it works. I declared an NSMutableArray instance designed to hold NSValue instances derived from the pointers of objects calling -handleDeallocation, which I implement as shown:
- (void)handleDeallocation:(id)toDie
{
NSValue *pointerValue = [NSValue valueWithPointer:toDie];
[self.deallocatedPointerValues addObject:pointerValue];
}
Now, here's a snippet of what I ran. SomeClass is an NSObject subclass with no additional properties or methods.
self.deallocatedPointerValues = [NSMutableArray array];
SomeClass *arsc = nil;
#autoreleasepool {
arsc = [[[SomeClass alloc] init] autorelease];
arsc.deallocationDelegate = self;
NSValue *prePointerValue = [NSValue valueWithPointer:arsc];
BOOL preDeallocated = [self.deallocatedPointerValues containsObject:prePointerValue];
NSLog(#"PreDeallocated should be no is %d",preDeallocated);
}
NSValue *postPointerValue = [NSValue valueWithPointer:arsc];
BOOL postDeallocated = [self.deallocatedPointerValues containsObject:postPointerValue];
NSLog(#"Post deallocated should be yes is %d",postDeallocated);
In this case, it can be verified that the object pointed to by arsc (which stands for auto released SomeClass) has been deallocated due to ending the #autoreleasepool block.
There are several significant limitations to the this approach. One, this cannot work when other messages of retain may be sent to your object that is returned from your factory method. Also, and this should go without saying, swizzling dealloc should only be done in experimental settings, and I think some would argue that it shouldn't be swizzled in testing (obviously it shouldn't be swizzled in production!). Finally, and more significantly, this doesn't work well with Foundation objects such as NSString that have been optimized in ways that it's not always clear whether you are creating a new instance or not. So this is most appropriate, if at all, for your own custom objects.
As a final word, I don't think it's practical to do this really. I felt it was more work than it was worth and is so narrowly applicable as the make spending time learning instruments better to be a far better investment when it comes to memory management. And, of course, with ARC's ascendency, this approach is archaic from the start. Regardless, if you do have need to write such tests, and can work around the limitations here, feel free to adapt this code. I'd be curious to see how it pans out in an actual testing environment.
I commend your dedication to TDD. But memory management is an area where you simply have to follow well-established conventions: "When returning an object, it needs to take care of its own lifetime." My unit tests catch me when I accidentally over-release something, but they won't catch a leak. For that, I rely first on Analyze, then on running the Leaks instrument.
Related
In iOS Programming Book from Big Nerd Ranch (3rd ed) they say on pg.194
..a knowledgeable programmer could still create an instance of BNRItemStore via allocWithZone:, which would bypass our sneaky alloc trap.To prevent this possibility, override allocWithZone: in BNRItemStore.m to return the single BNRItemStore instance.
+(id) allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
This statement seems confusing to me. Doesn't this following code not prove this wrong in a way-
#import <Foundation/Foundation.h>
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
+(id)retrieveObject;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
NSLog(#"Test2");
sharedStore= [[super allocWithZone:nil] init];
}
NSLog(#"sharedStore-> %#",sharedStore);
return sharedStore;
}
+(id)allocWithZone:(NSZone *)zone{
NSLog(#"Test1");
return [self sharedStore];
}
+(id)alloc{
NSLog(#"Retrieving super object");
NSLog(#"%#", [super allocWithZone:nil]);//Bypassing the subclass version of allocWithZone.
return [super allocWithZone:nil];
}
#end
int main(){
[[BNRItemStore alloc] init]; //the alloc message triggers a call to the subclass (overriding) version of +(id)alloc method
}
The output is:
2013-10-18 18:24:40.132 BNRItemStore[381:707] Retrieving super object
2013-10-18 18:24:40.134 BNRItemStore[381:707] BNRItemStore:0x7f8c72c091e0
If the call [super allocWithZone:nil] inside of subclass 'alloc' method would have triggered a call to subclass allocWithZone,the console would be logging "Test1" and "Test2" and finally would lead to static pointer getting allocated. But this did not happen.
This means that if we directly call [NSObject allocWithZone:nil] or [super allocWithZone:nil], the message would not redirect to the overriding version (subclass version) of allocWithZone but will give direct access to NSAllocateObject() function which does the actual allocation.
The code of +(id)allocWithZone in NSObject must look somewhat like this-
+(id)allocWithZone:(NSZone *)zone{
return NSAllocateObject();
}
Had this implementation(NSObject's allocWithZone:) included something like [self allocWithZone], the message dispatch mechanism would have included the subclass version of allocWithZone which would then make us go through the "sneaky" trap involving a call to sharedStore method.Following is the case that I'm talking about. Now if this were the case the code would definitely have infinite-looped.Clearly this isn't the case.
+(id)allocWithZone:(NSZone *)zone{
if([self allocWithZone:zone]) //this would trigger a call to subclass ver. which would call sharedStore method which would then have [super allocWithZone:nil].Infinite Loop
return NSAllocateObject();
}
So can someone clear up this query about this so called "sneaky" trap. Was the trap meant for blocking anyone from instantiating separately .i.e not being able to use NSObject's allocWithZone except when inside of sharedStore method ? Pls clarify..
The first, most important lesson here is that you should not override +allocWithZone:. I know the BNR book describes it (and the BNR book is generally very good). You shouldn't do it. I know that Apple includes some example code that does it. You shouldn't do it. (And Apple notes in the explanation that it is rare to need this.) Singletons should be created with the dispatch_once pattern.
You don't give the initial code, but I suspect that their example code overrides alloc, but not allocWithZone:. They're simply saying that if the caller uses allocWithZone:, it won't go through alloc, so they've also overridden alloc to catch that. (Of course the right answer would be just to override allocWithZone: and not alloc. But you shouldn't be overriding these methods in any case.)
EDIT:
I believe you are misunderstanding what "our sneaky alloc trap" means here. The author is assuming the following code at this point in the text:
#interface BNRItemStore : NSObject
+(BNRItemStore *)sharedStore;
#end
#implementation BNRItemStore
+(BNRItemStore *)sharedStore{
static BNRItemStore *sharedStore=nil;
if (!sharedStore){
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
#end
That's it; no +alloc overrides at all. It then points out "to enforce the singleton status…you must ensure that another instance of BNRItemStore cannot be allocated." (*)
The author goes on to suggest that we might enforce the singleton status by overriding +alloc, but immediately notes that this is insufficient, since the caller can use +allocWithZone: instead. Since it is documented that [NSObject alloc] calls [self allocWithZone:], it is necessary and sufficient to override +allocWithZone: and unnecessary and insufficient to override +alloc.
What you've done in your code is demonstrate that you can modify BNRItemStore to call [super allocWithZone:] in +alloc. That is not the point. If you can modify BNRItemStore, you could also make it a non-singleton. The point is whether an outside caller (main() in your case) can bypass the singleton instantiation, which she cannot. (**)
(*) The point it doesn't make at this point, and probably should, is that it is generally a bad idea to "enforce the singleton status" by quietly returning a singleton when the callers asked you to allocate a new object. If you need to enforce the singleton status, it is better IMO to do so with an assertion in init, since the request for a second allocation represents a programming error. That said, there are times when "transparent" singletons of immutable objects can be useful for performance reasons, such as the special singletons NSNumber provides for certain common integers, and this technique is appropriate in those cases. (By "transparent," I mean that the singleton-ness is an implementation detail that the caller should never worry about. This presumes at a minimum that the object is immutable.)
(**) Actually she can if she is determined to do so. She could always call NSAllocateObject() herself, bypassing +alloc entirely, and then call -init. This would of course be insane, and there is no reason to "protect" her from herself in doing this. It is not the job of an SDK to protect itself from the caller. It is only the job of an SDK to protect a caller from likely mistakes. The caller is never the enemy.
i'm not sure if this quite answers your question or not, but "allocWithZone:" was used back in the day to be able to partition the memory allocated. apple has since moved away from this concept and expects everything to be allocated in the same heap space. "allocWithZone:" does not even function the way it used to, and apple specifically says not to use it.
I implemented the old init-as-a-factory pattern, but in one particular case (but not others!) I get a warning from the analyser regarding memory leaks. And indeed, looking at the Cocoa Memory Management Policy rules, it is alloc, not init, which can return +1-retain-count objects.
So it appears that:
Releasing self and returning a new object from init is, strictly speaking, against the rules.
Many places on the internet promote this technique, and because of the tandem nature of alloc/init this does work.
The analyser sometimes complains about this, and sometimes doesn't.
So... have we been doing this wrong all along?
you can implemented init like this, which should release self to balance the retain count from alloc call.
- (id)initWithSomething:(id)something
{
[self release]; // don't need this line for ARC
self = nil;
return [[PrivateSubClass alloc] initWithSomething:something];
}
and it if very often to implement init as a factory method. e.g. NSArray, NSDictionary, NSString
As gaige said, it will be much more clearer if you post a piece of code rather than explanations.
Anyway, you can move your factory to the class method, so you will have no such problem at all. I mean something like this:
MyClass* instance = [MyClass instanceWithParameters:params];
#interface MyClass
+ (MyClass*) instanceWithParameters:(ParamType)params;
#end
Without knowing what is the code that is causing the analyzer's behavior it's hard to tell, but as a general rule, here's a couple of compiler-friendly ways to define init/factory methods.
Classic alloc/init
- (instancetype)initWithParameter:(id)parameter {
if(self = [super init]) {
_parameter = parameter;
}
return self;
}
Usage
MyCustomClass * myInstance = [[MyCustomClass alloc] initWithParameter:foo];
This will produce an instance with a +1 retain count. Under ARC this will be automatically managed properly since it follows the NARC rule (New, Alloc, Retain, Copy).
For the same reason, in pre-ARC environments it has to be explicitly released by the client.
Custom factory method
ARC
+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
return [[self alloc] initWithParameter:parameter]; // assuming -initWithParameter: defined
}
Pre-ARC
+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
return [[[self alloc] initWithParameter:parameter] autorelease]; // assuming -initWithParameter: defined
}
Usage
MyCustomClass * myInstance = [MyCustomClass canIHazInstanceWithParameter:foo];
Both in ARC and pre-ARC the method returns an autoreleased instance (this is clearly more explicit in the pre-ARC implementation), which doesn't have to be managed by the client.
Remarks
You may have noticed the instancetype keyword. That's a handy language extension introduced by Clang, that turns the compiler into a dear friend when implementing your own constructors/factory methods. I wrote an article on the subject, which may be relevant to you.
Whether factory methods are preferable to init methods is debatable. From a client perspective it does not make much difference under ARC, provided that you carefully follow the naming conventions, even though I personally tend to expose factory methods in the interface, while implementing custom init methods only internally (as I did in the examples above). It's more a matter of style than an actual practical concern.
I have a property that holds a Core Foundation object, specifically a CFHTTPMessageRef. I've tried using the attribute((NSObject)) macro to tell the compiler to treat the referenced object as a normal NSObject and handle the retaining and releasing of that object for me rather than having to CFRelease myself. I've tried changing the property to weak instead of strong, but nothing seems to work, the static analyzer still tells me I have a memory leak, and from the looks of the output from CFGetRetainCount, it would that it's right. Does anyone know why my property would cause a memory leak:
typedef __attribute__((NSObject)) CFHTTPMessageRef HTTPMessageRef;
#interface ABRemoteConnection : NSObject
#property (strong) HTTPMessageRef message;
#end
- (void)dataReceived:(NSNotification *)notification {
self.message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
// do some stuff and if the message is complete, use it and release the
// message object
if (messageIsComplete(self.message)) {
self.message = NULL;
}
}
From the documentation, it looks like __attribute__((NSObject)) is only meant to be used with retain.
But switching strong to retain means you're now adding another ref which the prop is going to manage for you; there's still the original ref that you have to CFRelease.
If you think about it, this makes sense. Imagine doing the same thing with a property(retain) NSObject *:
- (void)dataReceived:(NSNotification *)notification {
self.o = [[NSObject alloc] init];
}
This works fine—but only because ARC sees the alloc and automatically inserts a corresponding release for you at the end of the scope. With a CF___Create function, there's no automatic CFRelease at the end of the scope, so you have to manually write one.
If you really want HTTPMessageRef to be as convenient as, say, NSDictionary, it can't just be a typedef; it has to be a toll-free bridged class.
I'm new to Objective C. I see in many iPhone/iPad applications that after releasing an object, it would then be set to nil.
[self.obj release]
self.obj = nil;
I assume this is done so as not to leave the pointer to reference a memory location that is now deallocated. Now assume the following situation:
//Thread #1 code
..some code
..some code
[self.obj release]
-------> Thread #2 runs //Thread #2 code
..some code
if (self.obj){
some code
}
self.obj = nil; <----- Thread #1 runs
I was wondering if this situation possible? And if it is, is there a way to make the release/nil atomic?
this is actually not entirely correct
[self.obj release]
self.obj = nil;
you should write simply
self.obj = nil;
which will call the setter that will release the previous instance.
Yes, it could blow up. Consider your code example.
[self.obj release];
self.obj = nil;
You use self.obj which means you are referencing accessor/mutators methods instead of accessing your object directly. Chances are you'd declare "obj" as a retained property. Your .h would be something like...
#property (retain) Something *obj;
and your .m
#synthesize obj;
If you later release your object by using the methods created by your #synthesize you are safe.
[self setObj:nil];
// or equally valid
self.obj = nil;
// Below is (almost) identical way to access, release and nil the variable directly.
// The major difference is you don't multi-threaded protection of your property
// declaration (explained below).
[obj release];
obj = nil;
If you look back at the property I specified above you'll notice I didn't put in the very commonly seen nonatomic. It wasn't by accident. Take a look at Apple's docs
Properties are atomic by default so that synthesized accessors provide robust access to properties in a multithreaded environment—that is, the value returned from the getter or set via the setter is always fully retrieved or set regardless of what other threads are executing concurrently.
You can surround both operations in an #synchronized block to ensure that both operations complete before leaving the block:
#synchronized(lockObject)
{
[self.obj release];
self.obj = nil;
}
Provided that any other threads that might access that variable also synchronize around the same lock object, you shouldn't run into any issues.
I know Singleton pattern has been discussed so much. But because I'm not fully understand the memory management mechanism in Objective-C, so when I combined Singleton implementation with multithreading, I got a big error and cost me a whole day to resolve it.
I had a singleton object, let's call it ObjectX. I declared an object inside ObjectX that will detach a new thread, let's call that object objectWillSpawnNewThread, when I called
[[ObjectX sharedInstance].objectWillSpawnNewThread startNewThread];
The new thread could not be executed correctly, and finally I found out that I should not declare the object objectWillSpawnNewThread inside the singleton class.
Here are my questions:
How does Objective-C allocate static object in the memory? Where does Objective-C allocate them(Main thread stack or somewhere else)?
Why would it be failed if we spawn a new thread inside the singleton object?
I had searched the Objective-C language [ObjC.pdf] and Objective-C memory management document, maybe I missed something, but I currently I could not find any helpful information.
Learn the memory management rules. They are simple and in a week you’ll have the time investment back.
Forget about singletons. More precisely, forget about singletons enforced by code. They are unnecessarily complicated and 99% of the time are simply bad design.
Read Singletons are Pathological Liars by Miško Hevery, read it carefully, read all of the related blog posts and think about it. Most of the time there is no real need for a class to enforce having a single instance. Usually you can get around the problem by creating some kind of Factory class that will create most instances for you and wire them together:
#interface Factory {
id classYouWantJustOneInstanceOf;
}
- (id) wireMainController;
#implementation Factory
- (id) init {
[super init];
// No “sharedFoo” stuff necessary. Foo is a plain
// class without singleton boilerplate, easily testable.
classYouWantJustOneInstanceOf = [[Foo alloc] init];
return self;
}
- (id) wireMainController {
id controller = [[SomeClass alloc] init];
// Dependencies set explicitly, good.
[controller setFoo:classYouWantJustOneInstanceOf];
return [controller autorelease];
}
#implementation UIApplicationDelegate
- (void) applicationDidFinishLauching: (UIApplication) app {
// Now all your “singletons” will get created,
// no funny static stuff.
factory = [[Factory alloc] init];
controller = [[factory wireMainController] retain];
[window addSubview:controller.view];
// up and running
}
- (void) dealloc {
[controller release];
// Now all your “singletons” will get released.
[factory release];
[super dealloc];
}
I know this feels a bit uneasy compared to “simple” [Foo sharedFoo], but it’s worth it, trust me.
The advantages of this solution, in case they are not apparent:
No singleton boilerplate in your classes. No static shared instances, no thread synchronization, nothing.
Explicit dependency management. If class A needs instance of B to do its work, you can see that from a public setter or a constructor parameter of A. No suprise dependencies inside the implementation file. And if you need to wire A differently, say for testing purposes, it’s easier when the dependencies are explicit (as opposed to calling [B sharedB]).
Clear object lifecycle. No static variables, no static initialization and no lazy initialization unless you really want it. You know when objects get created and you can deallocate everything you create.
Note that this is a simplified case, so that the division between application delegate and Factory seems a bit pedantic, but in a real case it makes perfect sense (see the Principle of single responsibility).
It's hard to say what you did wrong unless you post some code, but there's no magic involved with singleton objects. There is no suuch thing as a static object.
Question 1:
All Objective-C objects are allocated from the heap. You can declare pointers to them from any scope, but somewhere in your code, directly or indirectly, the object's class must be sent the alloc message and the resulting pointer initialised and assigned to your pointer. Your object's instance variables must be initialised in your object's init method. So one way to implement your singleton would be as follows:
// Header
#interface ObjectX : NSObject
{
SpawnObject* objectWillSpawnNewThread;
}
+(ObjectX*) sharedInstance;
// Implementation
#implementation ObjectX
-(id) init
{
self = [super init];
if (self != nil)
{
objectWillSpawnNewThread = [[SpawnObject alloc] init];
}
return self;
}
+(ObjectX*) sharedInstance
{
static ObjectX* sharedInstance;
#synchronized([ObjectX class])
{
if (sharedInstance == nil)
{
sharedInstance = [[ObjectX alloc] init];
}
}
return sharedInstance;
}
#end
Question 2:
Can't tell you the answer, unless you post your code and the error message. However, one common problem is forgetting to set up an autorelease pool inside the new thread.