First off, my code:
#interface Block : NSObject {
NSData *data;
NSInteger slice_count;
}
#property (readonly) NSData *data;
+ (Stopwatch *) runOldTestsUsingConfiguration:(TestConfiguration *)c;
- (Slice *) getSlice:(NSUInteger)idx;
#end
- (Slice *) getSlice:(NSUInteger)idx {
void *b = (void*)[data bytes] + idx*slice_count;
int len = [data length] / slice_count;
Slice *ret = [Slice alloc];
[ret initWithBytesNoCopy:b length:len freeWhenDone:NO];
return ret;
//NSString *temp2 = [data description];
//NSRange r = NSMakeRange(idx*slice_count, [data length] / slice_count);
//NSData *d = [data subdataWithRange:r];
//NSString *temp = [d description];
//Slice *s = [[Slice alloc] initWithBytesNoCopy:(void *)[d bytes] length:r.length freeWhenDone:NO];
//return s;
}
where Slice is a simple subclass of NSData.
For some reason I'm getting a run-time error that seems to indicate my Slice instance either a) isn't actually a concrete instance (?) or b) something is going wrong in its inheritance and the message isn't binding itself to Slice properly (almost certainly by my as yet unknown error).
The exact error I'm getting is this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '***
initialization method -initWithBytes:length:copy:freeWhenDone:bytesAreVM:
cannot be sent to an abstract object of class Slice: Create a concrete instance!'
Can anyone help me out? I've tried just about everything I can think of (basic routines of which are detailed in the message call itself) and I am still coming up dry. What does it mean when it says 'create a concrete instance'? Isn't that what I'm doing when I alloc it?
Subclassing NSData is a lot more complicated than you would think. In most cases you are better off just writing a wrapper around NSData instead of a full subclass.
IIRC, init methods are allowed to re-assign self, and should therefore ALWAYS be used on the same line as alloc.
Slice *ret = [[Slice alloc] initWithBytesNoCopy:b length:len freeWhenDone:NO];
I'm not sure if that's the root cause, but it's a red-flag to me that may lead you in a good direction.
EDIT:
Actually, it has me wondering if you have overridden +alloc in your subclass and aren't returning an instance...
Related
I am calling an Api asynchronously to get the data. Then I am assigning the data to a shared instance successfully inside a block given below.
RestApiManager *api= [[RestApiManager alloc]init];
[ObjectBuilder sharedManager];
self.obj = [[Object alloc]init];
[api get_data:credential.password finish:^(NSDictionary *data) {
if(data){
self.obj = [ObjectBuilder obj:data];
[[ObjectBuilder sharedManager]setObject:self.obj];
}
This all working as expected except when I try to update some of the values in one of NSMutableDictionary properties of the obj.
- (void)update: (NSInteger) temp {
array3 = [[NSMutableArray alloc]init];
NSNumber *start =[[NSNumber numberWithInteger:var];
[NSNumber numberWithInteger:var2];
NSNumber *temp2 =[NSNumber numberWithInteger:temp];
[array3 addObject:temp2];
[array3 addObject:start];
[array3 addObject:end];
[self.obj.sch setObject:array3 forKey:temp2];
}
[obj.sch setObject:array3 forKey:temp2]; always crashes my app.
Now I have vague idea whats going on, basically I am setting obj inside a block code, whilst in my update function I am trying to change the contents outside block code.
The error I get is
reason: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance
Note this error goes away if i declare line
[[ObjectBuilder sharedManager]setObject:obj];
Outside the finish:^ block
But then I lose all the data obtained from api call.
Object is declared as below #interface Object : NSObject
#property (strong, nonatomic) NSMutableDictionary *sch;
I would like to put a pointer to an NSString pointer into an NSDictionary, and naturally, get it back out again. But I can't figure out the syntax.
I think is is something like
NSString* myString = #"Hi";
NSString**myStringPointer = myString;
NSDictionary* dictionary = #{#"pointer":myStringPointer};
But that is clearly not correct.
I am trying to change what string an NSString points to inside a selector.
-(void) updateString:(NSString*) aString {
aString = #"Hello World"; //
}
-(void) testUpdateString {
NSString *textString = #"TEST";
[self updateString:testString];
// testString still is #"TEST";
}
Thank you.
You can only put (pointers to) things that inherit from NSObject in an NSDictionary; a pointer to (a pointer to) an NSString isn't such an object. You can wrap it in an NSValue to store it in a dictionary.
NSDictionary *dictionary = #{ #"pointer": [NSValue valueWithPointer:myStringPointer], };
While it's worth being aware of the general idea of wrapping things that couldn't otherwise be put into an NSDictionary, NSArray, etc., in NSValue for this purpose, I can't think of a good reason to store an NSString ** in an NSDictionary, so it might be better to look at why you're trying to do that and whether there's a better way to achieve your larger goal.
You don't need a dictionary here, you can pass the object by reference to the updating
method:
-(void) updateString:(NSString **) aString {
*aString = #"Hello World";
}
-(void) testUpdateString {
NSString *testString = #"TEST";
[self updateString:&testString];
}
(If you are curious what actually happens behind the scenes, look up
__autoreleasing in the "Transitioning to ARC Release Notes".)
I'm pretty sure this is eactly the same problem as in componentsJoinedByString gives me EXC_BAD_ACCESS
basically, an array is populated using this code, with ARC turned on:
-(NSMutableArray *)getArrayOfCommaSeparatedSelectorStrings{
NSMutableArray *Array = [[NSMutableArray alloc] init];
for(NSMutableArray *e in [self getArrayOfSelectorArrays]) {
[Array addObject:[displayCSSInformation returnArrayAsCommaList:e]];
}
return Array;
}
and then displayCSSInformation tries to return a comma separated list with this method :
+(NSString *)returnArrayAsCommaList:(NSMutableArray *)ToBeConverted{
NSString *test = [ToBeConverted componentsJoinedByString:#", "];
return test;
}
Thanks for your help.
There's usually no need to use a separate method if all that method does is invoke another method. Remove your +returnArrayAsCommaList: method and just use componentsJoinedByString: on the array directly.
- (NSMutableArray *) getArrayOfCommaSeparatedSelectorStrings
{
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSMutableArray *e in [self getArrayOfSelectorArrays])
[array addObject:[e componentsJoinedByString:#", "]];
return array;
}
The above should work (it works in my small test example), if you are still getting errors:
Make sure that getArrayOfSelectorArrays is actually returning an array of array of strings. Log the output to the console or step through with a debugger.
Use the “Build & Analyze” option to have the static analyser check for any issues. This is less of an issue with ARC but it will still pick up things such as using uninitialised values.
Make sure you have properly bridged ownership from any Core Foundation objects.
I have this question here (as well other quesrtions on SO), and the Apple docs about Objective-C collections and fast enumeration. What is not made clear is if an NSArray populated with different types, and a loop is created like:
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
What exactly happens here? Will the loop skip over anything that is not an NSString? For example, if (for the sake of argument) a UIView is in the array, what would happen when the loop encounters that item?
Why would you want to do that? I think that would cause buggy and unintended behavior. If your array is populated with different elements, use this instead:
for (id object in myArray) {
// Check what kind of class it is
if ([object isKindOfClass:[UIView class]]) {
// Do something
}
else {
// Handle accordingly
}
}
What you are doing in your example is effectively the same as,
for (id object in myArray) {
NSString *string = (NSString *)object;
NSLog(#"%#\n", string);
}
Just because you cast object as (NSString *) doesn't mean string will actually be pointing to an NSString object. Calling NSLog() in this way will call the - (NSString *)description method according to the NSObject protocol, which the class being referenced inside the array may or may not conform to. If it conforms, it will print that. Otherwise, it will crash.
You have to understand that a pointer in obj-c has no type information. Even if you write NSString*, it's only a compilation check. During runtime, everything is just an id.
Obj-c runtime never checks whether objects are of the given class. You can put NSNumbers into NSString pointers without problems. An error appears only when you try to call a method (send a message) which is not defined on the object.
How does fast enumeration work? It's exactly the same as:
for (NSUInteger i = 0; i < myArray.count; i++) {
NSString* string = [myArray objectAtIndex:i];
[...]
}
It's just faster because it operates on lower level.
I just tried a quick example... Here is my code.
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
NSNumber *number = [NSNumber numberWithInteger:6];
[array addObject:number];
[array addObject:#"Second"];
Now if I simply log the object, no problem. The NSNumber instance is being cast as an NSString, but both methods respond to -description, so its not a problem.
for (NSString *string in array)
{
NSLog(#"%#", string);
}
However, if I attempt to log -length on NSString...
for (NSString *string in array)
{
NSLog(#"%i", string.length);
}
... it throws an NSInvalidArgumentException because NSNumber doesn't respond to the -length selector. Long story short, Objective-C gives you a lot of rope. Don't hang yourself with it.
Interesting question. The most generic syntax for fast enumeration is
for ( NSObject *obj in myArray )
NSLog( #"%#\n", obj );
I believe that by doing
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
instead, you are simply casting each object as an NSString. That is, I believe the above is equivalent to
for ( NSObject *obj in myArray ) {
NSString *string = obj;
NSLog( #"%#\n", string );
}
I could not find precise mention of this in Apple's documentation for Fast Enumeration, but you can check it on an example and see what happens.
Since all NSObject's respond to isKindOfClass, you could still keep the casting to a minimum:
for(NSString *string in myArray) {
if (![string isKindOfClass:[NSString class]])
continue;
// proceed, knowing you have a valid NSString *
// ...
}
This is my code:
NSError *error = nil;
SBJsonParser *parserJson = [[SBJsonParser alloc] init];
NSDictionary *jsonObject = [parserJson objectWithString:webServiceResponse error:&error];
[parserJson release], parserJson = nil;
//Test to see if response is different from nil, if is so the parsing is ok
if(jsonObject != nil){
//Get user object
NSDictionary *userJson = [jsonObject objectForKey:#"LoginPOST2Result"];
if(userJson != nil){
self.utente = [[User alloc] init];
self.utente.userId = [userJson objectForKey:#"ID"];
}
While Json string webServiceResponse is:
{"LoginPOST2Result":
"{\"ID\":1,
\"Username\":\"Pippo\",
\"Password\":\"Pippo\",
\"Cognome\":\"Cognome1\",
\"Nome\":\"Nome1\",
\"Telefono\":\"012345678\",
\"Email\":null,
\"BackOffice\":true,
\"BordoMacchina\":false,
\"Annullato\":false,
\"Badge\":1234}"
}
The problem arise when is execute this line:
self.utente.userId = (NSInteger *) [userJson objectForKey:#"ID"];
and the error is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectForKey:]: unrecognized selector sent to instance 0x6861520'
The error seems to be due to the fact that the object userJson is not an NSDictionary but rather NSCFString type and therefore does not respond to the message objectForKey:.
Where am I doing wrong?
The problem is that whilst the value in the json response for the key "LoginPOST2Result" looks like a dictionary, it is actually a string as it is enclosed in quotes.
So you are sending the objectForKey: message to an NSString and not an NSDictionary. NSString does not respond to objectForKey:.
It looks like the webServiceResponse is being generated incorrectly or parsed incorrectly.
You need to better understand what is a pointer and what no in the Cocoa Framework.
Infact you are defining userJson to NSDictionary and not NSDictionary *. Consider that all objects in Cocoa are pointers. Infact check that [NSDictionary objectForKey:] returns an "id" and then you must use NSDictionary *. Using simply NSDictionary you will refer to the class.
Similar mistake is done later in the cast to (NSInteger *) but NSInteger (NSInteger is not an object, it's a basic type hesitated from long or int (depends on the platform architecture) as you can see from its definition:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
And also it seems from the object definition above that the key you are trying to get is dumped as a string and you are trying to fetch a dictionary. Please check the original json which probably is not in the format you expect.
So at the end you have at least 3 errors that will make your app crash.