trouble with NSFastEnumeration protocol and ARC - xcode

I'm having trouble implementing the NSFastEnumeration protocol while migrating Objective-C code to ARC.
Could someone tell me, how to get rid of the following warnig (see code snippet)? Thanks in advance.
// I changed it due to ARC, was before
// - (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*) state objects: (id*) stackbuf count: (NSUInteger) len
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*) state objects: (__unsafe_unretained id *) stackbuf count: (NSUInteger) len
{
...
*stackbuf = [[ZBarSymbol alloc] initWithSymbol: sym]; //Warning: Assigning retained object to unsafe_unretained variable; object will be released after assignment
...
}
- (id) initWithSymbol: (const zbar_symbol_t*) sym
{
if(self = [super init]) {
...
}
return(self);
}

With ARC, something has to hold a strong or autoreleasing reference to each object, otherwise it will be released (just as the warning says). Because stackbuf is __unsafe_unretained, it's not going to hang onto the ZBarSymbol for you.
If you create a temporary autoreleasing variable and stash your object there, it will live until the current autorelease pool is popped. You can then point stackbuf to it without complaint.
ZBarSymbol * __autoreleasing tmp = [[ZBarSymbol alloc] initWithSymbol: sym];
*stackbuf = tmp;

Related

How can I resolve my CoreGraphics Memory Leak without crashing?

I have a code iterator that is a test function for conversion of raw data to UIImage objects for file storage. I have two failure paths or options. I can fail:
A.)By having a leak (instruments doesn't show it--instruments all allocations seems to be stable, I have to iterate a number of times say 50 and it will crash running the test function through instruments or normally). Memory appears to be the culprit due to "b" option below.
B.)I can do a forced release of the CG object within UIImage in which case the app will proceed. Once the iterator completes the test will crash on the very last iteration trying to free the CGImage underneath the image returned from my sub-function. The iterator will complete it's run no matter how many iterations I request. This would point to the fact I have fixed a leak. So now I'm trying to puzzle together what is special about the very last iteration? (ARC?)
- (UIImage *) convertRGBToUIImage:(unsigned char *) buffer
withWidth:(int) width
withHeight:(int) height
{
CGColorSpaceRef colorSpace = nil;
CGContextRef context = nil;
CGImageRef ref = nil;
UIImage *retImg = nil;
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
NSLog(#"ERROR: allocating color space convert Bitmap\n");
return nil;
}
context = CGBitmapContextCreate (buffer, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace );
if (context == NULL)
{
NSLog(#"ERROR: Context not created!");
return nil;
}
ref = CGBitmapContextCreateImage(context);
CGContextRelease(context);
retImg = [UIImage imageWithCGImage:ref];
CGImageRelease(ref);
return retImg;
}
iterator basics
for(int i = 0; i < numberiterations; i++)
{
UIImage *completedImage = [phelper convertRGBToUIImage:pBuffer withWidth:width withHeight:height]
//...save it out or display it
//Turn on one of the scenarios below
//scenario "A" -- will leak memory (instruments doesn't seem to turn anything up)
//ARC free
//completedImage = nil; //this will leak and fail after ~50 iterations
//-or-
//scenario "B" -- will crash on the very last iteration every time but can run x times
//CGImageRelease([completedImage CGImage];
}
The analysis tool in Xcode prefers scenario 'A' above. Again I can do this and everything seems great but it will not successfully complete the test. I think this points to ARC. I have tried to figure out the __bridge type casting with no success. I can't seem to get the syntax right and perhaps its not the answer to my problem. Any hints or ideas would be very appreciated.
The reason why Instruments is not showing a leak is that — technically — there is none
What you are seeing is the memory pressure from an #autoreleasepool that is not tight enough:
Your method convertRGBToUIImage:withWidth:withHeight: returns a new autoreleased image, but the closest #autoreleasepool is outside of your loop.
That means that all of those images are not disposed of until the method, in which your loop lives, returns.
This is the reason why you see your memory consumption growing with each iteration in scenario A and your application crash after the loop in scenario B:
UIImage is — mostly — an Objective-C wrapper around CGImageRef so that your additional CGImageRelease in scenario B pulls the backing store out from under the UIImage’s feet. This backing store happens to be the memory hungry part of the UIImage so your drastic increase in memory consumption almost vanishes.
When the #autoreleasepool is drained after the method with the loop returned, it disposes of all the temporary UIImage instances which — in turn — want to dispose of their backing-CGImageRef…and boom there goes the program.
The trick is to create your own #autoreleasepool inside the loop:
for (int i = 0; i < numberiterations; i++) {
#autoreleasepool {
// your old iteration body comes here
}
}
Now, scenario A will behave as expected and scenario B will crash immediately at the end of the first iteration — which is to be expected, as well.
And now for some serious brain-f**k:
If you run your original code under ARC in a “Release” scheme, (i.e. with the compiler flag -Os aka “fastest smallest”) scenario A will most likely not crash!
The reason for this seemingly bizarre behavior is, that ARC garnishes your code with a special set of C-functions that perform the actual memory management — and because these functions are constant (which method calls in Objective-C aren’t necessarily) the compiler can optimize certain combinations of them away.
This leads to the UIImage never being inserted into an autorelease-pool in the first place and, thus, being disposed of after each iteration of the loop.

