类方法的全局变量

发布于 2024-12-20 21:22:12 字数 1771 浏览 1 评论 0原文

背景

在 Cocoa 中,Apple 经常使用以下范例:

[NSApplication sharedApplication]
[NSNotificationCenter defaultNotificationCenter]
[NSGraphicsContext currentContext]
[NSCalendar currentCalendar]

等等。

他们偶尔也会使用我认为在处理大量代码时更清晰的范例。

NSApp //which maps to [NSApplication sharedApplication]

目标

我希望能够在我自己的类中以及其他类的扩展中使用这种全局变量。

MYClassInstance
NSDefaultNotificationCenter
NSCal /* or */ NSCurrentCalendar

等等。

“duh”方法

#define。简单地#define NSCal [NSCalendar currentCalendar],但我们现在都知道,宏是邪恶的(或者他们是这么说的),而且这似乎不是正确的 Cocoa 解决方案。

Apple 的方法

我能找到的有关 NSApp 的唯一来源是 APPKIT_EXTERN id NSApp;,这并不是完全可重用的代码。除非我弄错了,否则这段代码所做的就是将 NSApp 定义为周围世界的 id。不幸的是没有帮助。

接近,但不是完全

在我的搜索中,我设法找到了一些关于“全局常量”的线索,但是像这样的事情:

extern NSString * const StringConstant;

不幸的是仅限于编译时常量,并且无法映射到必要的类方法。

底线

我希望能够滚动我自己的 NSApp 样式的全局变量,这些变量映射到 [NSNotificationCenter defaultNotificationCenter] 等类方法。这可能吗?如果是这样,我该怎么办?

进一步的尝试

我尝试通过以下方式专门实现框架单例:

MySingletons.h

//...
extern id NSNotifCenter;
//...

MySingletons.m

//...
+(void)initialize
{
    NSNotifCenter = [NSNotificationCenter defaultCenter];
}
//...

MyAppDelegate.m

//...
#import "MySingletons.h"
//...
//in applicationDidFinishLaunching:
    [MySingletons initialize];
    NSLog(@"%@", NSNotifCenter);
//...

但是,这会导致编译时错误,无法找到 _NSNotifCenter 符号。

目标!

我目前正在开发一个 Objective-C 类来封装我在这个问题中提到的一些框架单例。等我搞定了之后我会把GitHub的信息添加到这里。

Background

In Cocoa, Apple frequently makes use of the following paradigm:

[NSApplication sharedApplication]
[NSNotificationCenter defaultNotificationCenter]
[NSGraphicsContext currentContext]
[NSCalendar currentCalendar]

and so on.

They also will occasionally make use of a paradigm that I feel is far more legible when working with vast amounts of code.

NSApp //which maps to [NSApplication sharedApplication]

Goal

I'd love to be able to utilize this sort of global variable, both in my own classes, and in extensions to other classes.

MYClassInstance
NSDefaultNotificationCenter
NSCal /* or */ NSCurrentCalendar

and so on.

The "duh" Approach

#define. Simply #define NSCal [NSCalendar currentCalendar], but as we all know by now, macros are evil (or so they say), and it just doesn't seem like the right Cocoa way to go about this.

Apple's Approach

The only source I could find regarding NSApp was APPKIT_EXTERN id NSApp;, which is not exactly reusable code. Unless I'm mistaken, all this code does is define NSApp to be an id the world around. Unfortunately unhelpful.

Close, but not Quite

In my searches, I've managed to find several leads regarding "global constants", however things like this:

extern NSString * const StringConstant;

are unfortunately limited to compile-time constants, and cannot map to the necessary class method.

Bottom Line

I'd love to be able to roll my own NSApp-style global variables, which map to class methods like [NSNotificationCenter defaultNotificationCenter]. Is this possible? If so, how should I go about it?

Further Attempts

I'm trying to implement specifically the framework singletons in the following way:

MySingletons.h

//...
extern id NSNotifCenter;
//...

MySingletons.m

//...
+(void)initialize
{
    NSNotifCenter = [NSNotificationCenter defaultCenter];
}
//...

MyAppDelegate.m

//...
#import "MySingletons.h"
//...
//in applicationDidFinishLaunching:
    [MySingletons initialize];
    NSLog(@"%@", NSNotifCenter);
//...

However, this results in a compile-time error where the _NSNotifCenter symbol cannot be found.

Goal!

I'm currently working on an Objective-C class to encapsulate some of the framework singletons I've referred to in this question. I'll add the GitHub information here when I get it up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

以歌曲疗慰 2024-12-27 21:22:12

很有趣,我刚刚在另一个问题上提出了这个建议

您只需将保存单例实例的变量公开为全局本身。 NSApp 实际上并未映射sharedApplication 调用。这是一个普通的旧指针;它是在应用程序启动过程中设置的,以指向您将从该调用返回的同一实例。

就像 NSApp 一样,您可以

extern MySingleton * MySingletonInstance;

在标头中为导入标头的任何文件声明变量(如果您愿意,可以使用 APPKIT_EXTERN ;文档表明它只是解析为ObjC中的extern)。

在实现文件中定义变量。通常,保存共享实例的变量被声明为static,以限制其与该文件的链接。如果删除static,则该语句定义在标头中“重新声明”的存储。

然后,像以前一样使用它。唯一需要注意的是,您仍然必须在第一次使用全局之前调用单例设置方法[MySingleton sharedInstance],以确保它已初始化。 -applicationDidFinishLaunching: 可能是执行此操作的一个不错的选择。

至于创建指向框架单例的指针,您可以将 [CocoaSingleton sharedInstance] 的结果存储在您喜欢的任何变量中:想要使用它的类中的 ivar、局部变量或您在程序中很早就通过编写的函数初始化的全局变量。

问题是,这并不能保证不会引起问题。除了 NSApp 的情况(或者除非它在某处有记录),实际上并不能保证您从任何给定的 sharedInstance 调用中返回的对象将保持活动状态、有效,或者在调用堆栈末尾之后有用。

这可能只是偏执,但我建议不要这样做,除非您可以在某个地方找到保证,您感兴趣的假定单例总是返回相同的实例。否则,您可能会突然得到一个悬空的全局指针。

针对您的代码,标头中的声明不会创建变量。您仍然需要在某处定义:

// MySingletons.h
// Dear compiler, There exists a variable, NSNotifCenter, whose 
// storage is elsewhere. I want to use that variable in this file.
extern id NSNotifCenter;

// MySingletons.m
// Dear compiler, please create this variable, reserving memory
// as necessary.
id NSNotifCenter;

@implementation MySingletons

// Now use the variable.
// etc.

如果您要创建单例,您可能需要浏览一下Apple 的单例文档

That's funny, I just made this suggestion on another question.

You just expose the variable that holds the singleton instance as a global itself. NSApp isn't actually mapping to a sharedApplication call. It's a regular old pointer; it was set up during the application launch process to point to the same instance that you would get back from that call.

Just like NSApp, you declare the variable for any file which imports the header:

extern MySingleton * MySingletonInstance;

in the header (you can use APPKIT_EXTERN if you like; the docs indicate that it just resolves to extern in ObjC anyways).

In the implementation file you define the variable. Usually the variable holding the shared instance is declared static to confine its linkage to that file. If you remove the static, the statement defines storage that is "redeclared" in the header.

Then, use it as you did before. The only caveat is that you still have to get your singleton setup method [MySingleton sharedInstance] called before the first time you use the global in order to make sure it's initialized. -applicationDidFinishLaunching: may be a good candidate for a place to do this.

As for creating pointers to framework singletons, you can just stash the result of [CocoaSingleton sharedInstance] in whatever variable you like: an ivar in a class that wants to use it, a local variable, or in a global variable that you initialize very early in your program via a function you write.

The thing is, that's not guaranteed not to cause problems. Except in the case of NSApp (or unless it's documented somewhere) there's really no guarantee that the object you get back from any given call to sharedInstance is going to remain alive, valid, or useful past the end of your call stack.

