C-array as property in iOS - ios

I am programming for iOS, and using ARC.
I am trying to use a c-array as property, but it reports error.
#property (strong, nonatomic)NSString *mappingTable[70][254];
The error is "Property cannot have array or function type NSString *[70][254]". How can I solve this problem? How can I declare c-array as property?
Note:
This is a two dimensional array, I think it is much easier to just use c-array, so I didn't use NSArray for it.

Surprised this hasn't been suggested already but you can store the c-array in an NSData object. I just used this method to store an array of frames.
#property (nonatomic) NSData *framesArray;
// Create and initialize your c-style frames array
CGRect frames[numberOfFrames];
...
self.framesArray = [NSData dataWithBytes:frames length:(sizeof(CGRect) * numberOfFrames)];
// To Access the property
NSUInteger arraySize = [self.framesArray length] / sizeof(CGRect);
CGRect *frames = (CGRect *) [self.framesArray bytes];

You can't declare it in that format. As the error message states you can't use C-style arrays in property declarations.
The new shorter syntax for arrays makes NSArray and NSMutableArray less of a pain. Instead of
[array objectAtIndex:3]
you can simply use
array[3]
I think in the long run the benefit of using Objective-C objects will outweigh the comfort of using C-style arrays.

you can not declare c/c++ arrays as properties, you could either use objective-c NSArray/NSMutableArray for property or you could declare c++ array.
#property (strong,nonatomic)NSArray *mappingTable;
or declare pure c style character array like this
char mappingTable[70][224];

If you are only going to use it as a private property of the class. Then keep it simple.
skip the YourClass.h file. And write it directly in the YourClass.m file like this.
//YourClass.m file
#import "YourClass.h"
#interface YourClass()
#property (strong,nonatomic)NSArray *mappingTable;
#end
#implementation YourClass
#synthesize mappingTable;
#end

Related

Custom object to NSMutableDictionary