analyze and memory alerts in xcode

I ran 'analyze" in xcode on a current iOS project to try to track down a freeze issue and there are a number of memory alerts that I don't understand (screenshot of one below).
What is going on there: I have a custom ObjC class extending NSObject; in the init method I alloc/init an NSMutableArray and then in a loop, populate it with NSMutableArrays. This nested array is declared as a property and released in dealloc(). It lives for the life of the app.
Am I doing this wrong? I don't understand the alert#3: # object not referenced in this execution path and has a retain count of +1.
Since my class allocs the outer array, it owns it and will clean it up. Do the inner arrays need to be released?
Thanks for any tips - still new at this.
EDIT/ADDITION
Trying to stamp out the additional memory warnings I am getting so I thought I would add to the question here in the event someone stumbles upon this w/ the same issue.
I am getting the following alert with the code below (the 2nd line "[keyArray addObject: etc"). What is going on: I have a custom class (Key - based on NSObject) that I instance and store in an array. Based on answers to my previous question, I guess my alloc increases the retain count and then when it is added to the array, the retain count isn't decremented - so the memory warning occurs.
What is the proper way to handle something like this? Use a placeholder like this:
Key * k = [[Key alloc] initKeyWithPath:path isBlackKey:NO]];
[keyArray addObject: k];
[k release];
Is that the proper way to do it? Or is there I way to write the custom class to return an autoreleased obj? (thanks and sorry to be so long winded!).
Potential leak of an object allocated on line 460
Method returns an Objective-C object with a +1 retain count (owning reference)
Object allocated on line 460 is not referenced later in this execution path and has a retain count of +1 (object leaked)
-(void) addOctaveToArraysWithTransform:(CGAffineTransform*)trans andPath: (CGMutablePathRef) path
{
path = [self createCF_keyUsingTransform: trans];
[keyArray addObject:[[Key alloc] initKeyWithPath:path isBlackKey:NO]];
}
Key.h
#import <Foundation/Foundation.h>
#import "Key.h"
#interface Key : NSObject {
#public
CGMutablePathRef keyPath;
BOOL isBlackKey;
NSValue * path;
int keyState;
BOOL needsRedraw;
}
#property (nonatomic, assign) int keyState;
#property (nonatomic, assign) BOOL needsRedraw;
#property (nonatomic) CGMutablePathRef keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag;
-(CGMutablePathRef) getKeyPath;
#end
Key.m
#import "Key.h"
#implementation Key
#synthesize keyState, needsRedraw, keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag
{
if ((self = [super init])){
isBlackKey = flag;
keyState = 0;
needsRedraw = NO;
keyPath = aPath;
CGPathRetain(keyPath);
}
return self;
}
-(CGMutablePathRef) getKeyPath
{
return keyPath;
}
#end
Yes, you have to release the inner arrays to balance the alloc/init. Remember the outer array will retain each inner array, and the outer array will presumably release those later. But here you are still responsible for the alloc/init you just did.
Hope that helps.
You have an allocation of an NSMutableArray on each iteration of the for-loop. Instead use: NSMutableArray array] which is a convenience method that return an autoreleased NSMUtableArray suitable for adding to fieldNotes which will retain the NSMutableArray.

Deep copy of dictionaries gives Analyze error in Xcode 4.2

