Question about creating classes in Objective-C - objective-c

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.

Related

Objective-C class as NSMutableArray

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

Prevent class from being subclassed in Objective-c

How do I prevent a particular class from being subclassed?
I am not aware of such functionality (say final keyword for example) in the language. However Apple says it has done so for all classes in AddressBookUI.framework (in iOS)
For educational purposes, how can I achieve the same functionality, or how would they have done such thing?
From iOS7 Release Notes(Requires login) :
Here's one way: override allocWithZone: from within your "final" class (substituting MyFinalClassName for your actual class name) like this:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (self != [MyFinalClassName class]) {
NSAssert(nil, #"Subclassing MyFinalClassName not allowed.");
return nil;
}
return [super allocWithZone:zone];
}
This will prevent a subclass that is not a member of MyFinalClassName from being alloc'ed (and therefore init'ed as well), since NSObject's allocWithZone: must be called eventually, and by refusing to call super from your "final" class, you will prevent this.
There's a simpler way to prevent subclassing in Xcode 6 as a result of Swift interop. To prevent Swift classes from being subclassed in Objective-C the objc_subclassing_restricted is added to all class definitions in the {ProjectName}-Swift.h file.
You can use this in your projects:
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
# define FOO_FINAL __attribute__((objc_subclassing_restricted))
#else
# define FOO_FINAL
#endif
FOO_FINAL
#interface Foo : NSObject
#end
#interface Bar : Foo
#end
The compiler will halt on the definition of Bar with Cannot subclass a class with objc_subclassing_restricted attribute
Here is possible solution:
#interface FinalClass : NSObject
#end
#implementation FinalClass
- (id)init
{
if (self.class != [FinalClass class]) {
return nil;
}
self = [super init];
if (self) {
// instance initialization
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
#end
#implementation InvalidSubclass
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
#end
I'm not sure this is 100% guaranteed because it's runtime-checking anyway, but it should be enough to block and warn people that they should not subclass this. Subclass might skip superclass's init, but then the instance will not be usable because it's not fully initialised by superclass.
Something like the following will ensure that every time an "impossible subclass" calls +alloc, an object will be allocated that is an instance of FinalClass, and not the subclass. This is essentially what NSObject's +alloc method does, but here we specify an explicit class to create. This is how NSObject allocates instances (in Obj-C 2), but there is no guarantee this will always be the case, so you may want to add an appropriate -dealloc which calls object_dispose. This method also means you don't get a nil object back if you try to instantiate a subclass - you do get an instance of FinalClass.
#interface FinalClass: NSObject
//...
+ (id)alloc; // Optional
#end
// ...
#import <objc/runtime.h>
#implementation FinalClass
+ (id)alloc {
if (![self isMemberOfClass:[FinalClass class]]) {
// Emit warning about invalid subclass being ignored.
}
self = class_createInstance([FinalClass class], 0);
if (self == nil) {
// Error handling
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
// Anything not in FinalClass will not work as +alloc will
// create a FinalClass instance.
#end
Note: I'm not sure I'd use this myself - specifying that a class shouldn't be subclassed is more in the nature of a design-contract with the programmer rather than an enforced rule at compile- or runtime.

Basic Objective C class to return an array

I am new to Objective C and am trying out some basic concepts but hitting a few brick walls
Essentially what I am trying to do is write a class that returns an array when messaged from another class
The class that returns the value would be something like -
ReturnClass.h
#import <Foundation/Foundation.h>
#interface ReturnClass : NSObject {
}
-(NSMutableArray *) getLocation ;
#end
ReturnClass.m
#import "ReturnClass.h"
#implementation ReturnClass
-(NSMutableArray *) getLocation {
//do some stuff to populate array
//return it
return latLngArray;
}
#end
I then wanted to call this in to another implementation in order to harvest the value
SomeOtherClass.m
#import "ViewController.h"
#import "ReturnClass.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *latLngArray = [[getLocation alloc] init];
}
I appreciate there is probably a fair bit wrong with this but please be gentle as I am trying to learn :)
The call would looke like this:
- (void)viewDidLoad
{
[super viewDidLoad];
ReturnClass *retClass = [[ReturnClass alloc] init];
NSMutableArray *latLngArray = [retClass getLocation];
}
This would give you the array produced by getLocation.
This code makes a new instance of ReturnClass every time it needs the array; this may or may not be what you want, because you cannot keep any state between invocations. If keeping state in ReturnClass is what you need, you would have to implement a singleton (here is a link to a question explaining a very good way of implementing singletons in Objective C).
You should review your concepts of object oriented programming in general. In particular, do not confuse a class with a method.
In your code, you have to instantiate a ReturnClass instance first to be able to call its method getLocation.
NSMutableArray *latLngArray = [[getLocation alloc] init];
For starters, this line misses the mark. You're sending +alloc to the name of a method. You should be sending that message to a class, like this:
ReturnClass *myObject = [[ReturnClass alloc] init];
and then sending messages to the resulting object, like this:
NSMutableArray *latLngArray = [myObject getLocation];

Structure (struct) in Xcode as Array, and global

I'm trying to create a structure, which should be used as an array.
Then at the same time I need to access it from a different view, so I'm guessing I need to make it global for now, until I learn a better way (have tried many suggestions from google searches)
But how do I declare a variable twice?
I mean both as my structure, and as an array?
Sometimes "global variables" can be appropriate, but not simply as "global variables". The proper way to do it in Objective C is with singletons.
Here is part of the .h implementation below:
#interface MySingleton : NSObject
#property (nonatomic, strong) NSMutableArray *myArray;
+ (MySingleton *)sharedSingleton
#end
And here is what the .m implementation would look like:
#implementation MySingletion
#synthesize myArray;
static MySingleton *shared = nil;
- (id)init
{
self = [super init];
if(self)
{
myArray = [[NSMutableArray alloc] init];
}
return self;
}
+ (MySingleton *)sharedSingleton
{
if(shared == nil)
{
shared = [[MySingleton alloc] init];
}
return shared;
}
Then, whenever you wanted to access this from a given class, you would #import "MySingleton.h" and access your array like so:
[MySingleton sharedSingleton].myArray
Edit: However, this does not mean that every time you need to pass data it should be done with a singleton. Singletons are correct in certain situations, but as others have mentioned, you should pass the data to the next class yourself in most situations.
Well i just created a new h and m fil, where i put a lot of varibales
using extend in the.h file
and alloc init in the .m file
Then i just include this file where i need the variables. Probably there is a much better way, but this was the only one i could figure out.

Is this a new way to define private instance variables in Objective-C?

I've recently updated to Xcode 4.3.2 and found that I can now declare private instance variables inside #implementation block like so:
#interface TestClass : NSObject
#property (nonatomic, copy) NSString *testProp;
#end
#implementation TestClass {
NSString *_testPropStore;
}
- (NSString *)testProp { return _testPropStore; }
- (void)setTestProp:(NSString *)testProp { _testPropStore = [testProp copy]; }
- (id)init {
if (self = [super init]) {
_testPropStore = nil;
}
return self;
}
#end
Notice the NSString *_testPropStore line inside #implementation brace block.
I've also tested with the following code:
TestClass *c1 = [[TestClass alloc] init];
TestClass *c2 = [[TestClass alloc] init];
c1.testProp = #"Hello";
c2.testProp = #"World";
NSAssert(c1.testProp == c2.testProp, #"It's working!");
Which seems to work fine. (That is, the app crashes with the "It's working" message at the NSAssert line.)
So is this a new feature of Objective-C for declaring private instance variables? Since I discovered this by accident, I would love to know if it is just for declaring private instance variables or will there be any side effects that I'm not aware of?
I couldn't find any relevant document since most questions of such type with the word private just ended up with answers on how to declare them on a private extension category which is different.
It's for real, it's the new way,* it's great, and, yes, it's in the docs. The Objective-C Programming Language, which is as close as we get to having an actual spec for the language, has the following to say:
The definition of a class is structured very much like its declaration. It begins with an #implementation directive and ends with the #end directive. In addition, the class may declare instance variables in braces after the #implementation directive:
#implementation ClassName
{
// Instance variable declarations.
}
// Method definitions.
#end
There's also a historical note a little ways back from that link, addressing the fact that we used to have to declare ivars in the interface block:
Historically, the interface required declarations of a class’s instance variables, the data structures that are part of each instance of the class. ... Instance variables represent an implementation detail, and should typically not be accessed outside of the class itself. Moreover, you can declare them in the implementation block or synthesize them using declared properties. Typically you should not, therefore, declare instance variables in the public interface and so you should omit the braces.
For the question of privacy, yes, these variables are truly private -- they act like ivars declared in the interface with the #private directive. This means that subclasses can't access them, by default. Their visibility can be changed, however, using either #protected or (if necessary for some bizarre reason) #public:
#interface Stuper : NSObject
#end
#implementation Stuper
{
#protected
NSString * sangfroid;
}
#end
#interface Stub : Stuper
- (void)setSangfroid: (NSString *)newSangfroid;
#end
#implementation Stub
- (void)setSangfroid: (NSString *)newSangfroid {
sangfroid = [newSangfroid copy];
}
*You have to use clang > 3.0, I believe, so that's just a few months ago as of this posting. GCC won't do it.
It's pretty new and it's valid as long as any compiler you need supports it.
It's great for minimizing your dependencies -- the includes and forwards may largely be in the implementation file. For this reason, and if all the compilers you use support it, then it's a better place for your ivars than in the #interface block.
A final caveat is that our current (may.2.2012) debuggers do not support this.