I would like to add a custom object to NSDictionary. It should store three variables: two strings and a boolean.
I read around the net and found NSCoder to be the way but I dislike the result. By using [dictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:customObject]] I end up with NSData information instead of human readable text. My target is to make it readable for human eyes. I do not want to encode the object into binary data.
To make it a little bit more complicated I would like to add my objects inside NSArray. For testing purposes I tried to add NSStrings to NSArray and to invoke [NSDictionary dictionaryWithObject:array forKey:#"myKey"]. The result is perfect. It is readable for human. I would like to add my custom object instead of the NSString.
Desired result should look something like this:
{
TextStrokeColor = "UIDeviceWhiteColorSpace 0.5 1";
TextStrokeWidth = 0;
MyObjects = (
MyCustomObject = {
name = "name";
boooool = 0;
description = "";
}
, ... other objects );
}
What should I use? I do not really get the difference and the use for NSCoder, NSCoding, NSCopying.
I need to be able to edit the text file later on the disk. By having binary representation I cannot. But seems there is no straightforward method.
You can simply use arrays, dictionaries, strings, numbers, dates (anything which can be written into a plist or JSON).
The question is wether this is done solely during 'archiving', or whether your in-memory representation is also arrays and dictionaries. You can also create a custom class which either uses a dictionary internally to store the data and archives / reloads from that dictionary, or the custom class is a standard class with properties and creates a dictionary on-the-fly when archiving or reloading.
Note that when using a keyed archiver, it can support setting the outputFormat to NSPropertyListXMLFormat_v1_0 for some use cases, so once your custom class implements archiving to plist data types you can easily archive the container to a plist (JSON will require more leg work from you to collate the data into true containers).
Have you tried actually creating a custom object by just making a class?
#interface MyCustomClass : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) BOOL boolValue;
#property (nonatomic, strong) NSString *description;
#end
#implementation MyCustomClass
// put any implementation methods here
#end
Then you can add it to an NSArray or an NSDictionary as you would with any other class.
MyCustomClass *myObject = [MyCustomClass new];
myObject.name = #"name";
myObject.boolValue = YES;
myObject.description #"a description";
[myMutableArray addObject:myObject];
myMutableDictionary[#"some key"] = myObject;

the SET methods in iOS when I enable ARC

Without ARC I write the set methods like this
- (void)setArr:(NSMutableArray *)arr
{
_arr = [arr retain];
}
but Xcode tell me "'retain' is unavailable: not available in automatic reference counting mode" when enable ARC.
_arr = arr; is all that is necessary. But practically, based on your code sample, there is no need to write the setter yourself. Simply declaring #property NSMutableArray *arr; in your header file is enough for the compiler to generate the setter and getter. When compiling with ARC enabled, retain and release calls are generated by the compiler for you.
When using arc, all you have to do is let the system handle the retain/release, this means:
- (void)setArr:(NSMutableArray *)arr
{
_arr = arr;
}
However, if your setter is as simple as this its just easier to let the compiler synthesize it.
Previously you had to use #synthesize, but now declaring it as a property on the header is enough.
#property (strong, nonatomic) NSMutableArray *arr;

iOS "copy" keyword on Mutable objects

I've a theoretical doubt about two type of declaration of a mutable object in iOS (and MacOSX I think) with ARC.
What's the difference between a declaration of an NSMutableArray in the Class Extension, like the code below:
#interface MyViewController ()
#property (copy) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
and a declaration of the same array in this way
#interface MyViewController ()
#property (nonatomic, strong) NSMutableArray* myMutableArray;
#end
//Class implementation
#implementation MyViewController
...
- (void)viewDidLoad
{
_myMutableArray = [#[] mutableCopy];
}
Which one is better? I've seen both versions around and apparently both work fine. However I'd like to know which one is the best option.
I know that the "copy" keyword is to use copy for classes that are part of a class cluster that have mutable/immutable pairs. So in this case, it appear to be the right choice. But the use of the "copy" keyword and the "mutableCopy" property (like the first example) seems a duplicate to me. Am I wrong?
Thanks!
The strong property is the one to use. Since its a mutable object (and is declared as such) then you wouldn't want a copy making, since then things like [self.myArray addObject:object] wouldn't work. You'd use copy properties for immutable objects that may have mutable versions passed in (so an NSString would often be a copy property).
The way the arrays are assigned (making a mutable copy of an empty array made using objective-c literals) is pretty clumsy and would be better written as self.myMutableArray = [NSMutableArray array];
Also, don't access the instance variable directly, use the property accessor.

Proper way to use instance variables/property/synthetize with ARC [duplicate]

This question already has answers here:
Declaration/definition of variables locations in ObjectiveC?
(4 answers)
Closed 9 years ago.
What is the proper way to work with instance variables (declared on interface), their #property and #synthesize, when working in ARC project? What I now do is following:
SomeClass.h:
#interface SomeClass : NSObject {
NSString *someString;
}
#property(nonatomic, copy) NSString* someString;
and SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
self.someString = #"Foobar";
}
The thing is that there are other approaches that works, like using just the #property:
SomeClass.h:
#interface SomeClass : NSObject
#property(nonatomic, copy) NSString* someString;
Accessing the someString without self:
SomeClass.m:
#implementation SomeClass
#synthesize someString;
- (void)someMethod {
someString = #"Foobar";
}
etc. I'm new to Objective-c, I'm used to Java. What is the proper way to work with attributes then? I understand that special cases will have special behavior, but what is the best approach in general? (by general I mean I want to access the variable from the class itself and from "outside" and I want ARC to still work correctly, eg. I don't have to worry about memory leaks)
For simple properties, you don't need the instance variable declaration or the #synthesize. The clang compiler will generate both for you by default. So you could write this in the header:
#interface SomeClass : NSObject
#property (nonatomic, copy) NSString *someString;
#end
And the implementation:
#implementation SomeClass
- (void)someMethod {
self.someString = #"Foobar";
}
#end
Avoid direct instance variable access unless you are in the -init method or overriding the setter. Everywhere else you should use the dot syntax (self.someString). If you do need access to the instance variable, the default synthesize will create an underscore-prefixed ivar, e.g. _someString.
Note that for classes with mutable versions like NSString/NSMutableString and NSArray/NSMutableArray the standard practice is to use a copy property. If you use strong on a string or array, the caller might pass in a mutable version and then mutate it from under you, causing hard-to-find bugs.
Check out this SO post for information about ARC.
(Edited) The "strong" attribute tells ARC to keep an object around until the object with the property is deallocated. You do need the "copy" attribute because an NSString property could have been passed in as an NSMutableString. The "copy" guarantees that the original object will be kept around. Again, I apologize for the incorrect/misleading information I originally had here.
The reason you can access the instance variable someString as well as the property self.someString is that the #synthesize someString line creates an instance variable for the property and creates methods for getting and setting the value of it. However, it is recommended that you use the property instead of directly using the instance variable because by using the instance variable, you cannot let the parent object know that you've changed one of its properties.

iOS5 Core Data and NSNumber is there autoboxing/unboxing?

Coming from Java, I'm pretty used to autoboxing, where an int is automatically wrapped to an Integer when needed, and an Integer may be unboxed into a primitive. Is there something similar that I can rely upon in iOS5?
currently, I'm using core data, and it takes a lot of typing to keep having to type
number.intValue
//or
number.boolValue
is there some way to use an NSNumber directly in equations and such? for example:
int x = 5+ nsNumberInstance;
Furthermore, every time I need to re-assign a number in the core data, I'm creating a new object like this.
managedObject.dynamicProperty = [NSNumber numberWithInt: int];
is there a better way to change the value of an already created NSNumber? What kinds of nifty shortcuts may I use to save myself from carpal tunnel 10 years from now?
Thank you!
Actually, when you are in your data model, and you use the "Create NSManagedObject subclass" menu item, there is an option that you can select titled "Use scalar properties for primitive data types".
This automatically handles this for you in many cases.
Here are some examples:
BOOL:
#property (nonatomic, retain) NSNumber * aBool;
becomes
#property (nonatomic) BOOL aBool;
NSDate:
#property (nonatomic, retain) NSDate * aDate;
becomes
#property (nonatomic) NSTimeInterval aDate;
Integer 32:
#property (nonatomic, retain) NSNumber * aNumber;
becomes
#property (nonatomic) int32_t aNumber;
and
Float:
#property (nonatomic, retain) NSNumber * aFloat;
becomes
#property (nonatomic) float aFloat;
NSDecimalNumber and NSString stay the same.
You can change these yourself in the previously generated header file if you have already generated the subclasses and the accessor methods will automatically update without having to re-generate the subclass.
Outside of Cocoa bindings, I can't think of many other places that have autoboxing of scalar types in Cocoa or Cocoa touch, so unfortunately you're out of luck there.
You don't really gain anything by working with NSNumbers in calculations, so dealing with the objects there isn't really necessary. It's much easier to work with scalar types, and then convert back and forth between NSNumbers when storing these numbers in Core Data, arrays, etc. The one case where you'd want to stay in this form would be NSDecimalNumbers, which do not represent numbers as your standard floating point values, and thus avoid the glitches you see when trying to work with decimals in those types.
Core Data stores objects, so you're not going to get around that at a base level, but you can make your life a little easier by using custom accessors on your NSManagedObject subclasses that take and return scalar values. Apple has an example of this in the "Managed Object Accessor Methods" section of the Core Data Programming Guide, where they show how to set up an accessor for a CGFloat value, instead of using an NSNumber:
#interface Circle : NSManagedObject
{
CGFloat radius;
}
#property CGFloat radius;
#end
#implementation Circle
- (CGFloat)radius
{
[self willAccessValueForKey:#"radius"];
float f = radius;
[self didAccessValueForKey:#"radius"];
return f;
}
- (void)setRadius:(CGFloat)newRadius
{
[self willChangeValueForKey:#"radius"];
radius = newRadius;
[self didChangeValueForKey:#"radius"];
}
#end
As a side note, using the dot syntax for -intValue and -boolValue, while it works, is not recommended. These are not properties, but one-way methods that extract values from the NSNumbers. Use brackets when dealing with them to make this clear in your code.

Resources