I have a class called "MyUploadClient" for handling video upload task. "MyUploadClient" has a protocol called "MyUploadClientDelegate". Someone use "MyUploadClient" to upload video needs to comfort to this protocol, and the delegate may be more than one, so I make a property called 'delegateSet' to store the delegates. The method '-invokeDelegateItemsWithAction:withObjects:...' is used to wrap the delegate method and invoke when it needs to.
But the delegate method "-uploadTask:progressChange:" has a parameter which type is float, the app crash when it execute "va_arg(args, id)".
To solve this problem, I change the parameter from float to NSNumber, but the app still crashed when execute "[invocation invoke]" with error "EXC_BAD_ACCESS".
Why did this happen and how can I fix this problem?
Relative code In file: MyUploadClient.h
#protocol MyUploadClientDelegate <NSObject>
- (void)startUploadTask:(MyVideo *)video;
- (void)didUploadTask:(MyVideo *)video error:(NSError * __nullable)error;
- (void)uploadTask:(NSString *)videoId progressChange:(float)progress;
……
#end
#interface MyUploadClient : NSObject
+ (instancetype)sharedClient;
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate;
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate;
……
#end
Relative code In file: MyUploadClient.m
#interface MyUploadClientDelegateItem : NSObject
#property (nonatomic, weak) id<MyUploadClientDelegate> delegate;
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate;
#end
#implementation MyUploadClientDelegateItem
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate {
self = [super init];
if (self) {
[self setDelegate:delegate];
}
return self;
}
#end
#interface MyUploadClient ()
#property (nonatomic, strong) NSMutableSet *delegateSet;
#end
#implementation MyUploadClient
- (NSMutableSet *)delegateSet {
if (!_delegateSet) {
_delegateSet = [NSMutableSet set];
}
return _delegateSet;
}
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate {
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
return ;
}
}
MyUploadClientDelegateItem *delegateItem = [[MyUploadClientDelegateItem alloc] initWithDelegate:delegate];
[self.delegateSet addObject:delegateItem];
}
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate {
MyUploadClientDelegateItem *deleteItem = nil;
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
deleteItem = item;
}
}
if (deleteItem) {
[self.delegateSet removeObject:deleteItem];
}
}
- (void)startUploadWithVideo:(MyVideo *)video {
……
[self invokeDelegateItemsWithAction:#selector(startUploadTask:) withObjects:video, nil];
……
}
- (void)uploadProgressChanged:(float)progress videoId:(NSString *)videoId {
……
[self invokeDelegateItemsWithAction:#selector(uploadTask:progressChange:) withObjects:videoId, progress, nil];
……
}
- (void)invokeDelegateItemsWithAction:(SEL)action withObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION {
NSMutableArray *deleteItems = [NSMutableArray arrayWithCapacity:0];
NSSet *copySet = [NSSet setWithSet:self.delegateSet];
for (MyDelegateItem *item in copySet) {
if (item.delegate && [item.delegate respondsToSelector:action]) {
NSMethodSignature *sigOfAction = [(id)item.delegate methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sigOfAction];
[invocation setTarget:item.delegate];
[invocation setSelector:action];
if (firstObj) {
NSInteger argsIndex = 2;
va_list args;
va_start(args, firstObj);
id argObject = firstObj;
while (argObject) {
if ([argObject isKindOfClass:[NSValue class]]) {
void *value;
[argObject getValue:&value];
[invocation setArgument:&value atIndex:argsIndex];
} else {
[invocation setArgument:&argObject atIndex:argsIndex];
}
argObject = va_arg(args, id);
argsIndex++;
}
va_end(args);
}
[invocation invoke];
}
if (!item.delegate) {
[deleteItems addObject:item];
}
}
for (id obj in deleteItems) {
[self.delegateSet removeObject:obj];
}
}
#end
Related
I'm trying to detect call states on iOS 10.
I have tested this on iOS 9.3 and it works fine.
But on iOS 10, CTCallCenter is deprecated, so I used Callkit.
I can't detect the call state.
I can't find any correct answers.
My code is:
#property (nonatomic, strong) CXCallObserver *callObserver;
...
self.callObserver = [[CXCallObserver alloc] init];
[callObserver setDelegate:self queue:nil];
...
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
if (call.hasConnected) {
NSLog(#"connected/n");
// perform necessary actions
} else if(call.hasEnded) {
NSLog(#"disconnected/n");
}
}
This code is working for me. The only difference is I provide a dispatch queue for the observer, instead of using the main queue.
#import <CallKit/CXCallObserver.h>
#import <CallKit/CXCall.h>
#interface CallStatusIos10 : NSObject <CXCallObserverDelegate>
{
dispatch_queue_t dq;
}
#property (nonatomic, strong) CXCallObserver* observer;
#end
#implementation CallStatusIos10
#synthesize observer;
-(id) init
{
if(self = [super init])
{
observer = [[CXCallObserver alloc] init];
// Hang on to a reference to the queue, as the
// CXCallObserver only maintains a weak reference
dq = dispatch_queue_create("my.ios10.call.status.queue", DISPATCH_QUEUE_SERIAL);
[observer setDelegate: self queue: dq];
}
return self;
}
#pragma mark - CXCallObserverDelegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call
{
NSString* callId = [call.UUID UUIDString];
if(!call.hasConnected && !call.hasEnded && !call.onHold)
{
if(call.outgoing)
{
// outgoing call is being dialling;
}
else
{
// incoming call is ringing;
}
}
else if(call.onHold)
{
// call is on hold;
}
else if(call.hasEnded)
{
// call has disconnected;
}
else if(call.hasConnected)
{
// call has been answered;
}
}
#end
Try this
Appdelegate.h
#import <CoreTelephony/CTCallCenter.h>
#import <CoreTelephony/CTCall.h>
...
#property (nonatomic, strong) CTCallCenter* callCenter;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
....
self.callCenter = [[CTCallCenter alloc] init];
[self handleCall]; //Call this method when you want to use it
....
}
-(void)handleCall
{
self.callCenter.callEventHandler = ^(CTCall *call){
if ([call.callState isEqualToString: CTCallStateConnected])
{
//NSLog(#"call stopped");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
//NSLog(#"call played");
}
else if ([call.callState isEqualToString: CTCallStateIncoming])
{
//NSLog(#"call stopped");
}
};
}
Taken from: How to detect call incoming programmatically
To know more: CoreTelephony Framework iOS 7
I'm learning Objective-C (having come from Java) and I'm am attempting to program a small Cocoa app on OSX. I am thinking with my Java hat on and that's probably why this isn't working. Basically I am trying to get an object of type Car added to an NSMutableArray on a button click but although the rest of the code seems to work, nothing is being saved to the array. I can't figure out why - help is appreciated. Here's the code:
#import "AppDelegate.h"
#import "Car.h"
#import "PileInterface.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
PileInterface *myStack;
Car *car;
//initialise Stack
-(void)stack{
myStack = [[PileInterface alloc]init];
//initialise myStack in Pileinterface
[myStack initialise];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//corrected code here to call the stack method above
//initialise myStack by calling stack method
[self stack];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (IBAction)addBtn:(NSButton *)sender {
car = [[Car alloc]init];
NSString *licence = [_txtField1 stringValue];
NSString *model = [_txtField2 stringValue];
NSString *colour = [_txtField3 stringValue];
if ([myStack isFull] == false) {
// add Car objects to car
//set licence
[car setLicence:licence];
// set model
[car setModel:model];
//set colour
[car setColour:colour];
//add to stack
[myStack push:car];
//append to textarea
NSString *message = licence;
[_txtArea setStringValue:message];
[_txtField2 setIntegerValue:[myStack size]];
} else {
NSString *message2 = #"Rejected. There are more than 4 cars currently for recycling!";
[_txtArea insertText:message2];
}
}
#end
Effectively Car is the object I want to get into myStack (which is a stack inside PileInterface, which also contains the NSMutableArray)
PileInterface implementation code here:
#import "PileInterface.h"
#implementation PileInterface
//arraylist of objects
NSMutableArray *list;
//initialise array
-(void)initialise{
list = [[NSMutableArray alloc]init];
}
-(void) push:(id) o{
[list insertObject:o atIndex:0];
}
-(id) pop{
if ([list count] > 0) {
NSObject *temp =[list objectAtIndex:0];
[list removeObjectAtIndex:0];
return temp;
} else {
return nil;
}
}
-(BOOL) isEmpty{
if ([list count] == 0) {
return true;
} else {
return false;
}
}
-(int) size{
return (int)[list count];
}
-(BOOL) isFull{
if ([list count] > 4) {
return true;
} else {
return false;
}
}
#end
Help Appreciated!
The class "PlayingCardDeck" inherits "Deck" class can not put the method
- (void) addCard:(Card *)card atTop:(BOOL)atTop
Xcode accuses me the following error:
no Known class method for selector 'addCard:atTop'
Class: PlayingCardDeck
#import "PlayingCardDeck.h"
#import "PlayingCard.h"
#implementation PlayingCardDeck
- (id)init
{
self = [super init];
if(self) {
for(NSString *suit in [PlayingCard validSuits]){
for (NSUInteger rank = 1; rank <= [PlayingCard maxRank]; rank++){
PlayingCard *card = [[PlayingCard alloc] init];
card.rank = rank;
card.suit = suit;
[PlayingCardDeck addCard:card atTop:NO];
}
}
}
return self;
}
#end
Class: Deck
#import "Deck.h"
#interface Deck()
#property (strong, nonatomic) NSMutableArray *cards;
#end
#implementation Deck
- (NSMutableArray *)cards
{
if(!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void) addCard:(Card *)card atTop:(BOOL)atTop
{
if(atTop){
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (Card *)drawRandomCard
{
Card *randomCard = nil;
if(self.cards.count){
unsigned index = arc4random() % self.cards.count;
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
#end
thank you all for the help.
The method -addCard:atTop: is an instance method. It must be sent to an instance of a class, not the class itself. This line:
[PlayingCardDeck addCard:card atTop:NO];
is attempting to send it to the class PlayingCardDeck, not any instance of that class. That's what the error is trying to tell you.
Since that code is in the -init method of the PlayingCardDeck class, you probably meant to send the message to self:
[self addCard:card atTop:NO];
I'm new to objective C and have been learning with the book "IOS Programming Big Nerd Ranch Guide 4th edition". I keep getting a recurring error and I have been working to resolve it for the last few hours. After researching for a bit, I came here. Any help would be greatly appreciated.
"No visible #interface for 'BNRItemStore' declares the selector 'deleteImageForKey;'"
BNRItemStore.h
#import <Foundation/Foundation.h>
#class BNRItem;
#interface BNRItemStore : NSObject
#property (nonatomic, readonly) NSArray *allItems;
// Notice that this is a class method and prefixed with a + instead of a -
+ (instancetype)sharedStore;
- (BNRItem *)createItem;
- (void)removeItem:(BNRItem *)item;
- (void)moveItemAtIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex;
#end
BNRItemStore.m
#import "BNRItemStore.h"
#import "BNRItem.h"
#interface BNRItemStore ()
#property (nonatomic) NSMutableArray *privateItems;
#end
#implementation BNRItemStore
+ (instancetype)sharedStore
{
static BNRItemStore *sharedStore = nil;
// Do I need to create a sharedStore?
if (!sharedStore) {
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
// If a programmer calls [[BNRItemStore alloc] init], let him
// know the error of his ways
- (instancetype)init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"Use +[BNRItemStore sharedStore]"
userInfo:nil];
return nil;
}
// Here is the real (secret) initializer
- (instancetype)initPrivate
{
self = [super init];
if (self) {
_privateItems = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)allItems
{
return [self.privateItems copy];
}
- (BNRItem *)createItem
{
BNRItem *item = [BNRItem randomItem];
[self.privateItems addObject:item];
return item;
}
- (void)removeItem:(BNRItem *)item
{
NSString *key = item.itemKey;
if (key) {
[[BNRItemStore sharedStore] deleteImageForKey:key];
}
[self.privateItems removeObjectIdenticalTo:item];
}
- (void)moveItemAtIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex
{
if (fromIndex == toIndex) {
return;
}
// Get pointer to object being moved so you can re-insert it
BNRItem *item = self.privateItems[fromIndex];
// Remove item from array
[self.privateItems removeObjectAtIndex:fromIndex];
// Insert item in array at new location
[self.privateItems insertObject:item atIndex:toIndex];
}
#end
BNRImageStore.h
#import <Foundation/Foundation.h>
#interface BNRImageStore : NSObject
+ (instancetype)sharedStore;
- (void)setImage:(UIImage *)image forKey:(NSString *)key;
- (UIImage *)imageForKey:(NSString *)key;
- (void)deleteImageForKey:(NSString *)key;
#end
BNRImageStore.m
#import "BNRImageStore.h"
#interface BNRImageStore ()
#property (nonatomic, strong) NSMutableDictionary *dictionary;
#end
#implementation BNRImageStore
+ (instancetype)sharedStore
{
static BNRImageStore *sharedStore = nil;
if (!sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// No one should call init
- (instancetype)init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"Use +[BNRImageStore sharedStore]"
userInfo:nil];
return nil;
}
// Secret designated initializer
- (instancetype)initPrivate
{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)setImage:(UIImage *)image forKey:(NSString *)key
{
self.dictionary[key] = image;
}
- (UIImage *)imageForKey:(NSString *)key
{
return self.dictionary[key];
}
- (void)deleteImageForKey:(NSString *)key
{
if (!key) {
return;
}
[self.dictionary removeObjectForKey:key];
}
#end
You are declaring the method deleteImageForKey in your BNRImageStore class, not in your BNRItemStore.
Check your implementation of removeItem: in BNRItemStore.m
- (void)removeItem:(BNRItem *)item
{
NSString *key = item.itemKey;
if (key) {
[[BNRItemStore sharedStore] deleteImageForKey:key];
}
[self.privateItems removeObjectIdenticalTo:item];
}
I assume you meant to refer to "BNRImageStore" and not BNRItemStore.
Apart from the typo, you should understand that Objective-C objects respond to selectors. Every Objective-C object has an array of selectors that it responds to. When you see the error: "No visible #interface for 'BNRItemStore' declares the selector 'deleteImageForKey;'" you should understand that the compiler does not see the selector you specified as being understood by the class in the error.
I have declared a class called 'SharedTranslationsArray' that I want to use in multiple view controllers.
I then in the view controller MainViewController.m I declare an instance of the variable in the "ViewDidLoadMethod" and try to add an object to the array in the singleton instance. There are no compilation errors / warning but the items are not added to the array.
Any advice would be appreciated. The relevant code samples are below
Thanks
**SharedTranslations.h**
#import "Foundation/Foundation.h"
#interface SharedTranslationsArray : NSObject {
NSMutableArray *translation_set;
}
static SharedTranslationsArray *sharedInstance;
#property (nonatomic, retain) NSMutableArray *translation_set;
+ (SharedTranslationsArray*) sharedInstance;
#end
**SharedTranslations.m**
#import "SharedTranslationsArray.h"
static SharedTranslationsArray *sharedInstance;
#implementation SharedTranslationsArray
#synthesize translation_set;
\+ (SharedTranslationsArray*)sharedInstance
{
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
\ + (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance]retain];
}
\ - (id)copyWithZone:(NSZone *)zone
{
return self;
}
\ - (id)retain
{
return self;
}
\ - (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
\ - (void)release
{
//do nothing
}
\ - (id)autorelease
{
return self;
}
#end
**MainViewController.m**
#import "MainViewController.h"
#import "Translations.h"
#import "SharedTranslationsArray.h"
#implementation MainViewController
\- (void)viewDidLoad {
NSMutableString *temp = [[NSMutableString alloc] init];
SharedTranslationsArray *ts = [SharedTranslationsArray sharedInstance];
Translations *translation = [Translations new];
translation.shortText = #"short";
translation.fullText = #"long";
translation.canDeleted = FALSE;
translation.active = TRUE;
[ts.translation_set addObject:translation];
}
#end
Make sure you actually initialize translation_set!
+ (SharedTranslationsArray*)sharedInstance
{
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
sharedInstance.translation_set = [NSMutableArray array];
}
return sharedInstance;
}
Also why are you calling it a set but declaring it as an array? If the order of the objects is not important you could make it an NSMutableSet