My own #{} literals - objective-c

As you know Apple has provided # literals for such classes as NSNumber, NSDictionary, NSArray, so we can create an object this way, for example
NSArray *array = #[obj1, obj2];
So I wonder, if there is a way to create such literals for my own classes? For example, I want to write smth. like
MyClass *object = MyClass[value1, value2];
And I don't want to write long parsers :)

# syntax are literals, which is feature of Clang compiler. Since its compiler feature, NO, you cannot define you own literals.
For more informations about compilers literals, please refer Clang 3.4 documentation - Objective-C Literals
Edit: Also, I just found this interesting SO discussion
Edit: As BooRanger mentioned at the comments, there exists method to create [] accessors (the Collection Literals way) to access custom objects. Its called Object Subscripting. Using this, you can access anything in your custom class like this myObject[#"someKey"]. Read more at NSHipster.
Here is my example implementation of "Subcriptable" object. For example simplicity, it just accesses internal dictionary. Header:
#interface LKSubscriptableObject : NSObject
// Object subscripting
- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
#end
Implementation:
#implementation LKSubscriptableObject {
NSMutableDictionary *_dictionary;
}
- (id)init
{
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (id)objectForKeyedSubscript:(id <NSCopying>)key
{
return _dictionary[key];
}
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key
{
_dictionary[key] = obj;
}
#end
You can then access to anything in this object simply using square brackets:
LKSubscriptableObject *subsObj = [[LKSubscriptableObject alloc] init];
subsObj[#"string"] = #"Value 1";
subsObj[#"number"] = #2;
subsObj[#"array"] = #[#"Arr1", #"Arr2", #"Arr3"];
NSLog(#"String: %#", subsObj[#"string"]);
NSLog(#"Number: %#", subsObj[#"number"]);
NSLog(#"Array: %#", subsObj[#"array"]);

Are you okay with this syntax?
MyClass *object = MyClass(value1, value2);
Just define macro like this:
#define MyClass(objects...) [[MyClass alloc] initWithObjects: #[objects]];
Compiler will allow class named MyClass and MyClass() macro.

Related

Objective-C class method local variables of type "self"

We're all familiar with the following pattern for instantiating instances of a class:
+ (instancetype)createInstance {
return [[self alloc] init];
}
This works because "self" in this case refers to the class rather than an object built from the class blueprint.
We also know this declaration, most commonly used in avoiding retain cycles:
typeof(self) someStrongSelf = self;
This is allows self's type to be dynamic and that code could be copy-pasted wherever needed no matter the class.
My question deals with combining the above two patterns when instantiating from a class method:
+ (instancetype)createInstance:(MyObject*)dependency {
typeof(self) instance = [[self alloc] init];
instance.dependency = dependency;
return instance;
}
This won't work because self is a class, and a typeof(class) is just a Class, but is there some mechanism for local variables equivalent to instancetype that would allow me the same flexibility as typeof(instance)? For example:
+ (instancetype)createInstance:(MyObject*)dependency {
instanceof(self) instance = [[self alloc] init]; //desired keyword
instance.dependency = dependency;
return instance;
}
If I really wanted this formalized, I know an alternative to this would be defining a protocol that does basically the same thing as above, but I'm curious if Objective-C allows the desired declaration style out of the box.
I understand what you're looking for, but there is no instanceof(self) pattern. The following achieves what you want, though admittedly doesn't have the elegance of typeof(self) pattern:
#interface Foo: NSObject
#property (nonatomic, copy) NSString *string;
#end
#implementation Foo
+ (instancetype)fooWithString:(NSString *)string {
Foo *foo = [[self alloc] init];
foo.string = string;
return foo;
}
#end
#interface Foobar: Foo
// perhaps some more properties here
#end
#implementation Foobar
// and perhaps some more methods here
#end
This implementation demonstrates that the convenience method still allows subclassing. I.e., you can do:
Foobar *foobar = [Foobar fooWithString:#"baz"];
And the resulting object will be a Foobar instance.

Initializer element is not a compile time constant

Using objective-c to write a program. I'm getting an error (initializer element is not a compile-time constant) and am not sure I follow why it's occurring. I'm just trying to initialize an array. I'm also using xcode6. My questions are: how can I rewrite this correctly in Objective-c and what would it look like in the new Swift? Also why is there an error - I don't follow how to implement some of the other threads on this question?
Name.h
#import <Foundation/Foundation.h>
#interface Name : NSObject
#property (nonatomic, retain) NSMutableArray *myArray;
#end
Name.m
#import "Name.h"
#implementation Name
NSMutableArray *myArray = [[NSMutableArray alloc] init]; //error shows up here - initializer element is not a compile-time constant
[myArray addObject:#"Object 1"];
[myArray addObject:#"Object 2"];
[myArray addObject:#"Object 3"];
#end
You should init the variable only inside a method
try override
-(id) init
{
self = [super init];
if(self)
{
myArray = [[NSMutableArray alloc] init];
}
return self;
}
Error
As the error say, you can only initialize compile time constant in the implementation of your class
This will work:
NSString* abcd = #"test";
Because #"test" is a constant and will never change after the compilation of your code.
[[NSMutableArray alloc] init] is not a constant and this is why you got an error. You will have to implement an init method to initialize your array.
Swift
For the swift part of your question:
You can still use NSArray in swift or use the swift Array type.
You can check out the Working with Cocoa Data Types Documentation or the Apple collections types Swift Documentation.
If you still want to use NSArray in swift :
var array:NSMutableArray = NSMutableArray()
array.addObject("test1")
array.addObject("test2")
// or
array:NSMutableArray = ["test1", "test2"]
Or if you want to use the swift array :
var array:String[] = ["test1", "test2"]
// or
var array:String[] = String[]()
array.append("test1")
array.append("test2")
Try to edit you "Building Settings"->"Compile Sources As" to "Objective-C++", maybe can solve your problem.

Property '' not found on object of type 'id'

I'm getting Property 'aVariable' not found on object of type id when trying to read or write aVariable to the array. Shouldn't it be known what class the object is that I added? Also noticed that it works to read the value with NSLog(#" %#",[[anArray objectAtIndex:0] aVariable]);
I'm a beginner at Objective C so it might be some simple thing I'm not getting.
AnObject
#interface AnObject : NSObject
#property (nonatomic,readwrite) int aVariable;
#end
AnotherObject
#interface AnotherObject : NSObject
#end
test.h
#import "test.h"
#implementation AnObject
#synthesize aVariable;
- (id)init
{
self = [super init];
if (self) {
aVariable=0;
}
return self;
}
#end
test.m
#implementation AnotherObject
- (id)init
{
self = [super init];
if (self) { }
return self;
}
- (NSMutableArray*) addToArray
{
NSMutableArray* anArray = [[NSMutableArray alloc] initWithCapacity:0];
AnObject* tempObject = [[AnObject alloc] init];
tempObject.aVariable=10;
[anArray addObject:tempObject];
// Property 'aVariable' not found on object of type 'id'
[anArray objectAtIndex:0].aVariable=[anArray objectAtIndex:0].aVariable + 1;
// Property 'aVariable' not found on object of type 'id'
NSLog(#" %i",[anArray objectAtIndex:0].aVariable);
// This works
NSLog(#" %i",[[anArray objectAtIndex:0] aVariable]);
return anArray;
}
#end
This code:
[anArray objectAtIndex:0].aVariable
Can be broken down into 2 sections:
[anArray objectAtIndex:0]
This returns an id- because you can put any type of object into an array. The compiler doesn't know what type is going to be returned by this method.
.aVariable
This is asking for the property aVariable on the object returned from the array - as stated above, the compiler has no idea what this object is - it certainly won't assume that it is an AnObject, just because that is what you added a line or two earlier. It has to evaluate each statement on its own. The compiler therefore gives you the error.
It is a little more forgiving when using accessor methods:
[[anArray objectAtIndex:0] aVariable];
This will give you a warning (that the object may not respond to the selector) but it will still let you run the code, and luckily enough your object does respond to that selector, so you don't get a crash. However this is not a safe thing to rely on. Compiler warnings are your friends.
If you want to use the dot notation, you need to tell the compiler what type of object is being returned from the array. This is called casting. You can either do this in two steps:
AnObject *returnedObject = [anArray objectAtIndex:0];
int value = returnedObject.aVariable;
Or with a mess of brackets:
int value = ((AnObject*)[anArray objectAtIndex:0]).aVariable;
The extra brackets are required to allow you to use dot notation when casting. If you want to use the accessor methods, you need fewer round brackets but more square brackets:
int value = [(AnObject*)[anArray objectAtIndex:0] aVariable];
-[NSArray objectAtIndex:] returns an id pointer. Since id does not contain information about your protocol the compiler cannot know the object has this property you declared; that is why it complains.
You can solve this by either cast the return value of objectAtIndex: or by using the getter/setter notation, i.e. [anArray objectAtIndex:0] setAVariable:...]. Also make sure you import your protocol definition, otherwise the compiler might also not know about the declared method and issue a warning.

Subclassing NSMutableString

I know the first thing to do when one has a question with Obj-C is to read the Apple docs.
And I did. I read p.95-p.102 of the Cocoa Fundamentals Guide. So this is what I know about composite object creation from class clusters.
Subclassing of class clusters should be done if provided functions need to be modified.
Composite objects must override primitive methods of the super class. (And I also read that the primitive methods of the super class of the super class have to be overridden as well.)
So I applied what I know.
Here's my code...
#import <Foundation/Foundation.h>
#interface GOObjectKeyName : NSMutableString
{
NSMutableString *embeddedNSMutableString;
}
//+ GOObjectKeyName;
//- init;
//From NSString
- (NSUInteger) length;
// From NSString
- (unichar) characterAtIndex: (NSUInteger) index;
// From NSString
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange;
// From NSMutableString
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString;
//- (void) dealloc;
#end
#import "GOObjectKeyName.h"
#implementation GOObjectKeyName
/*
+ GOObjectKeyName
{
return [[[self alloc] init] autorelease];
}
- init
{
self = [super init];
if (self)
{
embeddedNSMutableString = [[NSMutableString allocWithZone: [self zone]] init];
}
return self;
}
*/
- (NSUInteger) length
{
return [embeddedNSMutableString length];
}
- (unichar) characterAtIndex: (NSUInteger) index
{
return [embeddedNSMutableString characterAtIndex: index];
}
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
{
[embeddedNSMutableString getCharacters: buffer range: aRange];
}
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString
{
[embeddedNSMutableString replaceCharactersInRange: aRange withString: aString];
}
/*
- (void) dealloc
{
[embeddedNSMutableString release];
[super dealloc];
}
*/
#end
The commented methods are commented because I wasn't sure if they were necessary. I just wrote them down because I saw them in the example. And you also might see that it's basically another NSMutableString class. But don't worry, there is a bigger purpose to it, I just want to know that the basics are correct before I continue since this is my first time with composite objects.
And as you know... IT DOESN'T WORK!!
Please understand my frustration after hours of trial and error.
If someone could guide me here it would be a great relief.
Oh, and if you don't mind I have another small question.
The example on the apple docs describe the NSNumber analogy. But is NSNumber actually more efficient then using int, char, and other fundamental types?
Just curious.
It would certainly help if you would characterize the failure beyond "it doesn't work."
However, your -init method is commented out, so there's no way that embeddedMutableString is going to get created. And even if you uncomment it, you call +alloc to create embeddedMutableString, but you never initialize it.
After looking at the documentation, it seems that a subclass's -init method should look like this (warning: untested code):
-(id)init
{
if (self = [super init]) {
embeddedMutableString = [[NSMutableString alloc] init];
}
return self;
}
You will, of course, also have to provide a -dealloc that releases embeddedMutableString and calls [super dealloc].
As far as NSNumber, no, it's not more efficient than basic types like int but it does have other advantages. NSNumber is a class, so you can store instances of NSNumber in Cocoa's collection classes such as NSArray and NSSet. You can write them to property lists. NSDecimalNumber avoids the imprecision that comes from converting between decimal and binary notations. And so on...
To answer your NSNumber question, no, NSNumber is not more efficient than the C primitives. NSNumber is an encapsulation for the C primitives, essentialy allowing primitives to be inserted into objects like NSArray and NSSet. If you need that encapsulation ability, but want the speed of an actual int or float, it's simple enough to write your own NSNumber-like class that works for just one type.

NSMutableArray - force the array to hold specific object type only

Is there a way to force NSMutableArray to hold one specific object type only?
I have classes definitions as follow:
#interface Wheel:NSObject
{
int size;
float diameter;
}
#end
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray *wheels;
}
#end
How can I force wheels array to hold Wheel objects only with code? (and absolutely not other objects)
Update in 2015
This answer was first written in early 2011 and began:
What we really want is parametric polymorphism so you could declare, say, NSMutableArray<NSString>; but alas such is not available.
In 2015 Apple apparently changed this with the introduction of "lightweight generics" into Objective-C and now you can declare:
NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];
But all is not quite what it seems, notice the "lightweight"... Then notice that the initialisation part of the above declaration does not contain any generic notation. While Apple have introduced parametric collections, and adding a non-string directly to the above array, onlyStrings, as in say:
[onlyStrings addObject:#666]; // <- Warning: Incompatible pointer types...
will illicit the warning as indicated, the type security is barely skin deep. Consider the method:
- (void) push:(id)obj onto:(NSMutableArray *)array
{
[array addObject:obj];
}
and the code fragment in another method of the same class:
NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:#"asda" onto:oops]; // add a string, fine
[self push:#42 onto:oops]; // add a number, no warnings...
What Apple have implemented is essentially a hinting system to assist with automatic inter-operation with Swift, which does have a flavour of type-safe generics. However on the Objective-C side, while the compiler provides some extra hints the system is "lightweight" and type-integrity is still ultimately down to the programmer - as is the Objective-C way.
So which should you use? The new lightweight/pseudo generics, or devise your own patterns for your code? There really is no right answer, figure out what makes sense in your scenario and use it.
For example: If you are targeting interoperation with Swift you should use the lightweight generics! However if the type integrity of a collection is important in your scenario then you could combine the lightweight generics with your own code on the Objective-C side which enforces the type integrity that Swift will on its side.
The Remainder of the 2011 Answer
As another option here is a quick general subclass of NSMutableArray which you init with the kind of object you want in your monomorphic array. This option does not give you static type-checking (in as much as you ever get it in Obj-C), you get runtime exceptions on inserting the wrong type, just as you get runtime exceptions for index out of bounds etc.
This is not thoroughly tested and assumes the documentation on overriding NSMutableArray is correct...
#interface MonomorphicArray : NSMutableArray
{
Class elementClass;
NSMutableArray *realArray;
}
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;
#end
And the implementation:
#implementation MonomorphicArray
- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
elementClass = element;
realArray = [NSMutableArray arrayWithCapacity:numItems];
return self;
}
- (id) initWithClass:(Class)element
{
elementClass = element;
realArray = [NSMutableArray new];
return self;
}
// override primitive NSMutableArray methods and enforce monomorphism
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
{
[realArray insertObject:anObject atIndex:index];
}
else
{
NSException* myException = [NSException
exceptionWithName:#"InvalidAddObject"
reason:#"Added object has wrong type"
userInfo:nil];
#throw myException;
}
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[realArray removeObjectAtIndex:index];
}
// override primitive NSArray methods
- (NSUInteger) count
{
return [realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [realArray objectAtIndex:index];
}
// block all the other init's (some could be supported)
static id NotSupported()
{
NSException* myException = [NSException
exceptionWithName:#"InvalidInitializer"
reason:#"Only initWithClass: and initWithClass:andCapacity: supported"
userInfo:nil];
#throw myException;
}
- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }
#end
Use as:
MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];
[monoString addObject:#"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:#"Another string"];
Since Xcode 7, generics are available in Objective-C.
You can declare a NSMutableArray as:
NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:#[[Wheel new],[Wheel new]];
The compiler will give you a warning if you try to put non-Wheel objects in the array.
I could be wrong (I'm a noob), but I think, if you create a custom protocol and make sure the objects you are adding to the array follow the same protocol, then when you declare the array you use
NSArray<Protocol Name>
That should prevent objects being added that do not follow the said protocol.
as per i know.. before you added any object in wheels mutableArray, u have to add some check mark. Is the object which i am adding is class "wheel". if it is then add, other wise not.
Example:
if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id)
}
Something like this. i dont remember the exact syntax.
I hope this will help (and work... :P )
Wheel.h file:
#protocol Wheel
#end
#interface Wheel : NSObject
#property ...
#end
Car.h file:
#import "Wheel.h"
#interface Car:NSObject
{
NSString *model;
NSString *make;
NSMutableArray<Wheel, Optional> *wheels;
}
#end
Car.m file:
#import "Car.h"
#implementation Car
-(id)init{
if (self=[super init]){
self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
}
return self;
}
#end
Xcode 7 allows you to define Arrays, Dictionaries, and even your own Classes as having generics. The array syntax is as follows:
NSArray<NSString*>* array = #[#"hello world"];
I don't believe there's any way to do it with NSMutableArray out of the box. You could probably enforce this by subclassing and overriding all the constructors and insertion methods, but it's probably not worth it. What are you hoping to achieve with this?
That's not possible; an NSArray (whether mutable or not) will hold any object type. What you can do is to create your own custom subclasses as already suggested by Jim. Alternatively, if you wanted to filter an array to remove objects that weren't of the type you want, then you could do:
- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
int c = 0;
while(c < [array length])
{
NSObject *object = [array objectAtIndex:c];
if([object isKindOfClass:type])
c++;
else
[array removeObjectAtIndex:c];
}
}
...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];
Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.
You can use the nsexception if you dont have the specific object.
for (int i = 0; i<items.count;i++) {
if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
{
// do something..!
}else{
[NSException raise:#"Invalid value" format:#"Format of %# is invalid", items];
// do whatever to handle or raise your exception.
}
}
Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.
typedef NSMutableArray WheelList;
#interface NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
#end
#implementation NSMutableArray (WheelList)
- (wheel *) wheelAtIndex: (NSUInteger) index
{
return (wheel *) [self objectAtIndex: index];
}
- (void) addWheel: (wheel *) w
{
[self addObject: w];
}
#end
#interface Car : NSObject
#property WheelList *wheels;
#end;
#implementation Car
#synthesize wheels;
- (id) init
{
if (self = [super init]) {
wheels = [[WheelList alloc] initWithCapacity: 4];
}
return self;
}
#end
protocol maybe a good idea:
#protocol Person <NSObject>
#end
#interface Person : NSObject <Person>
#end
to use:
NSArray<Person>* personArray;
There is one-header file project which allows this:
Objective-C-Generics
Usage:
Copy ObjectiveCGenerics.h to your project.
When defining a new class use the GENERICSABLE macro.
#import "ObjectiveCGenerics.h"
GENERICSABLE(MyClass)
#interface MyClass : NSObject<MyClass>
#property (nonatomic, strong) NSString* name;
#end
Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.
Code: