Very simple question. Is it possible to create a class which is a list by it self? I mean:
I do
taskList *taskList1 = [[taskList alloc] init];
And than simply:
taskList1 addObject:[task1]
May seem stupid, but I'm totally new to O-C syntax
I'd need two methods:
-(instancetype) init;
which just initialize as an empty list
+(instancetype)taskList;
to allocate taskList instance
and last thing:
In interface i use:
#interface taskList : NSObject
or
#interface taskList : NSMuttableArray
I got stuck on something specific, didn't I? I'm sorry that I bother you with my programming level.
Alright, I gave up, just last question, because I have to finish it very soon.
I changed my approach I added
#property NSMutableArray *list;
Why does this:
taskList *TL1 =[taskList initTaskList];
task *task1 = [[task alloc] init];
task *task2 = [[task alloc] init];
TL1.list addObject:[task1];
doesn't work, I have "Expected identifier" Error
If you read the subclassing notes on NSArray / NSMutableArray you'll see that Apple recommend against subclassing them because they are a class cluster. (i.e. what you really get when you ask for one is an undocumented subclass, and the initialiser decides which undocumented subclass to return to you based on some undocumented qualifiers..
So just make an NSObject subclass which owns a (private) strong property of type NSMutableArray, and publish an api to access that array..
eg
#import "modelList.h"
//dont worry header is empty, its up to you to do that.. this is a subclass on NSObject
#interface modelList()
#property (strong, nonatomic) NSMutableArray *backingArray;
#end
#implementation modelList
#synthesize backingArray = _backingArray;
-(instancetype )init{
if (self = [super init]) {
[self setBackingArray:[[NSMutableArray alloc]init]];
}
return self;
}
//public API (the stuff that is prototyped in the header..)
-(id)objectAtIndex:(NSUInteger )index{
return [self.backingArray objectAtIndex:index];
}
-(BOOL )containsObject:(id)object{
return [self.backingArray containsObject:object];
}
-(void )addObject:(id)object{
//example application, qualifying object..
if ([object conformsToProtocol:#protocol(NSCoding)]) {
[self.backingArray addObject:object];
}
}
-(NSUInteger )count{
return [self.backingArray count];
}
//etc etc declare publicly the things you need to get the job done
#end
so far this is just a face for a mutable array obviously, but it gives you a place for whatever other model logic you need. good luck
I'm trying to initialize an object under the implementation section of my program since I'm planning to use the same object for multiple methods. I'm getting an error by trying to do this and I was just wondering why. Here are some examples below:
#implementation Fraction {
NSString *test = [[NSString alloc] init];
}
OR
#implementation Fraction {
int x = 0;
}
Although if you don't initialize the variables, they work fine without any errors or warnings. I'm sure this is how the code was designed, but I was just curious as to why. Thanks in advance for your answers!
The curly brace section of #implementation is only for declaring instance variables. You can't initialize them or put any other code there.
The proper place to initialize the instance variables is in an init method:
#implementaiton Fraction {
NSString *test;
int x;
}
- (id)init {
if ((self = [super init])) {
test = #"";
x = 0;
}
return self;
}
By surrounding #implementation in braces, you're declaring iVars, not declaring constants. And even if you weren't trying to declare a constant, you need to move your initialization into an -init flavored method if you wish the variable to hold an "initial value". If you were trying to declare a constant, it needs to be done outside of an #implementation block.
In #implementation section you cannot initialize any of your variables, only declare them. For other things use - (id) init method, since it is logically called after allocation, like this: [[CustomObjectClass alloc] init]; In addition, to declare private variables, it is suggested to use class extensions in your .m file like this: #interface CustomClassName()
I have a class which contains a method to download data from my server. This is in the form of JSON, but is parsed and put into an array. As this is a 'model' file, what would be the best way to make it so that multiple 'controllers' can access this data?
Hang it off the app delegate, stick it in a global variable, or declare a class method in appropriate spot that returns it.
Better yet, start thinking now about having a proper model layer that, among other things, can manage this data. That will make refactoring easier in the future as your application evolves.
You can create singleton object of class with array object. and by using class method you can get that array any where in app.
e.g.
in Cardnames.h file
#import <Foundation/Foundation.h>
#interface CardNames : NSObject
#property (nonatomic, retain) NSMutableArray *CardNamesArray;
+(CardNames*) getCardsList;
#end
in Cardnames.m file
#import "CardNames.h"
#implementation CardNames
#synthesize CardNamesArray;
static CardNames *cards = nil;
+(CardNames*) getCardsList
{
#synchronized(self)
{
if(cards == nil)
{
cards = [[self alloc] init];
cards.CardNamesArray = [[NSMutableArray alloc] init];
/// code to fill array
}
return cards;
}
}
#end
and in other classes u can use like this
#import "CardNames.h"
.
.
.
NSMutableArray *CardsArray = [[CardNames getCardsList] CardNamesArray];
I have a situation where it seems like I need to add instance variables to a category, but I know from Apple's docs that I can't do that. So I'm wondering what the best alternative or workaround is.
What I want to do is add a category that adds functionality to UIViewControllers. I would find it useful in all my different UIViewControllers, no matter what specific UIViewController subclass they extend, so I think a category is the best solution. To implement this functionality, I need several different methods, and I need to track data in between them, so that's what led me to wanting to create instance methods.
In case it's helpful, here's what I specifically want to do. I want to make it easier to track when the software keyboard hides and shows, so that I can resize content in my view. I've found that the only way to do it reliably is to put code in four different UIViewController methods, and track extra data in instance variables. So those methods and instance variables are what I'd like to put into a category, so I don't have to copy-paste them each time I need to handle the software keyboard. (If there's a simpler solution for this exact problem, that's fine too--but I would still like to know the answer to category instance variables for future reference!)
Yes you can do this, but since you're asking, I have to ask: Are you absolutely sure that you need to? (If you say "yes", then go back, figure out what you want to do, and see if there's a different way to do it)
However, if you really want to inject storage into a class you don't control, use an associative reference.
Recently, I needed to do this (add state to a Category). #Dave DeLong has the correct perspective on this. In researching the best approach, I found a great blog post by Tom Harrington. I like #JeremyP's idea of using #property declarations on the Category, but not his particular implementation (not a fan of the global singleton or holding global references). Associative References are the way to go.
Here's code to add (what appear to be) ivars to your Category. I've blogged about this in detail here.
In File.h, the caller only sees the clean, high-level abstraction:
#interface UIViewController (MyCategory)
#property (retain,nonatomic) NSUInteger someObject;
#end
In File.m, we can implement the #property (NOTE: These cannot be #synthesize'd):
#implementation UIViewController (MyCategory)
- (NSUInteger)someObject
{
return [MyCategoryIVars fetch:self].someObject;
}
- (void)setSomeObject:(NSUInteger)obj
{
[MyCategoryIVars fetch:self].someObject = obj;
}
We also need to declare and define the class MyCategoryIVars. For ease of understanding, I've explained this out of proper compilation order. The #interface needs to be placed before the Category #implementation.
#interface MyCategoryIVars : NSObject
#property (retain,nonatomic) NSUInteger someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance;
#end
#implementation MyCategoryIVars
#synthesize someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance
{
static void *compactFetchIVarKey = &compactFetchIVarKey;
MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
if (ivars == nil) {
ivars = [[MyCategoryIVars alloc] init];
objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[ivars release];
}
return ivars;
}
- (id)init
{
self = [super init];
return self;
}
- (void)dealloc
{
self.someObject = nil;
[super dealloc];
}
#end
The above code declares and implements the class which holds our ivars (someObject). As we cannot really extend UIViewController, this will have to do.
I believe it is now possible to add synthesized properties to a category and the instance variables are automagically created, but I've never tried it so I'm not sure if it will work.
A more hacky solution:
Create a singleton NSDictionary which will have the UIViewController as the key (or rather its address wrapped as an NSValue) and the value of your property as its value.
Create getter and setter for the property that actually goes to the dictionary to get/set the property.
#interface UIViewController(MyProperty)
#property (nonatomic, retain) id myProperty;
#property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;
#end
#implementation UIViewController(MyProperty)
-(NSMutableDictionary*) propertyDictionary
{
static NSMutableDictionary* theDictionary = nil;
if (theDictionary == nil)
{
theDictioanry = [[NSMutableDictionary alloc] init];
}
return theDictionary;
}
-(id) myProperty
{
NSValue* key = [NSValue valueWithPointer: self];
return [[self propertyDictionary] objectForKey: key];
}
-(void) setMyProperty: (id) newValue
{
NSValue* key = [NSValue valueWithPointer: self];
[[self propertyDictionary] setObject: newValue forKey: key];
}
#end
Two potential problems with the above approach:
there's no way to remove keys of view controllers that have been deallocated. As long as you are only tracking a handful, that shouldn't be a problem. Or you could add a method to delete a key from the dictionary once you know you are done with it.
I'm not 100% certain that the isEqual: method of NSValue compares content (i.e. the wrapped pointer) to determine equality or if it just compares self to see if the comparison object is the exact same NSValue. If the latter, you'll have to use NSNumber instead of NSValue for the keys (NSNumber numberWithUnsignedLong: will do the trick on both 32 bit and 64 bit platforms).
This is best achieved using the built-in ObjC feature Associated Objects (aka Associated References), in the example below just change to your category and replace associatedObject with your variable name.
NSObject+AssociatedObject.h
#interface NSObject (AssociatedObject)
#property (nonatomic, strong) id associatedObject;
#end
NSObject+AssociatedObject.m
#import <objc/runtime.h>
#implementation NSObject (AssociatedObject)
#dynamic associatedObject;
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, #selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, #selector(associatedObject));
}
See here for the full tutorial:
http://nshipster.com/associated-objects/
It mentioned in many document's online that you can't create create new variable in category but I found a very simple way to achieve that. Here is the way that let declare new variable in category.
In Your .h file
#interface UIButton (Default)
#property(nonatomic) UIColor *borderColor;
#end
In your .m file
#import <objc/runtime.h>
static char borderColorKey;
#implementation UIButton (Default)
- (UIColor *)borderColor
{
return objc_getAssociatedObject(self, &borderColorKey);
}
- (void)setBorderColor:(UIColor *)borderColor
{
objc_setAssociatedObject(self, &borderColorKey,
borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.layer.borderColor=borderColor.CGColor;
}
#end
That's it now you have the new variable.
Why not simply create a subclass of UIViewController, add the functionality to that, then use that class (or a subclass thereof) instead?
Depending on what you're doing, you may want to use Static Category Methods.
So, I assume you've got this kind of problem:
ScrollView has a couple of textedits in them. User types on text edit, you want to scroll the scroll view so the text edit is visible above the keyboard.
+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView
{
// scroll view to someviews's position or some such.
}
returning from this wouldn't necessarily require the view to move back, and so it doesn't need to store anything.
But that's all I can thinkof without code examples, sorry.
I believe it is possible to add variables to a class using the Obj-C runtime.
I found this discussion also.
I am really new to objective C, and I want to make a class that is an NSArray of NSDictionary, and then have a method that grabs a random entries. I know how to make that but I don't understand how to make it in the class. What I mean is I thought that you could put the code that declared (or whatever the correct terminology is) the array just sort of in the middle of the implementation file and then I would write a method under that. The only instance variable I had was the NSArray and that was in the interface file, along with the method prototype (or whatever) and these were the only things that were in the interface file.
I couldn't figure out the problem so I made a test class that was the same but with just an array of simple text strings. I used the same logic here and I'm pretty certain it is totally backward, I don't know in which way though.
This is the interface for the test class:
#import <Foundation/Foundation.h>
#interface TestClass : NSObject {
NSArray *TestArray;
}
#end
And this is the implementation file
#import "TestClass.h"
#implementation TestClass{
NSArray *TestArray;
}
TestArray = [[NSArray alloc] arrayWithObjects:#"stuff",#"things",#"example",#"stuffThings",nil];
#end
You should really read Apple's introduction to Objective-C. It explains the syntax and structure of the language. You must also read the Objective-C memory management guide so that your programs don't leak memory and crash.
Having said that, here's probably what you're trying to create (I took the liberty of changing some of your variable names):
TestClass.h
#import <Foundation/Foundation.h>
#interface TestClass : NSObject {
NSArray* strings_;
}
// Method declarations would go here
// example:
- (NSString*)randomElement;
#end
TestClass.m
#import "TestClass.h"
#import <stdlib.h>
// Notice how the implementation does NOT redefine the instance variables.
#implementation TestClass
// All code must be in a method definition.
// init is analogous to the default constructor in other languages
- (id)init {
if (self = [super init]) {
strings_ = [[NSArray alloc] initWithObjects:#"stuff", #"things", nil];
}
return self;
}
// dealloc is the destructor (note the call to super).
- (void)dealloc {
[strings_ release];
[super dealloc];
}
- (NSString*)randomElement {
NSUInteger index = arc4random() % [strings_ count];
return [strings_ objectAtIndex:index];
}
#end
For random number generation, it's easy to use arc4random() because it doesn't require setting the seed value.