33

I wrote this simple code to try out the new Objective-C literal syntax for NSArrays:

NSArray *array = @[@"foo"];
NSLog(@"%@", array[0]); 

The first line works fine, but the subscripting results in an error:

Expected method to read array element not found on object of type 'NSArray *'

Just wondering if I have done something wrong, or if the literals haven't been fully implemented yet. I'm compiling with Apple LLVM 4.0 and using the iOS 5 SDK.

Here's a screenshot of the error, too.

Error

Community
  • 1
  • 1
Ben Trengrove
  • 8,191
  • 3
  • 40
  • 58
  • The page on clang.llvm.org about Objective-C literals state that you must use Apple LLVM 4.0 or clang v3.1 to use the new features. Do you have one of these installed, and is it specified in your build settings? – Alexis King Jul 11 '12 at 05:30
  • Yes I am using LLVM 4.0. Should have mentioned that – Ben Trengrove Jul 11 '12 at 05:30
  • 3
    You've also got to be compiling with the iOS 6 or OS X 10.8 SDKs -- otherwise Foundation objects don't have the necessary methods for the subscripting bit of the literal syntax. – jscs Jul 11 '12 at 06:38
  • 1
    @JoshCaswell Thats the answer, I am compiling to iOS5. Post as an answer so I can accept! – Ben Trengrove Jul 11 '12 at 07:01
  • http://stackoverflow.com/questions/9347722/apple-llvm-4-0-new-features-on-xcode-4-4-literals – Paresh Navadiya Jul 11 '12 at 07:12

7 Answers7

36

You've got to be compiling with the iOS 6 or OS X 10.8 SDKs -- otherwise Foundation objects don't have the necessary methods for the subscripting bit of the literal syntax.* Specifically in this case, the subscripting expects objectAtIndexedSubscript: to be implemented by NSArray, and that's a new method that was created to interact with this compiler feature. The parts of the new syntax that just have to do with object creation should work fine, though -- I don't believe that requires any new methods.

Further reading at http://clang.llvm.org/docs/ObjectiveCLiterals.html


*I base this on a bit of research performed by borrrden: https://stackoverflow.com/a/11407844/603977

I've gotten a lot of upvotes on this answer, which I really feel is founded on borrrden's. Please, if you think my answer is worth an upvote, click through and vote there too.

Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195
  • 2
    There is one small thing I'd like to warn about. Literal bools are also not supported because of this. However, a quick fix that I implemented was adding this to the beginning of one of my common headers (in an iOS project) -> `#ifndef __IPHONE_6_0 #if __has_feature(objc_bool) #undef YES #undef NO #define YES __objc_yes #define NO __objc_no #endif #endif` – borrrden Jul 26 '12 at 08:42
  • 1
    why don't the subscripting refer to just objectAtIndex (if objectAtIndexedSubscript isn't found, or only on -iOS6)? – Jonathan. Aug 10 '12 at 19:10
  • 1
    Since Xcode 4.4, the new syntax will still deploy back to iOS 5 by calling objectAtIndex:. Your deployment target must be set correctly to 5.0 and the SDK to the latest. See https://developer.apple.com/library/ios/#releasenotes/ObjectiveC/ObjCAvailabilityIndex/index.html for a compatibility matrix. – Mike Weller Apr 17 '13 at 13:14
15

If you're not targeting iOS 6 or OS X 10.8, I would like to point out that it's still remarkably easy to get subscripting to work. All you have to do is add the required methods as a category the classes you want subscripting to work for, and implement those methods appropriately. So add to the following classes the methods:

NSArray : - (id)objectAtIndexedSubscript: (NSUInteger)index;

NSMutableArray : - (void)setObject: (id)obj atIndexedSubscript: (NSUInteger)index;

NSDictionary : - (id)objectForKeyedSubscript: (id)key;

NSMutableDictionary : - (void)setObject: (id)obj forKeyedSubscript: (id)key;

Implementing this is a simple as calling the appropriate method for the class. For example, to implement subscripting on NSArray you just implement:

- (id) objectAtIndexedSubscript:(NSUInteger)index{
    return [self objectAtIndex:index];
}

The only downside I can see is you need to make sure to import your category into any class that intends on using the subscripting. Of course, you can get around that requirement by including the #import in your prefix header, usually the file: <appname>-Prefix.pch. (thanks Josh Caswell for pointing that out).

One upside is you can alter the subscripting methods to suit your needs. For example, Apple doesn't allow you to add/remove objects to NSMutableArray using subscripting, but this can be accomplished easily enough:

 - (void) setObject:(id)obj atIndexedSubscript:(NSUInteger)index{
    if (index < self.count){
        if (obj)
            [self replaceObjectAtIndex:index withObject:obj];
        else
            [self removeObjectAtIndex:index];
    } else {
        [self addObject:obj];
    }
}
Aaron Hayman
  • 8,492
  • 2
  • 36
  • 63
  • «you need to make sure to import your category into any class that intends on using the subscripting» No problem there; just put it in your prefix header! Nice post! – jscs Jul 26 '12 at 06:45
  • Thanks @JoshCaswell! I'd forgotten about the prefix. I edited the post to include your info. – Aaron Hayman Jul 26 '12 at 11:56
  • 6
    Good point, but it's worth adding that you don't need to do this to *target* iOS5, only to *compile* with an earlier SDK. You can target iOS5 (and use the new constructs) and compile with the iOS6 sdk. – Cris Aug 01 '12 at 06:54
  • That's true, but for the moment iOS 6 is beta only. Not particularly useful for production code. Of course, in a month or so the point will be mute. – Aaron Hayman Aug 04 '12 at 01:38
2

I got the following code from this link:

You can just add this category to NSObject and collection subscripting will work. I put it in my .pch file.

// Add support for subscripting to the iOS 5 SDK.
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSObject (SubscriptingSupport)

- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
- (id)objectForKeyedSubscript:(id)key;

@end
#endif

Of course you will need the newest version of CLANG which is already in XCode 4.4.

Hima
  • 1,249
  • 1
  • 14
  • 18
Jeff Wolski
  • 6,332
  • 6
  • 37
  • 69
  • This works really well and is really simple compared to the other answers. Thank you! – pigoz Aug 26 '12 at 13:42
  • How does this differ from Aaron's answer? – jscs Sep 18 '12 at 16:43
  • His answer suggests creating multiple categories. This is a simpler way as you only need create a single category. While this may be obvious to experienced developers, it is likely to be less obvious to developers who are new to categories. – Jeff Wolski Sep 18 '12 at 22:28
  • I missed that this is a category on `NSObject`, and I have to say that I don't like it. This way, the category is going to be implemented on objects for whom the methods have no meaning. There was a language feature (`@optional` protocol methods) introduced a while back to avoid exactly that. The methods should be added to the classes where they are needed. – jscs Sep 20 '12 at 17:27
  • That's a good point. Using @optional would have been a good idea. Of course, Xcode 4.5 is officially released now, and this whole thing is no longer necessary. – Jeff Wolski Sep 20 '12 at 20:15
1

I got the same problem, but then it wasn't b/c of a lacking iOS version.. but it was simply because the original array was set as an NSArray rather than an NSMutableArray. Changing it to NSMutableArray fixed it for me

abbood
  • 23,101
  • 16
  • 132
  • 246
0

I'm adding this because this is a common error that still exists in Xcode as of 7/2015 and it's not easy to figure out how to resolve it.

I received this error when attempting to call a method on an object without having created an instance of the object. My solution was to create an instance of the object, then call the method on the property on the instance of that object.

Example: What didn't work: [self methodCall:arrayItem] (see full example below)

[self tappedUser:self.activities[indexPath.row].followItem.user.givenName];

What fixed it: ObjectClass newObject = arrayItem; [self methodCall:newObject] (see full example below)

FollowActivityItem *followItem = self.activities[indexPath.row];
[self tappedUser:followItem.user.givenName];
jungledev
  • 4,195
  • 1
  • 37
  • 52
0

add #import <Foundation/Foundation.h> to your h file, and use an NSMutableArray instead

HannahCarney
  • 3,441
  • 2
  • 26
  • 32
-1

If anyone gets to this old thread after getting this error in Xcode 9.3 beta 4 with some legacy Objective-C code like I did, here was my fix.

Update:

@property (nonatomic, strong) id<CustomClass> myObject;

To:

@property (nonatomic, strong) NSMutableArray<CustomClass> *myObject;
Justin Domnitz
  • 3,217
  • 27
  • 34