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.
Related
I have a simple PFObject subclass setup. MyPFSubclass. It looks something like this:
#interface MyPFSubclass: PFObject <PFSubclassing>
+ (NSString *)parseClassName;
#property (retain) NSString *myString;
#end
#import <Parse/PFObject+Subclass.h>
#implementation MyPFSubclass
#dynamic myString;
+ (NSString *)parseClassName {
return #”MyPFSubclass”;
}
#end
This works great, as expected, until what I discovered today.
I can set the myString value and read and write as expected, an NSLog shows the data to be what I set it to.
MyPFSubclass *obj = [MyPFSubclass new];
obj.myString = "#hello";
//prints expected value as set above
NSLog(#"%#", obj.myString);
obj[#"myString"] = "#hello";
//prints expected value as set above, again
NSLog(#"%#", obj[#"myString"]);
However, if I do the following, I do not get the changed result.
obj[#"myString"] = #"Hello";
//prints original value, not as set above
NSLog(#"%#", obj.myString);
It seems the key name setters and getters are independent to the subclass setters and getters. I don't want this!
As an example, I have a subclassed view that takes a generic PFObject and key name from which it can get and set values.
How can I resolve this? Any reason why I cannot mix usage of the subclass and keyname getters and setters?
Not an exact answer, however it is a solution.
I found that in the scenario of using a PFObject subclass, using setValue:forKeyPath is more reliable than using Parse's own bracketing syntax. i.e. myObject[#"myAttribute"].
Where this latter appears non-interchangeable with subclass properties, I found replacing it with setValue:forKeyPath works.
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;
Trying to extend the capabilities from a open source project, I wrote a category for add a new method. In this new method, the category needs to access to an internal method from the original class, but the compiler says that it can't find the method (of course, is internal). Is there any way to expose this method for the category?
EDIT
I don't want to modify the original code, so I don't want to declare the internal method in the original class header file.
The code
In the original class implementation file (.m), I have this method implementation:
+(NSDictionary*) storeKitItems
{
return [NSDictionary dictionaryWithContentsOfFile:
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:
#"MKStoreKitConfigs.plist"]];
}
In the category, I want to add this method:
- (void)requestProductData:(NSArray *(^)())loadIdentifierBlock
{
NSMutableArray *productsArray = [NSMutableArray array];
NSArray *consumables = [[[MKStoreManager storeKitItems] objectForKey:#"Consumables"] allKeys];
NSArray *nonConsumables = [[MKStoreManager storeKitItems] objectForKey:#"Non-Consumables"];
NSArray *subscriptions = [[[MKStoreManager storeKitItems] objectForKey:#"Subscriptions"] allKeys];
if(loadIdentifierBlock != nil) [productsArray addObjectsFromArray:loadIdentifierBlock()];
[productsArray addObjectsFromArray:consumables];
[productsArray addObjectsFromArray:nonConsumables];
[productsArray addObjectsFromArray:subscriptions];
self.productsRequest.delegate = self;
[self.productsRequest start];
}
In every line in which I call storeKitItemscompiler says: Class method "+storeKitItems" not found ...
This is trivial, make a forward declaration of the method.
Unfortunately, in obj-c, every method declaration must be inside #interface, so you can make it work in your category .m file with another internal category, e.g.
#interface MKStoreManager (CategoryInternal)
+ (NSDictionary*)storeKitItems;
#end
No implementation is needed, this only tells the compiler the method is somewhere else, similarly to #dynamic with properties.
If you are only interested in removing the warning, you can also just cast the class to id, the following should work, too:
NSDictionary* dictionary = [(id) [MKStoreManager class] storeKitItems];
However, my favorite solution is to do it a bit differently, let's assume the following example:
#interface MyClass
#end
#implementation MyClass
-(void)internalMethod {
}
#end
#interface MyClass (SomeFunctionality)
#end
#implementation MyClass (SomeFunctionality)
-(void)someMethod {
//WARNING HERE!
[self internalMethod];
}
#end
My solution is to split the class into two parts:
#interface MyClass
#end
#implementation MyClass
#end
#interface MyClass (Internal)
-(void)internalMethod;
#end
#implementation MyClass (Internal)
-(void)internalMethod {
}
#end
And include MyClass+Internal.h from both MyClass.m and MyClass+SomeFunctionality.m
A category has no access to the private methods of a class. It's no different than trying to call those methods from any other class. At least if you call the private method directly. Since Objective-C is so dynamic, you can call private methods (which is a bad idea) using other means such as using performSelector or with NSInvocation.
Again, this is a bad idea. An update to the implementation of the class could break your category.
Edit: Now that there is code posted -
Since the +storeKitItems method is not declared in the .h file, no category or other class can access the private method.
In you category implementation file you can define and informal protocol for the method
#interface YourClasses (ExternalMethods)
+(NSDictionary*) storeKitItems;
#end
This will stop the compiler from complaining about not knowing of the method storeKitItems in you category.
In my iOS project i am creating an NSArray that can contains integer values. There are several functions in this class that do their task on that NSArray. So i thought to create NSArray as private attribute of that class. In viewDidLoad i am allocating memory to this array i.e
myArray = [[NSArray alloc] init];
myArray will be used in several method of this class. When i analyze my code it shows memory leak as i am allocating myArray and not releasing it.
If i write [myArray release] in dealloc or viewDidUnload warning still there. If i release myArray in last method of class that is using this array, xCode wont allow me to do.
Here is How i am declaring it in my class
.h file
#interface FightVC : UIViewController
{
NSArray *myArray;
}
I want to know what is possible solution of this. other then using autorelease . I don't want to make it public so i am not writing this array as property.
Thanks in advance
Using a private #property as mentioned in the other answer is probably the nicest and cleanest way to do this. Raw instance variables aren’t a very modern way of doing things. However, if you are going down that road, you can implement a getter for your ivar in which you release the old ivar and retain the new one:
- (void)setMyArray:(NSArray *)array {
[myArray release];
myArray = [array retain];
}
That’s the typical pattern anyway (which is what having an #property does for you automatically).
After that, you can create the array, use the setter, and then release the object:
NSArray *newArray = [[NSArray alloc] init];
[self setMyArray:newArray];
[newArray release];
That should keep the analyzer from squawking at you. A few things stick out to me though:
1) [[NSArray alloc] init] isn’t likely to do what you want it to do. It’s going to create an empty, immutable array. You probably either want an NSMutableArray, or you want to instantiate it with objects already in it using a different initializer.
2) NSArrays aren’t really suited for holding integers themselves, they hold objects. You can either use an NSPointerArray or you can put the integers into NSNumbers and put them into an NSArray.
To make a property private, you have to create an anonymous category.
In your .m file:
#interface myClass ()
#property (nonatomic, retain) NSArray *myPrivateArray;
#end
#implementation myClass
// Class code here
#end
To release your array, simply set the property to nil.
self.myPrivateArray = nil;
I have an array called client in one of my classes and I want to use the information in that array in another class that i have. I have set up the property and synthesized the array in my first class. The code for my first class is
#synthesize client;
...
- (IBAction)testing:(id)sender {
NSString *textContent = myTextView.text;
textContent = [textContent stringByReplacingOccurrencesOfString:#" " withString:#""];
client = [textContent componentsSeparatedByString:#"."];
NSLog(#"%#", client);
}
In my second class I tried importing the h file for my first class and then just accessing the array. The code that I am using is
- (IBAction)ButtonStuff:(id)sender {
ArrayManipulationViewController *myClass = [[ArrayManipulationViewController alloc]init];
NSLog(#"Second Interface");
NSArray *test = myClass.client;
NSLog(#"%#", test);
}
To access object from multiple classes, a common approach is to declare the object in the parent class, and then pass a shared instance of that object to all child classes that require access. For instance, you could declare the array in the AppDelegate, and set array properties in your subclasses, and pass the instance of the array from the AppDelegate to all your subclasses.
Eg: create an NSArray (myArray) in your app delegate, then in the AppDelegate implantation, pass the myArray instance to your sub view controllers using properties.
Or, if you'd prefer; you can declare the array in your first class, and then pass the array instance from your first class to your second class using properties. Then, any changes made in your second class will be available in your first class, since the INSTANCE is the same.
UPDATED ANSWER:
For the second approach, you're best declaring the array in your first class implementation, and then when you instantiate the second class, pass the instance of the array to the second class using properties. In this example, you'll need to have an NSArray property in your second class to be able to pass the array over to it using [secondClassInstance setClient: client];
Your second class interface might look like this:
#interface SecondClass : NSObject
{
NSArray *client;
}
#property (nonatomic, retain) NSArray *client; // don't forget to synthesize
#end
Then, in your first class, you can do the following to pass over your instance of the array:
NSArray *client = [[NSArray alloc] initWithObjects:#"Object 1", #"Object 2"];
//...
SecondClass *secondClass = [[SecondClass alloc] init];
[secondClass setClient: client]; // passing the original client instance here
// don't forget to release secondClass instance when finished