I have the following method in a NSDictionary category, to do a deep copy, which works fine.
I just upgraded from Xcode 4.1 to 4.2, and the Analyze function gives two analyzer warnings for this code, as indicated:
- (id)deepCopy;
{
id dict = [[NSMutableDictionary alloc] init];
id copy;
for (id key in self)
{
id object = [self objectForKey:key];
if ([object respondsToSelector:#selector(deepCopy)])
copy = [object deepCopy];
else
copy = [object copy];
[dict setObject:copy forKey:key];
// Both -deepCopy and -copy retain the object, and so does -setObject:forKey:, so need to -release:
[copy release]; // Xcode 4.2's Analyze says this is an incorrect decrement of the reference count?!
}
return dict; // Xcode 4.2's Analyze says this is a potential leak
}
Are these bugs in Xcode's analyzer, or are there changes I can make to avoid these warnings?
I'm not using ARC yet, though I am interested if there are additional changes needed to support ARC for this method.
Presumably, it is because deepCopy does not begin with the prefix copy.
So you may want to change to something like copyWithDeepCopiedValues (or something like that), and then see if the analyzer flags that.
Update
As Alexsander noted, you can use attributes to denote reference counting intent. This should (IMO) be the exception to the rule, and used rarely, if ever. Personally, I will not use attributes for objc methods because it is fragile.
The only attribute I have used so far has been consume, and every time I use these attributes has been in statically typed contexts (e.g. C functions and C++ functions and methods).
The reasons you should avoid attributes when possible:
1) Stick with conventions for the programmers' sake. The code is clearer and you do not need to refer to the documentation.
2) The approach is fragile. You can still introduce reference count imbalances, and attributes can be used to introduce build errors due to conflicts in attributes.
The following cases are all built with ARC enabled:
Case #1
#import <Foundation/Foundation.h>
#interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
#end
#implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:#"MONType"];
return ret;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:#"NSAttributedString"];
}
else {
obj = [MONType new];
}
NSLog(#"Result: %#, %#", obj, [obj string]);
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
This program produces the following error: error: multiple methods named 'string' found with mismatched result, parameter type or attributes.
Great, the compiler's doing what it can to prevent these issues. What that means is that conflicts in attributes can introduce errors based on the translation. This is bad because when nontrivial codebases are combined and attributes conflict, you will have errors to correct and programs to update. This also means that simply including other libraries in translation units can break existing programs when attributes are used.
Case #2
Header.h
extern id NewObject(void);
Header.m
#import <Foundation/Foundation.h>
#import "Header.h"
#interface MONType : NSObject
- (NSString *)string __attribute__((objc_method_family(copy)));
#end
#implementation MONType
- (NSString *)string
{
NSMutableString * ret = [NSMutableString new];
[ret appendString:#"-[MONType string]"];
return ret;
}
#end
id NewObject(void) {
id obj = nil;
if (random() % 2U) {
obj = [[NSAttributedString alloc] initWithString:#"NSAttributedString"];
}
else {
obj = [MONType new];
}
return obj;
}
main.m
#import <Foundation/Foundation.h>
#import "Header.h"
int main (int argc, const char * argv[])
{
#autoreleasepool {
for (size_t idx = 0; idx < 8; ++idx) {
id obj = NewObject();
NSLog(#"Result: %#, %#", obj, [obj string]);
}
}
/* this tool's name is ARC, dump the leaks: */
system("leaks ARC");
return 0;
}
Ok. This is just bad. We've introduced leaks because the necessary information was not available in the translation unit. Here's the leaks report:
leaks Report Version: 2.0
Process 7778: 1230 nodes malloced for 210 KB
Process 7778: 4 leaks for 192 total leaked bytes.
Leak: 0x1005001f0 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500320 size=64 zone: DefaultMallocZone_0x100003000 __NSCFString ObjC CoreFoundation mutable non-inline: "-[MONType string]"
Leak: 0x100500230 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
Leak: 0x100500390 size=32 zone: DefaultMallocZone_0x100003000 has-length-byte: "-[MONType string]"
note: the count may differ because we used random()
This means that because MONType is not visible to main(), the compiler bound the ARC properties to methods which were visible to the current TU (that is, string from declarations in Foundation, all of which follow convention). As a result, the compiler got it wrong and we were able to introduce leaks into our program.
Case 3
Using a similar approach, I was also able to introduce negative reference count imbalances (premature releases, or a messaged zombie).
note: Code not provided because Case #2 already illustrates how one can accomplish a reference count imbalance.
Conclusion
You can avoid all these problems and improve readability and maintainability by sticking with convention, rather than using attributes.
Bringing the conversation back to non-ARC code: Using attributes makes manual memory management more difficult for programmers' readability, and for the tools which are there to help you (e.g. compiler, static analysis). If the program is suitably complex such that the tools can't detect such errors, then you should reconsider your design, because it will be equally complex for you or somebody else to debug these issues.
Adding onto #Justin's answer, you can tell the compiler that -deepCopy returns a retained object by appending the NS_RETURNS_RETAINED attribute to the method's declaration like so:
- (id) deepCopy NS_RETURNED_RETAINED;
Alternatively, you can use explicitly control the method's "family" using the objc_method_family attribute like so:
- (id) deepCopy __attribute__((objc_method_family(copy)));
If you do this, the compiler will know that this method is in the copy family and returns a copied value.

Xcode error keeps saying size undeclared how do i fix this?

I am writing the below code into xcode and it keeps coming up size undeclared: first use in this function how do I fix this so I can run the code?
// on "init" you need to initialize your instance
-(id) init
{ CCSprite *spaceCargoShip = [CCSprite
spriteWithFile:#"spaceCargoShip.png"];
[spaceCargoShip setPosition:ccp(size.width/2, size.height/2)];
[self addChild:spaceCargoShip];
The size variable isn't declared in that function. You have to get it from somewhere else — perhaps self.size, but without seeing the rest of the code I don't know where it should be coming from.
I'm guessing that you want to position your cargo ship at the lower left corner of the screen. For that, you want to use the size of the sprite you created, which is spaceCargoShip.contentSize.
Instead of
[spaceCargoShip setPosition:ccp(size.width/2, size.height/2)];
use
[spaceCargoShip setPosition:ccp(spaceCargoShip.contentSize.width/2,
spaceCargoShip.contentSize.height/2)];
Have fun!
I was having the same issue.
The code has to be put in the init function and inside the if statement.
- (id)init {
if (self = [super init]) {
//Code for the spaceCargoShip
...
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize]; //<-- This is the "size" variable that the code is looking for.
...
//Enter the code for the spaceCargoShip in here.
CCSprite *spaceCargoShip = [CCSprite spriteWithFile:#"SpaceCargoShip.png"];
[spaceCargoShip setPosition:ccp(size.width/2, size.height/2)];
[self addChild:spaceCargoShip];
}
return self;
}
The reason that it is not working is that the variable falls out of scope if you don't put it within the if statement.

Is this the right way to add items to NSCombobox in Cocoa?

I'm Delphi programmer and very new to Cocoa.
at first I tried this :
-(void)awakeFromNib
{
int i;
NSString *mystr;
for (i=1;i<=24;i++)
{
[comboHour addItemWithObjectValue:i];
}
}
But it didn't work. Then I tried to search on Google but no luck.
After experimenting about 30 min, I come with this:
-(void)awakeFromNib
{
int i;
NSString *mystr;
for (i=1;i<=24;i++)
{
mystr = [[NSString alloc]initWithFormat:#"%d",i];
[comboHour addItemWithObjectValue:mystr];
//[mystr dealloc];
}
}
My questions are:
Is this the right way to do that ?
Do I always need to alloc new
NSString to change its value from
integer ?
When I uncomment [mystr dealloc],
why it won't run ?
Does it cause memory leak to alloc
without dealloc ?
Where can I find basic tutorial like
this on internet ?
Thanks in advance
Do I always need to alloc new NSString to change its value from integer ?
Generally yes; however, there are more convenient ways to create strings (and many other types of objects) than using alloc and init (see autorelease pools below)
You can pass any Objective-C object type to addItemWithObjectValue:, including NSString and NSNumber objects. Both classes have a number of convenient class methods you can use to create new instances, for example:
for (int i = 0; i < 24; ++i)
{
[comboHour addItemWithObjectValue:[NSNumber numberWithInt:i]];
}
When I uncomment [mystr dealloc], why it won't run ?
Never call dealloc. Use release instead.
Cocoa objects are reference counted, like COM objects in Delphi. Like COM, you call release when you're finished with an object. When an object has no more references it is automatically deallocated.
Unlike COM, Cocoa has "autorelease pools", which allows you to, for example, create a new NSString instance without having to worry about calling release on it.
For example: [NSString stringWithFormat:#"%d", 123] creates an "autoreleased" string instance. You don't have to release it when you're done. This is true of all methods that return an object, except new and init methods.
Does it cause memory leak to alloc without dealloc ?
Yes, unless you're using garbage collection.
Where can I find basic tutorial like this on internet ?
See Practical Memory Management
The correct way is:
-(void)awakeFromNib
{
int i;
for (i=1;i<=24;i++)
{
NSString *mystr = [[NSString alloc]initWithFormat:#"%d",i];
[comboHour addItemWithObjectValue:mystr];
[mystr release];
}
}
You can use NSNumber instead of NSString, which might be preferable depending on your context.
You do need to create a new object everytime, because addItemWithObjectValue: is expecting an object rather than a primitive.
You can create a new object (e.g. `NSString), via two methods:
Using alloc/init, like how you did it initially. Such initializations require the release of the object once it isn't required anymore in the allocation scope, using release rather than dealloc.
Using stringWithFormat: factory methods that use auto release pool to release themselves "automatically". The code would look like:
-(void)awakeFromNib
{
int i;
for (i=1; i <= 24; i++) {
NSString *s = [NSString stringWithFormat:#"%d", i];
[comboHour addItemWithObjectValue:s];
}
}
However, it is recommended not to use such construction within loops.
For memory issues, check out the Memory Management Programming Guide for Cocoa
Based on the code you posted and your stated experience level, I recommend going through Apple's Currency Converter tutorial if you haven't already. It's the standard Cocoa tutorial every beginner should read. Fundamentals like interacting with IBOutlets are covered.

Resources