ARC Releasing Too Soon - objective-c

I wrote a BinaryReader class in Objective-C that can extract an NSString from bytes created using C#'s BinaryWriter.Write(string). It seems to work great, but when I call:
BinaryReader *binaryReader = [[BinaryReader alloc] initWithData:dataArray];
NSString *strName = [binaryReader readString];
// strName is not nil, but the value is no longer correct (seems to have been erased)
I stepped into the readString method, and everything looks great (the string is correct) up until the method returns (I think ARC is kicking in).
Why would this be happening?
Here's my readString method:
-(NSString *)readString
{
NSUInteger bytesCount = (NSUInteger)[self read7BitEncodedInt];
const void *byteData = [_data bytes] + _dataIndex;
NSString *returnValue =
[[NSString alloc] initWithBytes:byteData length:bytesCount encoding:NSUTF8StringEncoding];
_dataIndex += bytesCount;
return returnValue;
}
The returnValue contains the proper string right before the method returns.
By request, here's my init function for BinaryReader.
-(id)initWithData:(NSData *)theData
{
self=[super init];
if(self != nil)
{
_dataIndex = 0;
_data = theData;
}
return self;
}
and the relevant part of header
#interface BinaryReader : NSObject
{
#private
NSData *_data;
int _dataIndex;
}

Are you sure the result is actually broken? The code you posted seems fine and there should be no ownership/memory issues when compiled with ARC.
Lately there have been issues reported with lldb (the debugger) not showing correct values in some cases. Try to log the result using NSLog.

ARC should not release the object so soon, so your error might be inside the readString itself.

Put your binaryReader as a property with the strong attribute, it may solve your problem.
#propert(nonatomic, strong) BinaryReader* binaryReader;
It tell to the compil to not release the object.

Related

Better way than write dozens of empty getters?

I use lazy instantiation on my properties, to have my class created and used as fast as possible. To achieve this, I write lots of 'empty' getters like this:
- (VMPlacesListFilter *)currentFilter
{
if (!_currentFilter) {
_currentFilter = [[VMPlacesListFilter alloc] init];
}
return _currentFilter;
}
They are all the same: if the instance variable is nil, call the -alloc and -init on the class of the property, then return the instance variable. Very common and straightforward.
If I don't create this getter by myself, Objective-C's automatic synthesization creates a getter for me, which does only the returning part (does not init the object if the instance variable is nil).
Is there any way to avoid writing this boilerplate code?
Nope, I'm afraid there's no good way around it, if you really want to have lazy initialization. Personally, I usually save lazy initialization for stuff that could really be time consuming or memory intensive (say, loading images or view controllers), and initialize cheap stuff (like simple data structures or model objects) in init.
- (instancetype) init {
self = [super init];
if( self ) {
_cheapThing1 = [NSMutableArray array];
_cheapThing2 = [[MyModelObject alloc] init];
}
return self;
}
- (ExpensiveThing*) expensiveThing
{
if( _expensiveThing == nil ) {
_expensiveThing = [[ExpensiveThing alloc] init];
}
return _expensiveThing;
}
Unless you're loading something from disk or the network, I wouldn't worry too much about initialization time. Of course, profile it.
I know this is an Objective-C question, but it's worth noting that Swift has lazy initialization built-in.
lazy var currentFilter = VMPlacesListFilter()
First off, I totally agree with #zpasternack that "lazy load" should not be misused. However, automatically generating setters and getters is completely doable with the power of Objective-C runtime. In fact, CoreData is doing this.
Anyway, I have come up with some stupid code implementing a class called LazyClass, in which you can declare dynamic properties like lazyArray (see below). Using dynamic method resolution, when the property is accessed for the first time, a getter that calls the corresponding class's default +alloc and -init method will be automatically added to the class. All underlying instance variables are stored in an NSMutableDictionary called myVars. Of course you can manipulate ivars through the runtime API as well, but using a dictionary should save some work.
Please note that this implementation just shows the basic idea of how it works. It lacks error checking and is not supposed to be shipped.
LazyClass.h
#interface LazyClass : NSObject
#property NSMutableDictionary *myVars;
// lazily initialized property
#property NSArray *lazyArray;
#end
LazyClass.m
#import "LazyClass.h"
#import <objc/objc-runtime.h>
#implementation LazyClass
#dynamic lazyArray;
- (instancetype)init {
self = [super init];
self.myVars = [NSMutableDictionary dictionary];
return self;
}
- (NSMutableDictionary *)getMyVars {
return self.myVars;
}
// the generated getter method
id dynamicGetterMethodIMP(id self, SEL _cmd) {
// selector name, which is also the property name
const char *selName = sel_getName(_cmd);
NSString *selNSName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", selNSName];
if (![self valueForKeyPath:keyPath]) {
// get the actual type of the property
objc_property_t property = class_getProperty([self class], selName);
const char *attr = property_getAttributes(property);
NSString *attrString = [[NSString alloc] initWithCString:attr encoding:NSUTF8StringEncoding];
NSString *typeAttr = [[attrString componentsSeparatedByString:#","] firstObject];
NSString *typeName = [typeAttr substringWithRange:NSMakeRange(3, typeAttr.length - 4)];
// the default initialization
Class typeClass = NSClassFromString(typeName);
[self setValue:[[typeClass alloc] init] forKeyPath:keyPath];
}
return [self valueForKeyPath:keyPath];
}
// the generated setter method
void dynamicSetterMethodIMP(id self, SEL _cmd, id value) {
// get the property name out of selector name
// e.g. setLazyArray: -> lazyArray
NSString *propertyName = NSStringFromSelector(_cmd);
propertyName = [propertyName stringByReplacingOccurrencesOfString:#"set" withString:#""];
propertyName = [propertyName stringByReplacingOccurrencesOfString:#":" withString:#""];
propertyName = [NSString stringWithFormat:#"%#%#", [propertyName substringToIndex:1].lowercaseString, [propertyName substringFromIndex:1]];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", propertyName];
[self setValue:value forKeyPath:keyPath];
}
// dynamic method resolution
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) containsString:#"set"]) {
class_addMethod([self class], aSEL, (IMP)dynamicSetterMethodIMP, "^?");
} else {
class_addMethod([self class], aSEL, (IMP)dynamicGetterMethodIMP, "v#:");
}
return YES;
}
#end
Documentation
If it's the verboseness that bothers you, I suppose you could compress lazy initialisers that only need one-line initialization using the ternary operator:
- (VMPlacesListFilter *)currentFilter
{
return _currentFilter ? : (_currentFilter = [[VMPlacesListFilter alloc] init]);
}
DISCLAIMER: I don't do this, but it's interesting that it can be done

Access from swift to objective c function with a **void parameter

This is mainly an "academic" question, so please don't ask "why do you need to do this" :-)
I have this objective c method, I'm not using ARC (I know that it leaks if I don't release the allocated string).
This method takes a pointer to a pointer to something in memory, then changes the pointed content to an NSMutableString.
+(void) writeString:(void **)var
{
NSMutableString *aString = [[NSMutableString alloc] initWithFormat:#"pippo %#", #"pluto"];
*var = aString;
}
From Objective C I access it in this way and it works correctly
NSString *str;
[FLSwiftUtils writeString:&str];
NSLog(#"%#", str); // prints pippo pluto
Now I want to access it from Swift in the same way, but I have problem with pointers.
I tried this way:
var opaque = COpaquePointer.null() // create a new opaque pointer pointing to null
FLSwiftUtils.writeString(&opaque)
println(opaque)
But it prints VSs14COpaquePointer (has 1 child), because opaque is a pointer and I don't understand how to deference it and access the pointed NSMutableString
If I try, then, to create an UnsafePointer starting from the opaque value:
var str = UnsafePointer<NSMutableString>(opaque)
println(str)
the println prints an empty line
I even tried this:
var secondStr = AutoreleasingUnsafePointer<NSMutableString>(opaque.value)
println(secondStr)
println(secondStr.memory)
but it prints
VSs26AutoreleasingUnsafePointer (has 1 child)
__NSCFString
I think I am near the solution, but sure I'm doing something wrong.
I received the answer on the Apple Forum (note: this code is FOR ARC)
+ (void)writeString:(void **)var
{
NSMutableString *aString = [[NSMutableString alloc] initWithFormat:#"pippo %#", #"pluto"]; // this is autoreleased by ARC
*var = (void *)CFBridgingRetain(aString); // send a retain, the release will be sent at the swift side
}
Then just release it into Swift like this:
var opaque = COpaquePointer.null() // create a new opaque pointer pointing to null
TestClass.writeString(&opaque)
var string = Unmanaged<NSString>.fromOpaque(opaque).takeRetainedValue() // this function: creates an object, from the opaque pointer "opaque", take it's value and balance it with a release
println(string)

premature dealloc in ARC based app

I have a problem that seems to be a premature release of an in-use object in an ARC based app. I'm trying to create a folder on an FTP server. The relevant parts of code are below; i'll describe the problem first.
problem with the code is, that the debug output in the
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
method is never called.
Instead, i just get an _EXC_BAD_ACCESS_ error.
While debugging i found out two things:
the error only appears if the following line of code (createDir method) is executed:
[ftpStream open];
if that message isn't sent, the rest of the code doesn't really make sense - but it doesn't crash either...
I tracked the EXC_BAD_ACCESS down with NSZombieEnabled: With zombie objects enabled, the GDB produces the following debugger info:
*** -[FTPUploads respondsToSelector:]: message sent to deallocated instance 0x9166590
The referred address 0x9166590 is the address of my FTPUploads object.
It looks like the streams delegate is deallocated before it can handle messages.
Why does the system deallocate an in-use object? How can i prevent it from being deallocated prematurely?
code:
FTPUploads.h excerpt:
#import <Foundation/Foundation.h>
enum UploadMode {
UploadModeCreateDir,
UploadModeUploadeData
};
#class UploadDatasetVC;
#interface FTPUploads : NSObject<NSStreamDelegate> {
#private
NSString *uploadDir;
NSString *ftpUser;
NSString *ftpPass;
NSString *datasetDir;
NSArray *files;
/* FTP Upload fields */
NSInputStream *fileStream;
NSOutputStream *ftpStream;
// some more fields...
enum UploadMode uploadMode;
UploadDatasetVC *callback;
}
- (id) initWithTimeseriesID: (int) aTimeseriesID
fromDatasetDir: (NSString *) aDir
withFiles: (NSArray *) filesArg
andCallbackObject: (UploadDatasetVC *) aCallback;
- (void) createDir;
#end
FTPUploads.m excerpt
#import "FTPUploads.h"
#import "UploadDatasetVC"
#implementation FTPUploads
- (id) initWithTimeseriesID: (int) aTimeseriesID
fromDatasetDir: (NSString *) aDir
withFiles: (NSArray *) filesArg
andCallbackObject: (UploadDatasetVC *) aCallback {
self = [super init];
if (self) {
uploadDir = [NSString stringWithFormat: #"ftp://aServer.org/%i/", aTimeseriesID];
ftpUser = #"aUser";
ftpPass = #"aPass";
datasetDir = aDir;
files = filesArg;
bufferOffset = 0;
bufferLimit = 0;
index = 0;
callback = aCallback;
}
return self;
}
- (void) createDir {
uploadMode = UploadModeCreateDir;
NSURL *destinationDirURL = [NSURL URLWithString: uploadDir];
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) destinationDirURL);
assert(writeStreamRef != NULL);
ftpStream = (__bridge_transfer NSOutputStream *) writeStreamRef;
[ftpStream setProperty: ftpUser forKey: (id)kCFStreamPropertyFTPUserName];
[ftpStream setProperty: ftpPass forKey: (id)kCFStreamPropertyFTPPassword];
ftpStream.delegate = self;
[ftpStream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
// open stream
[ftpStream open];
CFRelease(writeStreamRef);
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"aStream has an event: %i", eventCode);
switch (eventCode) {
// all cases handled properly
default:
// no event
NSLog(#"default mode; no event");
break;
}
}
EDIT: added creation code that is used in the class UploadDatasetVC:
FTPUploads *uploads = [[FTPUploads alloc] initWithTimeseriesID: timeseries_id
fromDatasetDir: datasetDir
withFiles: files
andCallbackObject: self];
[uploads createDir];
It looks to me like the only reference to your FTPUploads object is the delegate property on the stream. This won't retain your object, so if nothing else has a reference to the object, the object will be dealloced. A.R.C. doesn't try to prevent this scenario.
What you need to do is have the code that allocates the FTPUploads object keep a reference to the object until it completes.
It also wouldn't be a bad idea to set the ftpStream.delegate property to nil in your FTPUploads dealloc method, as this will prevent a crash if the object is dealloced prematurely.
The problem is that your ftpStream object is being deallocated. You create it with CFWriteStreamCreateWithFTPURL(), then release it with CFRelease(). You used a __bridge cast, which basically means "don't do any memory management on this assignment". So ARC didn't retain it when you assigned it to ftpStream. Since your intention was to transfer ownership from CF to ARC, that was the wrong cast to use.
You actually wanted either __bridge_retained or __bridge_transfer. I can never remember which is which, though. Luckily, there's another option—the CFBridgingRetain() and CFBridgingRelease() macros. They resolve down to those same bridging casts, but are named far more clearly.
In this case, you want CF to release it, but bridge it over to ARC. So you want CFBridgingRelease(). That will tell ARC to take ownership of the object, and then do a CFRelease. In short, replace this:
ftpStream = (__bridge NSOutputStream *) writeStreamRef;
with this:
ftpStream = CFBridgingRelease(writeStreamRef);
And then remove the call to CFRelease() a few lines later.
My guess is that you should either wait until the stream is finished to do CFRelease(writeStreamRef), or do a __bridge_transfer to transfer the ownership over to ftpStream before you release your writeStreamRef

autoreleasing NSString in class method causing app crash in iOS

The error I receive is as follows:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); //breakpoint that says Thread 1: Program Received Signal: "EXC_BAD_ACCESS".
[pool release];
return retVal;
}
My two questions can be found at the bottom of this post :)
I am currently working on an assignment for an iOS programming class and have hit a road bump.
I have found a fix, shown below, but it doesn't make sense to me. Check it out:
#implementation MyClass
// This class method takes an (NSMutableArray *) and returns an NSString with its contents printed out.
+ (NSString *)myString:(NSMutableArray)anArray
{
// NSString *myString = [[NSString alloc] init]; OLD CODE THAT CAUSES MEMORY LEAK
NSString *myString = [[[NSString alloc] init] autorelease]; //NEW CODE THAT RELEASES FIRST ALLOCATION OF myString WHEN THE FIRST stringByAppendingFormat: IS CALLED
NSString *vp = VARIABLE_PREFIX; //#defined above to be #"%
for (id object in anArray) {
if ([object isKindOfClass:[NSString class]]) {
if ([object hasPrefix:vp]) {
myString = [myString stringByAppendingFormat:#"%#",[object substringFromIndex:1]];
}else{
myString = [myString stringByAppendingFormat:#"%#",object];
}
}else if ([object isKindOfClass:[NSNumber class]]) {
myString = [myString stringByAppendingFormat:#"%#",object];
}
}
return myString; //shouldn't I autorelease myString right before this line? NO NOT ANY MORE. THIS myString IS NOT THE ORIGINAL THAT I alloc-init, BUT AN AUTORELEASED OBJECT RETURNED BY THE stringByAppendingFormat: message.
}
When I try to send the message [myString autorelease];, the program crashes with the above error. It is working fine now as shown above, but I do not understand why.
Every time I send a message containing the "magic words" alloc, init, copy I have to call release, it don't I? Or are the rules different in a Class method (can the Class itself own a file?). I do not call retain in the object that is calling this file.
Here are my two questions:
Why does this crash when I try to release theDescription using autorelease?
Does my code create a memory leak?
This is my very first question on stack overflow! Thank you for your help!
Why does this crash when I try to release theDescription using autorelease?
Assuming you mean myString, it crashes because myString is already autoreleased. You got it by calling -stringByAppendingFormat:, which returns an autoreleased string. Now, you're probably thinking: "But I created it by calling +alloc, so I should release it." That's true, but NSStrings are immutable, and when you call -stringByAppendingFormat: you get a different string back, and that string is autoreleased. Autoreleasing it a second time is an error.
Does my code create a memory leak?
Yes, but not really. The "leaked" object is the empty string that you allocate in the beginning. You never release that string, so you've got a leak. However, NSString is apparently optimized so that [[NSString alloc] init] returns a singleton, so in this particular case it doesn't make any difference that the empty string isn't released. The other strings that are assigned to myString are all autoreleased, so none of those objects are leaked.
Why does this crash when I try to release theDescription using
With the updated code, the problem is that ypu are reassigning the pointer to myString using the methid which already returns an autoreleases object: stringbyappending, therefore if you call autorelease on this object which is already going to get autoreleased i will crash.
Aditionaly the first assugnment in the alloc init gives a memory leak when ypu reassign with stringbyappendingstring, since you lose the reference to the previously created string with alloc init and therefore you will never be able to release it.
Sorry for the formatting on my iPhone atm =)

Objective C 's NSString subStringToIndex causing strange issue

Hi everyone I am working on a game and I have run into a really weird issue and was hoping to get some help. Basically sometimes the text is a bit long to show in one textbox so I wanted it to break down a string and store the undisplayed text in a string to be used in the next message box. So when I use this code..
NSString * Talkin1 = #"Hello I am an annoying string";
NSString * Talkin2 = [Talkin1 substringToIndex:5];
It makes Talkin2 the value of Hello which is what I want. Then I store it in the object with..
[_window setMultiMessage:Talkin2];
which goes to my set and get methods in an object i made.
In it's interface...
NSString * multiMessage;
in its .m
-(void) setMultiMessage:(NSString*)messageTwo
{
multiMessage = messageTwo;
}
-(NSString*) getMultiMessage
{
return multiMessage;
}
then later the main object pulls it out again, when it is done closing the first window with...
NSString * talking = [_window getMultiMessage];
Now in debugging, I have noticed talking's value will be "out of scope" when i get the string back from _window. So then it crashes my program when it tries to use it.
But. If i do this everything works fine.
NSString * Talkin1 = #"Hello I am an annoying string";
//NSString * Talkin2 = [Talkin1 substringToIndex:5];
[_window setMultiMessage:Talkin1];
So it works perfect (except for splitting the string like I want) when I use #" " but not when I use any result of substringToIndex or substringFromIndex.
I am new to objective c so I assume it is something basic I am missing. Any help would be wonderful! Thank you in advance.
(Assuming no GC.)
-substringToIndex: returns an autoreleased object. But you are not retaining the object in the setter, thus no one "owns" the Talkin2 and it will be deallocated "later". You need to copy or retain the string in the setter.
-(void) setMultiMessage:(NSString*)messageTwo {
if (multiMessage != messageTwo) {
[multiMessage release];
multiMessage = [messageTwo retain];
}
}
Actually you should really use Objective-C 2.0's declared property feature. It allows correct setter and getter be generated automatically.
#interface .... { ... }
...
#property(copy) NSString* multiMessage; // <--
...
#end
#implementation ....
#synthesize multiMessage; // <--
...
#end
...
_window.multiMessage = Talkin2;
// or: [_window setMultiMessage:Talkin2];
NSString* talking = _window.multiMessage;
// or: talking = [_window multiMessage];