I am still new to objective-c and I need some insight about variable declaration.
The point of my program is to read a message delivered in several NSData parts. Whenever the program receive a NSData it adds it to the already partially complete message as unsigned chars. This means that the message buffer must be define as a property of the viewController:
#property unsigned char* receiveBuf;
#property int bufSize;
At that point the program has a NSData object named data, and treat it like this:
int len = [data length];
unsigned char* tempBytebuf = (unsigned char*)[data bytes];
for(int i = 0; i < len; i++){ //read all bytes
if (tempBytebuf[i] == 0x7E) { //this char means start of package
_bufSize = 0;
}
else if (tempBytebuf[i] == 0x7D) { //this char means end of package
[self readMsgByte:_receiveBuf :_bufSize];
}
else { //any other char is to be put in the message
_receiveBuf[_bufSize++] = tempBytebuf[i]; //Error happens here
}
}
if I proceed like this it will result in an error EXC_BAD_ACCESS.
If I could tell the program to reserve space for the buffer it would solve the problem:
#property unsigned char receiveBuf[256];
but it seems I can't do this with #properties.
Is there a way of assigning that space in the ViewDidLoad() for example or somewhere else?
Thanks for your support!
EDIT 1:
It seems I just found a solution in some of my previous codes, I should have declare my char table in the implementation instead.
#implementation ViewController
{
unsigned char msgBuf[256];
}
Still if someone could tell me the real difference between the #property and implementation space for variable declaration that would prevent me from doing other mistakes like this.
Thanks a lot.
If you needed to declare that as unsigned char*, then malloc/calloc/realloc/free are your friends.
In reality, making that ivar NSMutableData and using APIs like -appendBytes:length: should be all that's necessary.
Use NSMutableData:
#property (strong) NSMutableData *recievedData;
It provides a nice wrapper around an arbitrary piece of memory who's contents and length can change at any time - and most importantly it fits in perfectly with the objective-c runtime and memory management.
Just create an empty (0 length) data object like this:
self.receivedData = [[NSMutableData alloc] init];
And then at any time you can do:
[self.recivedData appendBytes:bytes length:length];
Or:
[self.recievedBytes appendData:anotherNSDataObj];
From there you can read it with the getBytes: method.
There are other ways to do it, by declaring lower level instance variables, but #property is the modern approach. The older style of instance variable is able to what you tried, but we are moving away from them and it will probably be deprecated soon.
Related
Appending new string to old string is causing crash.
(abnormally works if i do as like this StructIOS.server = #""; StructIOS.server = [StructIOS.server stringByAppendingString:#".stackoverflow.com"];).
struct.h:
struct iOS {
__unsafe_unretained NSString *input_url;
__unsafe_unretained NSString *use_url;
__unsafe_unretained NSString *server;
};
struct iOS StructIOS;
ViewController.m:
StructIOS.use_url = #"relay/pincode/apicode/accesskey/1/2/3/99/../900";
NSArray *work_array = [StructIOS.use_url componentsSeparatedByString:#"/"];
StructIOS.server = [work_array objectAtIndex:0];
if([StructIOS.server length] > 0) {
NSLog(#">>> 4: %#", StructIOS.server); // OK!!
StructIOS.server = [StructIOS.server stringByAppendingString:#".stackoverflow.com"]; // FAIL!!
NSLog(#">>> 5: %#", StructIOS.server);
}
Output:
>>> 4: relay
crash
Expected output:
>>> 5: relay.stackoverflow.com
EDIT: following way worked without crash
NSString *fool_ios;
// read from NSString type
StructIOS.server = fool_ios;
// save to NSString type
fool_ios = StructIOS.server;
The answer is two-fold:
Don't store objects in Objective-C Structs. ARC won't manage the memory for them.
Don't use unsafe_unretained unless you understand exactly what it does and exactly why you need it.
Simply make your variables instance variables of your class. That will make them strong, which is what you want.
EDIT:
Note that in Swift, it is valid to store objects in a Struct. Swift is able to memory manage them inside a struct, where C does not.
Any time the compiler forces you to use __unsafe_unretained, you're likely doing something wrong. (There are exceptions to that, but at your level of understanding, you should pretend that __unsafe_unretained doesn't exist.)
Why are they __unsafe_unretailed?
componentsSeparatedByString() will presumably internally be creating some substrings which you are not taking ownership of due to using __unsafe_unretained, thus they are being deleted when componentsSeparatedByString function got out of scope.
The log at line 4 is working purely though chance and good luck as the string is deallocated but still there at that memory location.
If you rewrite the struct as a simple class (which inherits from NSObject) it should work.
I'm trying to simulate the behaviour of a component under heavy memory load.
The main idea is to allocate a bunch of memory, keep it resident, then work with my component, however no matter what I do, the memory I allocate on purpose seem do disappear somehow.
After a first test using a strong NSArray of NSData chunks (1Mb each), I tried with a rougher but lower level approach (to prevent some ARC magic to happen):
// somewhere after the includes
static char *foo[MAX_CHUNKS];
[...]
// this function is actually called, I checked :)
- (void) allocateMemoryChunks:(int) chunks ofSize:(long) bytes {
for(int i = 0; i < chunks; ++i) {
foo[i] = (char *) malloc(bytes);
}
}
No matter what I set for chunks and bytes, as long as the memory can be allocated, it 'disappears' from the memory count in Xcode and leaves me with the usual meager 2Mb consumed by the application (I'm talking of allocations in the range of 400 chunks of 1024 * 1024 bytes, or 1 chunk of 400 * 1024 * 1024 bytes, for what's worth...).
The memory is clearly outside the reach of ARC (I'm using plain C mallocs!) yet, I can't see it (note that the same happened when I used NSData and NSArray to hold the data).
Where does that memory go? What am I forgetting?
Thank you!
Addendum
For the records, the ARC version of the allocateMemoryChunks is:
- (void) allocateMemoryChunks2:(int) nchunks ofSize:(long) bytes {
self.allocated = [NSMutableArray arrayWithCapacity:nchunks];
for(int i = 0; i < nchunks; ++i) {
char *tmpraw = (char *) malloc(bytes);
NSData *tmp = [NSData dataWithBytes:tmpraw length:bytes];
[self.allocated addObject:tmp];
free(tmpraw);
}
}
with allocated declared as:
#property (nonatomic, strong) NSMutableArray *allocated;
I have spent many hours trying to find a solution, so if it IS here somewhere and I missed it I am sorry ...
In .h
#property (nonatomic, strong) NSArray *physicalMan;
-(int) getManeuverRating:(int *) value;
In .m
physicalMan = [[NSArray alloc] initWithObjects:#"Grip-Hold", #"Strike", #"Fall", #"Response", nil];
NSLog(#" The second element is: %#", [physicalMan objectAtIndex:1]);
NSLog (#" This is the index location of Grip-Hold: %i", [physicalMan indexOfObject:#"Grip-Hold"]);
[self getManeuverRating:[physicalMan indexOfObject:#"Grip-Hold"]];
}
-(int) getManeuverRating:(int *) value
{
int a = *value;
return a + 1;
}
The NSLogs work fine with the proper values, which is why I am so confused as to why the function will not work.
The compiler warning says "Incompatible integer to pointer conversion sending 'NSUInteger' (aka 'unsigned int') to parameter of type 'int *'"
I have tried removing the * and I have tried to find other data types, and converting data types, and I cannot get anything to work correctly. Please help or point me in the right direction ... what am I doing wrong? what am I missing?
The indexOfObject: method of NSArray returns an NSUInteger, not an int*. Passing an int to a method that takes int* is incorrect: the value at the corresponding memory location would not be valid.
You should change the getManeuverRating: method as follows:
-(int) getManeuverRating:(NSUInteger) value
{
return value + 1;
}
You are not pointing to an int... you should make this function
-(NSInteger)getManeuverRating:(NSInteger) value
{
NSinteger a = value;
return a + 1;
}
If that is giving you issues you should also try casting the integer in the initial function...
So instead of
[self getManeuverRating:[physicalMan indexOfObject:#"Grip-Hold"]];
Do
NSInteger index = (NSInteger) [physicalMan indexOfObject:#"Grip-Hold"];
[self getManeuverRating:index];
You should be using NSInteger instead of int simply because it is good to write in objective-c syntax. But it is just a wrapper. You could also make it take and return an NSUInteger and not cast it.
Another modernization thing you could do (and this is an aside) is declare your array like this...
NSArray * physicalMan = #[#"Grip-Hold", #"Strike", #"Fall", #"Response"];
I'm trying to figure out if there is a general way to essentially wipe or encrypt the memory associated with NSObjects. I don't really care if it's a bit cumbersome, I just want to make sure it really can't be read.
For example if I have an
NSString* str = [[NSString alloc] initWithFormat:#"TESTING"];
it's relatively simple to do
unsigned char* strPtr = (unsigned char *) CFStringGetCStringPtr
((CFStringRef) str, CFStringGetSystemEncoding());
memset(strPtr, 0, [str length]);
And I can do similar things for NSData. But I would really like to have something more general.
I have looked into, with no luck:
Zones, which seem like they are no longer used.
Creating a parent class of NSObject and casting everything to that and keeping track of the memory regions. This has just been a complete pain though.
Encrypting the actual contents of the data and forcing a decrypt on access. This gets back to the problem of knowing something about every single type.
Encrypting then decrypting memory regions, can't find a way to reliably know where in memory a general object is.
Any hints or ideas would be greatly appreciated.
You can add a category to NSObject that uses reflection via the runtime API. This can be used to clear ivars/properties, even properties declared as readonly.
One drawback is that it does not clear certain properties, such as the frame of a UIView, and I'm not sure why it misses that.
NSObject+Scrub.h:
#interface NSObject (Scrub)
- (void) scrub;
#end
NSObject+Scrub.m:
#import "NSObject+Scrub.h"
#import <objc/runtime.h>
#implementation NSObject (Scrub)
- (void) scrub
{
Class myClass = [self class];
unsigned int count;
//Scrub the Ivars
Ivar *ivars = class_copyIvarList(myClass, &count);
for (int i = 0; i < count ; i++) {
Ivar ivar = ivars[i];
object_setIvar(self, ivar, nil);
}
free(ivars);
}
#end
Good luck!
I have the following example class:
Test.h:
#interface Test : UIButton {
NSString *value;
}
- (id)initWithValue:(NSString *)newValue;
#property(copy) NSString *value;
Test.m:
#implementation Test
#synthesize value;
- (id)initWithValue:(NSString *)newValue {
[super init];
NSLog(#"before nil value has retain count of %d", [value retainCount]);
value = nil;
NSLog(#"on nil value has retain count of %d", [value retainCount]);
value = newValue;
NSLog(#"after init value has retain count of %d", [value retainCount]);
return self;
}
Which produces the following output:
2008-12-31 09:31:41.755 Concentration[18604:20b] before nil value has retain count of 0
2008-12-31 09:31:41.756 Concentration[18604:20b] on nil value has retain count of 0
2008-12-31 09:31:41.757 Concentration[18604:20b] after init value has retain count of 2147483647
I am calling it like:
Test *test = [[Test alloc] initWithValue:#"some text"];
Shouldn't value have a retain count of 1? What am I missing?
Thanks for your help.
Don't look at retain counts. They're not useful and will only mislead you — you can't be certain that nothing else is retaining an object, that an object you get from somewhere isn't shared.
Instead, concentrate on object ownership and follow the Cocoa memory management rules to the letter. That way your memory management will be correct no matter what optimizations Cocoa may be doing behind the scenes for you. (For example, implementing -copy as just -retain for immutable objects.)
Furthermore, it's critical to understand the difference between properties of your objects and instance variables within your objects. In your question's code, you are assigning a value to an instance variable. That instance variable is just that: a variable. Assigning to it will behave like any other variable assignment. To use the property, you must use either dot syntax or bracket syntax to actually invoke the property's setter method:
self.value = newValue; // this is exactly equivalent to the next line
[self setValue:newValue]; // this is exactly equivalent to the previous line
The code generated for the dot syntax and the bracket syntax is identical, and neither will access the instance variable directly.
You are passing in a literal string. The compiler probably allocates it in static memory and sets the retain count to the maximum possible value.
Try a dynamically allocated string instead and see what happens.
NSString* string = [[NSString alloc] initWithString: #"some text"];
Test* test = [[Test alloc] initWithValue: string];
You've got a reference to an immutable string. Assignment doesn't need to copy the value (the string data) since it's immutable. If you do a mutable operation, like value = [newValue uppercaseString] then it should copy the bits into value, and value's retain count incremented.
You're passing in a string constant, which can't really be deallocated. I think that 2147483647 is probably UINT_MAX, which basically means that the object can't be released.
I think you want to do this:
self.value = newValue;
which will invoke the property setter and cause the copy to occur. "value = newValue" simply assigns a pointer value to the instance variable.
You shouldn't be paying attention to the retain counts, just follow the Cocoa memory management rules. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
hmm.. we're getting closer.
it appears that newValue's retain count is also 2147483647.
I tried dynamically allocating the string instead with the same retain count results.
I found a helpful article here: http://www.cocoadev.com/index.pl?NSString
FTA:
Does the NSString returned by #"" need to be released, or is it autoreleased?
Neither. #""-strings are of class NSConstantString?, and thus act like atoms in lisp; they hang around. That is, if you use #"cow" in two separate places in your code, they will be referencing the very same object.
I don't think -release or -autorelease does anything to either of them.
If I have "copy" on the property though, shouldn't it copy the contents of the target memory into new memory with a retain count of 1? It would seem the copy attribute does nothing in this case?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
char *cstr = "this is a c string";
NSString *str = [[NSString alloc] initWithUTF8String:cstr];
NSLog(#"rc1: %d", [str retainCount]);
[pool drain];
return 0;
}
If you run the above code, it will display a retain count of 1
In Cocoa, many immutable objects will simply retain themselves when you ask for a copy within the same zone. If the object is guaranteed not to change (i.e. its immutableness) then an exact duplicate is redundant.
In Objective-C, the constant string class is separate to Cocoa's NSString class, although it may be a subclass of NSString (I'm not too sure). This constant string class may override NSObject's methods like retain, release and dealloc so that they do nothing, and also override retainCount so that it always returns the same number, UINT_MAX or so. This is because an Objective-C constant string is created in static memory. It must have the overall general behaviour of a Cocoa object (when using Cocoa) so that it can be added to arrays, used as keys to a dictionary etc, except in regards to its memory management, since it was allocated differently.
Disclaimer: I don't actually know what I'm talking about.