This may just be paranoia, but I'd suggest not doing this unless you can find a guarantee somewhere that the supposed singletons you're interested in always return the same instance. Otherwise, you might suddenly end up with a dangling global pointer.

Addressing your code, the declaration in your header doesn't create a variable. You still need a definition somewhere:

// MySingletons.h
// Dear compiler, There exists a variable, NSNotifCenter, whose 
// storage is elsewhere. I want to use that variable in this file.
extern id NSNotifCenter;

// MySingletons.m
// Dear compiler, please create this variable, reserving memory
// as necessary.
id NSNotifCenter;

@implementation MySingletons

// Now use the variable.
// etc.

If you're creating a singleton, you might want to glance at Apple's singleton documentation.

淡墨 2024-12-27 21:22:12

这里现有的讨论非常有趣,我做了一些研究,发现了一些我以前从未意识到的事情:我可以将我自己的项目中的头文件#import导入到项目的.pch<中/code> 文件(预编译头)。此头文件自动对项目中的所有其他类文件可见,无需我付出任何努力

这是我现在正在做的一个例子。在 .pch 文件中,现有代码下方:

#import "MyIncludes.h"

MyIncludes.h 中,有两种事物,类别和外部(后者根据 Josh 的建议):

extern NSString* EnglishHiddenKey;
extern NSString* IndexOfCurrentTermKey;

@interface UIColor (mycats)
+ (UIColor*) myGolden;
+ (UIColor*) myPaler;
@end

在 < em>MyInincludes.m 我们提供定义来满足头文件中的所有声明。外部变量不必在任何类中定义:

#import "MyIncludes.h"

NSString* EnglishHiddenKey = @"englishHidden";
NSString* IndexOfCurrentTermKey = @"indexOfCurrentTerm";

@implementation UIColor (mycats)
+ (UIColor*) myGolden {
    return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900];
}
+ (UIColor*) myPaler {
    return [self colorWithRed:1.000 green:0.996 blue:0.901 alpha:1.000];
}
@end

除了使用 pch 文件来获得神奇的全局可见性的部分之外,这与 Josh 的建议没有任何不同。我将其作为单独的答案(而不仅仅是评论)发布,因为它很长并且需要格式化,并且显式代码可能会对某人有所帮助。

(请注意,没有内存管理,因为我使用的是 ARC。当然,外部程序会泄漏,但它们应该会泄漏:只要应用程序运行,它们就需要存在。)

The existing discussion here was so intriguing that I did a little research and discovered something I'd never realized before: I can #import a header file from my own project into the project's .pch file (the precompiled header). This header file becomes automatically visible to all the other class files in my project with no effort on my part.

So here's an example of what I'm now doing. In the .pch file, beneath the existing code:

#import "MyIncludes.h"

In MyIncludes.h are two kinds of thing, categories and externs (the latter in accordance with Josh's suggestion):

extern NSString* EnglishHiddenKey;
extern NSString* IndexOfCurrentTermKey;

@interface UIColor (mycats)
+ (UIColor*) myGolden;
+ (UIColor*) myPaler;
@end

In MyIncludes.m we provide definitions to satisfy all the declarations from the header file. The externs don't have to be defined from within any class:

#import "MyIncludes.h"

NSString* EnglishHiddenKey = @"englishHidden";
NSString* IndexOfCurrentTermKey = @"indexOfCurrentTerm";

@implementation UIColor (mycats)
+ (UIColor*) myGolden {
    return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900];
}
+ (UIColor*) myPaler {
    return [self colorWithRed:1.000 green:0.996 blue:0.901 alpha:1.000];
}
@end

Except for the part about using the pch file to get magical global visibility, this is not really any different from Josh's suggestion. I'm posting it as a separate answer (rather than a mere comment) because it's long and needs formatting, and the explicit code might help someone.

(Note that there is no memory management, because I'm using ARC. The externs leak, of course, but they are supposed to leak: they need to live as long as the app runs.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文