Objective C: unrecognized selector sent to Subclass of UIViewController Subclass - objective-c

is almost a week that i can't fix a brain painful problem:
I have a UIViewController subclass named StreamingViewControllerCommon that implement this property:
#property (nonatomic, assign, readonly) BOOL isMusicStopped;
and of course i #synthesize it in the .m file
then i have 2 subclasses of this class: Listen_UIViewController and LastNews_UIViewController that modify (not calling self.isMusicStopped but accessing directly to it) the var isMusicStopped.
In another Class i have a NSMutableDictionary that contains 2 instance (1 for each class) of these two classes but when i try to do this:
if (streamingViews){
for(StreamingViewControllerCommon* aView in streamingViews){
BOOL stopped = aView.isMusicStopped;
NSLog(#"%#",stopped);
if(stopped){
[aView closeStream];
[streamingViews removeObjectForKey:[aView class]];
[aView release];
aView = nil;
}
}
}
i obtain this error:
2011-02-08 15:55:09.760 ProjectName[6182:307] +[LastNews_UIViewController isMusicStopped]: unrecognized selector sent to class 0x2143c
2011-02-08 15:55:09.768 ProjectName[6182:307] CoreAnimation: ignoring exception: +[LastNews_UIViewController isMusicStopped]: unrecognized selector sent to class 0x2143c
But the weird thing is that in the StreamingViewControllerCommon implement also these methods:
-(void) destroyStreamer{
}
-(void) closeStream{
[self destroyStreamer];
}
and when i do:
NSMutableArray* keys = [[NSMutableArray alloc] initWithArray:[streamingViews allKeys]];
[keys removeObject:[thisView class]];
NSMutableArray *tmp = [[NSMutableArray alloc] initWithArray:[streamingViews objectsForKeys:keys notFoundMarker:#"404"]];
if (tmp){
for (StreamingViewControllerCommon* vc in tmp)
[vc closeStream];
[tmp release];
}
[streamingViews removeObjectsForKeys:keys];
i am not getting any error and the subclasse's overridden closeStream methods are right called.
What am i doing wrong?
Best Regards, Antonio
EDIT: As I wrote in the comment: Changing this:
if (streamingViews){
for(StreamingViewControllerCommon* aView in streamingViews){
BOOL stopped = aView.isMusicStopped;
NSLog(#"%#",stopped);
if(stopped){
[aView closeStream];
[streamingViews removeObjectForKey:[aView class]];
[aView release];
aView = nil;
}
}
}
in this:
if (streamingViews){
for (id aViewClass in streamingViews){
StreamingViewControllerCommon* aView = [[streamingViews objectForKey:aViewClass] retain];
//NSLog(#"%#",aView.isMusicStopped);
if(aView.isMusicStopped){
[aView closeStream];
[streamingViews removeObjectForKey:aViewClass];
[aView release];
aView = nil;
}
}
}
Did the trick :P The for each cycle of a NSDictionary returns its keys not the objects and, since i was using the Class of objects as key i was obtaining that weird exception

For whatever reason, it looks like streamingViews contains not an instance of LastNews_UIViewController, but the class itself. That's what the plus sign in +[LastNews_UIViewController isMusicStopped] signifies.

Related

cannot init a class object : objective-C

I'm trying to make a simple subclass of CCNode, but I can't create the object.
It gives me the error "* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[ContentPane<0x206898> init]: cannot init a class object.'"
Here is my subclass of CCNode:
#import "ContentPane.h"
#implementation ContentPane{
int content[8][4];
CCSprite *_rockPath1;
CCSprite *_rockPath2;
}
- (id)init {
self = [super init];
if (self) {
CCLOG(#"ContentPane created");
}
return self;
}
#end
Here is where I try to initiate it:
- (void)didLoadFromCCB {
// tell this scene to accept touches
self.userInteractionEnabled = TRUE;
_counter = 0;
ContentPane *pane = [ContentPane init];
}
Couple things,
In Obj-c when you want to initialize an Object, you need to allocate space for it.
That is done using the alloc keyword.
so your ContentPane *pane = [ContentPane init];
turns into ContentPane *pane = [[ContentPane alloc] init];
Also, whatever tutorial you are following, Stop... the way you have declared your variables we call them (iVars) is a very old fashioned way of doing things, they should really be properties. and Boolean values are represented by YES and NO not TRUE and FALSE
If you are here just like me wondering why you code is crashing.
[NSArray init];
should be :
[[NSArray alloc] init];
or
[NSArray array];
You crash could caused by any other class here NSArray here is for reference only.

static variables inside Objective-C blocks?

I'm working on some CoreAnimation stuff. A navigation controller with a few view controllers. And the view controllers have UISCrollViews for different "pages" of a "brochure." On each page, there might be some animation that gets triggered when the user flips to that page.
I was trying something like this (for one-shot animations).
void (^animationBlock)() =
^{
static bool alreadyTriggered = NO;
if(alreadyTriggered)
return;
[CATransaction begin];
[CATransaction setCompletionBlock:
^{
alreadyTriggered = YES;
}];
// Do me some animations...
[CATransaction commit];
};
NSMutableDictionary* pageBlocks = [[NSMutableDictionary alloc] init];
[pageBlocks setObject:[animationBlock copy] forKey:<animation's name>];
[self.animationBlocks setObject:pageBlocks forKey:<some page number>];
[pageBlocks release];
[animationBlock release];
"animation's name" and "some page number" are placeholders for the sake of explanation (they are arbitrary NSString literals).
And the code that triggers these animations is:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
int pageNumber = floor(self.scrollView.contentOffset.x / self.scrollView.frame.size.width);
NSMutableDictionary* pageBLocks = [self.animationBlocks objectForKey:[NSString stringWithFormat:#"page%i",pageNumber]];
[CATransaction begin];
for(id key in pageBLocks)
((void (^)())[pageBLocks objectForKey:key])();
[CATransaction commit];
}
So far so good, only that If I pop the brochure from the navigation controller (aka calls dealloc on the brochure) and then push it in again, the static bool is still set.
My thoughts:
- am I retaining the block?
I don't know.. I'm calling release after adding it (with copy) to the dictionary and also the brochure's dealloc method calls release on the dictionary.
- am I keeping another copy of the static bool somewhere?
My first bool is allocated when I declare the block as static within the scope of a method.. well depends on Objective-C's activation record scheme which I haven't looked into. But assuming so, that copy is gone when releasing the object on popViewcOntroller. And another copy of it from invoking copy on the block when adding it to the dictionary should be released when the dictionary is killed?
am I retaining the whole brochure object itself? I didn't completely get it from the Apple docs, but they say if I access an instance variable by reference I retain self. I tried releasing self from inside the block and everything keeps running just fine...?
Make the block return a value and then decide whether or not to remove the block from the dictionary.
// ...
BOOL shouldRemove = block();
if (shouldRemove) {
[pageBlocks removeObjectForKey:key];
}
// ...
Let's test the static variable
#interface TestClass : NSObject
#end
#implementation TestClass
- (void(^)(void))block;
{
return [[^{
static BOOL staticBOOL = NO;
NSLog(#"%d", staticBOOL);
staticBOOL = YES;
} copy] autorelease];
}
#end
int main(int argc, char *argv[]) {
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
TestClass *test1 = [[TestClass alloc] init];
test1.block();
test1.block();
TestClass *test2 = [[TestClass alloc] init];
test2.block();
test2.block();
[p release];
}
This outputs
#=> 2012-04-23 00:43:38.501 Untitled[8380:707] 0
#=> 2012-04-23 00:43:38.503 Untitled[8380:707] 1
#=> 2012-04-23 00:43:38.503 Untitled[8380:707] 1
#=> 2012-04-23 00:43:38.504 Untitled[8380:707] 1
How do we solve the problem? I would probably remove the object from the dictionary once it had been executed like this
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
{
NSInteger pageNumber = floor(self.scrollView.contentOffset.x / self.scrollView.frame.size.width);
NSMutableDictionary *pageBlocks = [self.animationBlocks objectForKey:[NSString stringWithFormat:#"page%i", pageNumber]];
[CATransaction begin];
for (id key in [pageBlocks copy]) {
void (^block)(void) = [pageBlocks objectForKey:key];
if (block) {
block();
}
[pageBlocks removeObjectForKey:key];
}
[CATransaction commit];
}
Your comment
So far so good, only that If I pop the brochure from the navigation
controller (aka calls dealloc on the brochure) and then push it in
again, the static bool is still set.
seems to imply you want a BOOL member variable on the pushed view controllers... If that's correct you can achieve this using objc_setAssociatedObject().
I might add a category to UIViewController to achieve this:
#interface UIViewController (OneShotAnimation)
#property ( nonatomic ) BOOL animationHasBeenPlayed ;
#end
#implementation UIViewController (OneShotAnimation)
-(void)setAnimationHasBeenPlayed:(BOOL)b
{
objc_setAssociatedObject( self, #"animationHasBeenPlayed", [ NSNumber numberWithBool:b ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}
-(BOOL)animationHasBeenPlayed
{
return [ objc_getAssociatedObject( self, #"animationHasBeenPlayed" ) boolValue ] ;
}
#end

NSDictionary class changing if held in external Singleton?

I am watching a somewhat cruel behaviour momentarily: I have a ViewController for building a View programmatically. For this purpose I have stored the names of the UILabels that will be displayed in a NSDictionary that is held in an external class which is a singleton.
Unfortunately the NSDictionary is not accessible if I want to use the values in loadView. So I made some tests: The NSDictionary and its contents are availbale in init and the class is, of course, NSCFDictionary. If I have a look at it in loadView the class sometimes is NSCFDictionary and sometimes also CALayer or NSString?! I absolutely don't know what is happening??? This is the code I use:
- (id) init
{
self = [super initWithNibName:nil bundle:nil];
if (self)
{
UITabBarItem *tbi = [self tabBarItem];
[tbi setTitle:#"xxx"];
}
NSEnumerator *num = [[[ValueDispatcher dispatcher] labelDic] keyEnumerator];
NSLog(#"Class(init): %#", [[[ValueDispatcher dispatcher] labelDic] class]);
NSLog(#"No: %i", [[[ValueDispatcher dispatcher] labelDic] count]);
for (id key in num)
{
NSLog(#"Key %# Value %#", key, [[[ValueDispatcher dispatcher] labelDic] valueForKey:key]);
}
return self;
}
- (void)loadView
{
NSLog(#"Class(loadview)1: %#", [[[ValueDispatcher dispatcher] labelDic] class]);
NSLog(#"No: %i", [[[ValueDispatcher dispatcher] labelDic] count]);
NSEnumerator *num = [[[ValueDispatcher dispatcher] labelDic] keyEnumerator];
for (id key in num)
{
NSLog(#"Key34 %# Value %#", key, [[[ValueDispatcher dispatcher] labelDic] valueForKey:key]);
}
...
At which point between init and loadView can or will a NSDictionary be changed?
Btw, another info that might be important: If I use the above code and the NSDictionary is filled by an external service everything works fine. But if I fill the NSDictionary from a stored plist during startup it fails and I watch the described behaviour...
If I have a look at it in loadView the class sometimes is NSCFDictionary and sometimes also CALayer or NSString?
this (typically) means you have a reference count issue or you have not unregistered your observers correctly -- assuming you never set the dictionary. run instruments with zombies enabled.
At which point between init and loadView can or will a NSDictionary be changed?
a lot can happen in that time beyond the code you posted.
You'll need to retain that dictionary for as long your singleton needs it.
If you're using ARC, just make sure that ivar and/ or property are strong.
If you aren't using ARC, and you have a property setter to manage this for you, make sure you are actually using that setter.
And if no ARC, and you're setting your ivar directly, just make sure to retain the dictionary (and release the old one, if any)

Does Objective-C support Generics?

I wonder whether Objective-C offers any support for generics?
For instance, consider a method:
-(void) sort: (NSMutableArray *) deck {
}
Is there any way for me to make it only deal with Deck of Cards?
Is something like this possible to enforce?
-(void) sort: (NSMutableArray <Card *>) deck {
}
Objective-C supports lightweight Generics since 2015, with the Xcode 7.
The Xcode 7 compiler will give you the compiler warning if there is a type mismatch.
For example, the following line will raise a compiler warning as the second object in the array causes type mismatch. The array allows only NSString objects.
NSArray <NSString *> *myArray = [#"str2", #1, #"str2"];
You can use the introspection tools offered by the objective-c runtime.
Basically, it means you can check if all objects in an array either are a kind of class (Class A or one subclass of it) or a member of class (class A), or if a objects conforms to a protocol or responds to a selector (a certain method is present).
-(void) sort: (NSMutableArray *) deck {
for(id obj in deck){
if(obj isKindOfClass:[A class]]){
//this is of right class
}
}
}
You could write a Category method on NSArray that checkouts this on every object.
BOOL allAreKindOfA = [array allObjectsAreKindOfClass:[A class]];
Normally you actually don't need this very often, as you know what you put inside a collection.
If you need to check the type or ability of an object in a Array, this might be an indicator, that your Architecture is broken
Another option could be a subclass of NSMutableArray that only accepts certain classes. But be aware of the subclassing notes for NSMutableArray and NSArray, as these are Class-Clusters and therefore not easy to subclass.
Note: In my other answer I created a NSMutableArray subclass, that uses a block to test, if a certain requirement is fulfilled. If you test against class-membership, this will do exactly what you want. Use the second block for error handling.
As of Xcode 7's release, Apple has added support for Objective-C generics.
NSArray <NSString *> *arrayOfStrings = #[#"a", #"b"];
NSDictionary <NSString *, NSDate *> *dictionaryOfDates = #{ #"a" : #1 };
Inspired by MonomorphicArray I came up with another idea:
Create a subclass on NSMutableArray, that takes two blocks:
AddBlock — a block that test, if one or more requirements are full filed and adds the object only, if its passes the test
FailBlock — a block, that defines what happens, if the test was not successful.
The AddBlock could test for a certain class membership like
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
and the FailBlock can raise an exception, fail silently or add the element, that failed the test, to another Array. If no failBlock is provided, a default block will raise an error.
The blocks will define, if an array acts like an generic array, or as a filter.
I will give an complete example for the second case.
VSBlockTestedObjectArray.h
#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element);
typedef void(^FailBlock)(id element);
#interface VSBlockTestedObjectArray : NSMutableArray
#property (nonatomic, copy, readonly) AddBlock testBlock;
#property (nonatomic, copy, readonly) FailBlock failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;
#end
VSBlockTestedObjectArray.m
#import "VSBlockTestedObjectArray.h"
#interface VSBlockTestedObjectArray ()
#property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
#end
#implementation VSBlockTestedObjectArray
#synthesize testBlock = _testBlock;
#synthesize failBlock = _failBlock;
#synthesize realArray = _realArray;
-(id)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock
FailBlock:(FailBlock)failBlock
Capacity:(NSUInteger)capacity
{
self = [self initWithCapacity:capacity];
if (self) {
_testBlock = [testBlock copy];
_failBlock = [failBlock copy];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}
-(id)initWithTestBlock:(AddBlock)testBlock
{
return [self initWithTestBlock:testBlock FailBlock:^(id element) {
[NSException raise:#"NotSupportedElement" format:#"%# faild the test and can't be add to this VSBlockTestedObjectArray", element];
} Capacity:0];
}
- (void)dealloc {
[_failBlock release];
[_testBlock release];
self.realArray = nil;
[super dealloc];
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if(self.testBlock(anObject))
[self.realArray insertObject:anObject atIndex:index];
else
self.failBlock(anObject);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[self.realArray removeObjectAtIndex:index];
}
-(NSUInteger)count
{
return [self.realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [self.realArray objectAtIndex:index];
}
-(void)errorWhileInitializing:(SEL)selector
{
[NSException raise:#"NotSupportedInstantiation" format:#"not supported %#", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}
#end
Use it like:
VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
NSLog(#"%# can't be added, didn't pass the test. It is not an object of class NSString", element);
}];
VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
NSLog(#"%# can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];
[stringArray addObject:#"test"];
[stringArray addObject:#"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:#"test2"];
[stringArray addObject:#"test3"];
[numberArray addObject:#"test"];
[numberArray addObject:#"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:#"test2"];
[numberArray addObject:#"test3"];
NSLog(#"%#", stringArray);
NSLog(#"%#", numberArray);
Note: This code is not fully tested. Probably some of the unimplemented method should be implemented for usage in real world programs.
Not directly, no. There a few ways to simulate it, but it requires a lot of wrapper code, boilerplate code, and runtime overhead. I just switch to Objective-C++ and use C++ templates when I want or need proper generics.
So if you wanted to introduce typesafety/checks to an NSArray, you could approach it using something like this:
template <typename T>
class t_typed_NSMutableArray {
public:
t_typed_NSMutableArray() : d_array([NSMutableArray new]) {}
~t_typed_NSMutableArray() { [d_array release]; }
/* ... */
T* operator[](const size_t& idx) {
T* const obj([this->d_array objectAtIndex:idx]);
assert([obj isKindOfClass:[T class]]);
return obj;
}
void addObject(T* const obj) {
assert([obj isKindOfClass:[T class]]);
[this->d_array addObject:obj];
}
private:
NSMutableArray * const d_array;
};
in use:
t_typed_NSMutableArray<Card> array([self cards]); // < note this exact constructor is not defined
Card * firstCard = array[0]; // << ok
NSString * string = array[0]; // << warning
then you also get type safety and overloading when passing the collection, so you could not pass t_typed_NSArray<Card> as an t_typed_NSArray<NSURL>.
There is an easy, effective way of doing this (I've been using it on projects for a couple of years now). Sadly, someone deleted the answer, and my attempts to get it re-instated were rejected. Here goes again:
You can re-implement a cut-down version of C++ templating within Obj-C because Obj-C encapsulates all of C (and C++ templates are C-macros with some improved compiler/debugger support):
This only needs to be done once, using a single header file. Someone has done it for you:
https://github.com/tomersh/Objective-C-Generics
You end up with 100% legal Obj-C code that looks like this:
NSArray<CustomClass> anArray= ...
CustomClass a = anArray[0]; // works perfectly, and Xcode autocomplete works too!
This all works fine in XCode, with autocomplete, etc.

Retrieving Class Object from NSArray and adding to self

Hi im trying to retrieve an object of a specific class from an NSMutableArray, and then add it to self: eg:
- (void) init{
_Objects = [[NSMutableArray alloc]init];
Psychicing *psy = [[Psychicing alloc]init];
[psy startPsychic];
[_Objects addObject: psy];
[psy release];
}
This creates an object of class Psychicing, then runs the [psy startPsychic] method to create the internals of the class object. Then I add the psy object to _Objects NSMutableArray.
-(void)startPsychic{
id psychicParticle = [CCParticleSystemQuad ......]; //is Synthesised with (assign)
//Other things are set here such as position, gravity, speed etc...
}
When a touch is detected on screen, I want to take the psy object from the _Objects array and add it to self: Something like this (Although this gives runtime error)
-(void) Touches.....{
for (Psychicing *psy in _Objects){
[self addChild: psy.psychicParticle];
}
}
I hope i have explained it clearly enough, if you need more clarification let me know.
So basically:
[MainClass Init] -> [Psychicing startPsychic] -> [MainClass add to array] -> [MainClass add to self]
I'm assuming the _Objects (which should be a lowercase o to follow conventions) is storing objects other than the Psychicing object and you're trying to pull just the Psychicing object out of it in the -(void)Touches... method (which also should be lowercase). If so, you could do:
for (id obj in _Objects)
{
if ([obj isMemberOfClass:[Psychicing class]])
[self addChild:obj.psychicParticle];
}
That will cause only the Psychicing objects in the array to be added as a child to self.
It looks like you do have another error though if the code you pasted in is your real code. Init should be defined as:
- (void) init{
_Objects = [[NSMutableArray alloc]init];
Psychicing *psy = [[Psychicing alloc]init];
[psy startPsychic];
[_Objects addObject: psy];
[psy release];
}
with _Objects defined as an instance variable (or property) in the class's interface. As you wrote it, it's a method variable in the init method and is leaking. So when you try to access _Objects in -touches, _Objects is most likely nil.
Okay, with the help of McCygnus I got it working, the only thing missing with a pointer to the id object:
for (id obj in _Objects){
if ([obj isMemberOfClass:[Psychicing class]]){
Psychicing *apsy = obj;
[apsy.psychicParticle setPosition:location];
[self addChild:apsy.psychicParticle];
}
}