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;
Related
I have class Project. I need to create class CheckboxProject that will be used with checkbox list, it will have property 'checked' and I will check/uncheck items.
So, I use inheritance and did it. But dataSource array is contains Project classes. How can I convert this array to CheckboxProject classes?
#interface Project : NSObject
#property (nonatomic, copy) NSString *name;
#end
#interface CheckboxProject : Project
#property (nonatomic) BOOL checked;
#end
I have dataSourse array of Project. I need to convert it to array of CheckboxProject and then I need be able to change checked to YES/NO.
I need to get array of CheckboxProject from array of Project
Array in obj-c isnt type safe so you can put what ever you want inside.
You can check at runtime the class type
id obj = [array objectAtIndex:index];
if([obj isKindOfClass:[CheckmarkProject class]]) {
// do something with the checkmark
} else if ... // the other type of class {
}
but in any case it is bad practice to do such a thing. (I would prefer accessing the base class vars in the mix case)
I'm using this following code pass data to my third controller:
[self presentControllerWithName:#"ThirdView" context:MyArray];
The thing is, I would like to pass more than a simple array. I would like to pass a separate string, and another array if possible, and I don't want to add the string or the other array to "MyArray".
Is there a different way of going about this, or do I just restructure this code?
You can create custom object with the data you want to pass or you can bundle the data in the dictionary or array. In swift you can use tuple as well.
This is an example with dictionary:
NSDictionary *myData = #{
#"MainArray" : MyArray,
#"MyString" : #"string",
#"AnotherArray" : anotherArray
};
[self presentControllerWithName:#"ThirdView" context: myData];
Example with Array:
NSArray *myData = #[MyArray, #"string", anotherArray];
[self presentControllerWithName:#"ThirdView" context: myData];
While you could use a dictionary or an array as Greg has suggested, you get no type-safety and you have to ensure that the key names / indexes are the same in both places.
A better approach would be to subclass NSObject and provide a wrapper for the data you are wanting to transfer to that view controller.
Interface:
#interface ThirdViewState : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSArray *list;
#end
Simple empty implementation:
#implementation ThirdViewState
#end
Then you can construct an instance of this object, populate it with data and pass it to the view controller:
ThirdViewState *state = [[ThirdViewState alloc] init];
state.title = #"My 3rd view";
state.list = myArray;
[self presentControllerWithName:#"ThirdView" context:state];
Then in the third view controller's awakeWithContext: method you can pull out the data:
- (void)awakeWithContext:(ThirdViewState *)state {
[super awakeWithContext:state];
// do whatever with state
}
I have two classes each with an instance method that use the same piece of code.
This piece of code takes a NSString and return an NSArray.
Currently the same piece of code is repeated in the two classes.
Is there a way to write it separately and call it by the two classes? I tried to make a method in a subclass of NSArray, but there are many problems due to the fact that NSArray is an abstract class. Any suggestions?
Thank you.
Instead of subclassing NSArray, the correct approach to extent the behaviour of a class is to create a category on that class.
So, you can create a category on NSString that returns an array, and after you have imported that category to your project, you can call it as if it was part of NSString, for example:
NSString *myString = #"Hello";
NSArray *myArray = [myString generateArrayFromString];
You can find a guide on how to create a category here:
Customizing Existing Classes
You can try to make a NSString category. This category will return the array.
E.g.:
//
// NSString+MyCategory.h
#import
#interface NSString (MyCategory)
-(NSArray *)myMethod;
#end
//
// NSString+MyCategory.m
#import "NSString+MyCategory.h"
#implementation NSString (MyCategory)
-(NSArray *)myMethod {
NSArray *_arr = [self componentsSeparatedByString:#","];
return _arr;
}
#end
Then in your class (or whatever you want in your code) you can import the category:
#import "NSString+MyCategory.h"
and then use it on any string:
NSArray *myArray = [anyString myMethod];
From the sound of it (parsing a string into an NSArray, with on reference to the class's instance fields) you can make the method a class (vs instance) method and invoke it from either class.
Ie:
+(NSArray*)parseThisString:(NSString*)theString {
doSomething;
return result;
}
Invoke with [TheNameOfTheClass parseThisString:inputString].
Of course, if you are reverencing values in the class's instance this won't work.
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.
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