2017-05-16 3 views
0

J'essaie d'envoyer des notifications à Notification Center sur Mac OS X 10.11.6 (El Capitan). Maintenant, je ne suis pas un expert en Objective-C, donc j'utilise l'exemple this comme base.Objective-C et CoreFoundation: crash de "sélecteur non reconnu envoyé" sur i386

La liste complète:

// 
// main.m 
// badonkas 
// 
// Created by Yoshiki Vázquez Baeza on 21/07/13. 
// Copyright (c) 2013 Yoshiki Vázquez Baeza. All rights reserved. 
// 
// Based on the source code as written by Norio Nomura for the project 
// usernotification https://github.com/norio-nomura/usernotification 
// gcc -framework Foundation main.m 
// 

#import <Foundation/Foundation.h> 
#import <objc/runtime.h> 

NSString *fakeBundleIdentifier = nil; 

@implementation NSBundle(swizle) 

// Overriding bundleIdentifier works, but overriding NSUserNotificationAlertStyle does not work. 

- (NSString *)__bundleIdentifier{ 
    if (self == [NSBundle mainBundle]) { 
     return fakeBundleIdentifier ? fakeBundleIdentifier : @"com.apple.terminal"; 
    } else { 
     return [self __bundleIdentifier]; 
    } 
} 

@end 

BOOL installNSBundleHook() 
{ 
    Class class = objc_getClass("NSBundle"); 
    if (class) { 
     method_exchangeImplementations(class_getInstanceMethod(class, @selector(bundleIdentifier)), 
             class_getInstanceMethod(class, @selector(__bundleIdentifier))); 
     return YES; 
    } 
    return NO; 
} 


#pragma mark - NotificationCenterDelegate 

@interface NotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> 

@property (nonatomic, assign) BOOL keepRunning; 

@end 

@implementation NotificationCenterDelegate 

- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification 
{ 
    self.keepRunning = NO; 
} 

@end 

int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
     NSString *message = [defaults stringForKey:@"-message"] ?: @""; 
     NSString *title = [defaults stringForKey:@"-title"] ?: @"Notification"; 
     NSString *subtitle = [defaults stringForKey:@"-subtitle"] ?: @""; 

     if (installNSBundleHook()) { 

      fakeBundleIdentifier = [defaults stringForKey:@"identifier"]; 

      NSUserNotificationCenter *nc = [NSUserNotificationCenter defaultUserNotificationCenter]; 
      NotificationCenterDelegate *ncDelegate = [[NotificationCenterDelegate alloc]init]; 
      ncDelegate.keepRunning = YES; 
      nc.delegate = ncDelegate; 

      NSUserNotification *note = [[NSUserNotification alloc] init]; 
      note.title = title; 
      note.subtitle = subtitle; 
      note.informativeText = message; 

      [nc deliverNotification:note]; 

      while (ncDelegate.keepRunning) { 
       [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 
      } 
     } 

    } 
    return 0; 
} 

Maintenant, cela fonctionne très bien lors de la compilation d'un binaire 64 bits. Cependant, si je compile comme un 32 bits binaire avec les -m32 ou -arch i386 drapeaux, il se bloque:

2017-05-15 22:16:50.886 a.out[68034:2781117] -[NotificationCenterDelegate setKeepRunning:]: unrecognized selector sent to instance 0x7a019ef0 
2017-05-15 22:16:50.886 a.out[68034:2781117] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NotificationCenterDelegate setKeepRunning:]: unrecognized selector sent to instance 0x7a019ef0' 
*** Call stack at first throw: 
(
    0 CoreFoundation      0x9bc9d9b9 __raiseError + 201 
    1 libobjc.A.dylib      0x9e277fd1 objc_exception_throw + 276 
    2 CoreFoundation      0x9bca1463 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275 
    3 CoreFoundation      0x9bb91dec ___forwarding___ + 1020 
    4 CoreFoundation      0x9bb919ce _CF_forwarding_prep_0 + 14 
    5 a.out        0x000608d5 main + 533 
    6 libdyld.dylib      0x90c4c6ad start + 1 
) 
68034: trace trap 

Pourquoi est-ce, et est-il une solution?

(Parce que quelqu'un demandera sans aucun doute [pour une bonne raison]: j'ai besoin qu'il soit en 32 bits pour pouvoir être relié à un programme 32-bit fermé et je ne peux pas obtenir une version 64-bit de J'ai déjà essayé de lier un dylib 32 bits et j'ai rencontré ce problème.)

Répondre

0

Avez-vous examiné les avertissements du compilateur lors de la construction de 32 bits? Je suis sûr qu'ils auraient expliqué.

En 32 bits, Objective-C ne fournit pas l'auto-synthèse des propriétés. Vous devez faire quelques choses "à la main". Si une propriété doit avoir une variable d'instance pour sauvegarder le stockage, vous devez déclarer cette variable d'instance. En outre, vous devez le faire dans le @interface; La déclaration des variables d'instance dans le @implementation n'est pas prise en charge non plus.

Ensuite, vous devez explicitement @synthesize la propriété ou fournir des implémentations des méthodes d'accès. Si vous utilisez @synthesize et que vous avez nommé votre variable d'instance autre que le même nom que la propriété (sans trait de soulignement principal), vous devez spécifier la variable d'instance à utiliser. Par exemple, @synthesize keepRunning = _keepRunning;. C'est-à-dire que le nom de la variable d'instance par défaut est différent de la convention commune et le nom par défaut avec la synthèse automatique.

Alors:

@interface NotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> 
{ 
    BOOL _keepRunning; 
} 

@property (nonatomic, assign) BOOL keepRunning; 

@end 

@implementation NotificationCenterDelegate 

@synthesize keepRunning = _keepRunning; 

- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification 
{ 
    self.keepRunning = NO; 
} 

@end 
+0

Merci un bouquet pour l'explication claire et solution. "Pas un expert en Objective-C" était vraiment un euphémisme pour "je ne connais pas du tout l'Objective-C". Les avertissements ne sont pas super utiles dans ce cas :( –