We are developing remote desktop app on macOS and recently got user's report about unexpected app crash on macOS 15.2 beta.
On macOS 15.2, there's strange app crash on NSDictionary extension method.
We have narrowed down the steps and create the sample code to duplicate this issue.
Create a cocoa app project in objective-c
Try to access [NSNull null] value in NSDictionary
Use specific method name for NSDictionary extension - (long long)longLongValueForKey:(NSString*)key withDefault:(long long)defaultValue;
The console output for the example app is like below, the method pointer seems to be wrong when trying to get value with longLongValueForKey:withDefault: method to a [NSNull null] object.
********* longLongValueForKey: a: 0
********* longLongValueForKey: b: 100
********* longLongValueForKey: c: 0
********* longLongValueForKey:withDefault: a: -1
********* longLongValueForKey:withDefault: b: 100
********* exception: -[NSNull longLongValue]: unrecognized selector sent to instance 0x7ff8528d9760
********* longLongValueForKey:withDefault1: a: -1
********* longLongValueForKey:withDefault1: b: 100
********* longLongValueForKey:withDefault1: c: -1
Please create an objective-c app project and add below code to reproduce this issue.
//
// AppDelegate.m
// DictionaryTest
//
// Created by splashtop on 2024/11/13.
//
#import "AppDelegate.h"
#define IsNullObject(id) ((!id) || [id isKindOfClass:[NSNull class]])
@interface NSDictionary (extension)
(long long)longLongValueForKey:(NSString*)key;
(long long)longLongValueForKey:(NSString*)key withDefault:(long long)defaultValue;
(long long)longLongValueForKey:(NSString*)key withDefault1:(long long)defaultValue;
@end
@interface AppDelegate ()
@property (strong) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSDictionary* dict = @{
@"b" : @(100),
@"c" : [NSNull null],
};
@try {
long long a = [dict longLongValueForKey:@"a"];
NSLog(@"********* longLongValueForKey: a: %lld", a);
long long b = [dict longLongValueForKey:@"b"];
NSLog(@"********* longLongValueForKey: b: %lld", b);
long long c = [dict longLongValueForKey:@"c"];
NSLog(@"********* longLongValueForKey: c: %lld", c);
}
@catch(NSException* e) {
NSLog(@"********* exception: %@", e);
}
@try {
long long a = [dict longLongValueForKey:@"a" withDefault:-1];
NSLog(@"********* longLongValueForKey:withDefault: a: %lld", a);
long long b = [dict longLongValueForKey:@"b" withDefault:-1];
NSLog(@"********* longLongValueForKey:withDefault: b: %lld", b);
long long c = [dict longLongValueForKey:@"c" withDefault:-1];
NSLog(@"********* longLongValueForKey:withDefault: c: %lld", c);
}
@catch(NSException* e) {
NSLog(@"********* exception: %@", e);
}
@try {
long long a = [dict longLongValueForKey:@"a" withDefault1:-1];
NSLog(@"********* longLongValueForKey:withDefault1: a: %lld", a);
long long b = [dict longLongValueForKey:@"b" withDefault1:-1];
NSLog(@"********* longLongValueForKey:withDefault1: b: %lld", b);
long long c = [dict longLongValueForKey:@"c" withDefault1:-1];
NSLog(@"********* longLongValueForKey:withDefault1: c: %lld", c);
}
@catch(NSException* e) {
NSLog(@"********* exception: %@", e);
}
}
@end
@implementation NSDictionary (extension)
(long long)longLongValueForKey:(NSString*)key {
long long defaultValue = 0;
id value = [self objectForKey:key];
if (IsNullObject(value) || value == [NSNull null]) {
return defaultValue;
}
if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]) {
return [value longLongValue];
}
return defaultValue;
}
(long long)longLongValueForKey:(NSString*)key withDefault:(long long)defaultValue {
id value = [self objectForKey:key];
if (IsNullObject(value) || value == [NSNull null]) {
return defaultValue;
}
if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]) {
return [value longLongValue];
}
return defaultValue;
}
(long long)longLongValueForKey:(NSString*)key withDefault1:(long long)defaultValue {
id value = [self objectForKey:key];
if (IsNullObject(value) || value == [NSNull null]) {
return defaultValue;
}
if ([value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[NSString class]]) {
return [value longLongValue];
}
return defaultValue;
}
@end