5

我意识到这个问题存在一些主观性,但考虑到 Apple 开发对命名约定非常固执己见,我希望以其他人会理解我的编码在做什么的方式来做这件事。我试图以最通用的方式提出这个问题,但我会在评论中添加我的一些具体细节,以防它影响你的答案。

假设我同时支持 iOS 6 和 iOS 7。现有类上有一个新方法,它只存在于 iOS 7 SDK 中。假设以对我的应用程序“足够好”的方式实现功能是相当简单的。但是,当然,我更愿意使用 SDK 版本,因为它可能会得到更好的支持、更高效并且更好地处理边缘情况。

本问答中所述,处理这种情况很简单。

if ([myInstance respondsToSelector:@selector(newSelector)]) {
    //Use the SDK method
} else {
    //Use my "good enough" implementation.
}

但我不想用一大堆条件调用乱扔我的代码。似乎将这种动态方法选择封装起来会更好。(特别是在我的情况下,该方法实际上还没有发货,名称/签名可能会改变。)

我的直觉是添加一个实现我的功能的类类别以及实现这种动态选择方法的包装方法。

这是正确的方法吗?如果是这样,我应该使用什么命名约定?(我显然不能将我的方法命名为与 iOS7 方法相同的名称,否则会发生命名冲突。)

我的直觉反应是调用我的包装方法 safeNewSelector 和我的实现一个名为 lwNewSelector 的私有方法(其中 lw 是我的标准类前缀)。但我更愿意使用一些被认为是标准命名约定的东西。

4

3 回答 3

3

我的直觉是添加一个实现我的功能的类类别以及实现这种动态选择方法的包装方法。

听起来不错。类别方法的命名约定是小写前缀加下划线。因此,如果您要隐藏一个名为 的方法doSomething:withAwesome:,则应命名您的类别方法ogr_doSomething:withAwesome:(假设您使用OGR作为公共前缀)。

您确实必须为类别方法添加前缀。如果两个类别实现相同的方法,则将运行未定义的行为。您不会收到编译时或运行时错误。你只会得到未定义的行为。(Apple 可以并且确实在类别中实现了“核心”功能,您无法轻易检测到他们已经这样做了。)

于 2013-08-07T15:53:27.193 回答
2

Go for a category and chose a name that is pretty unique, for example prefixed by some company/project specific prefix. Let's say the method in iOS 7 is going to be called funky and you chose the prefix foo. Then you'd do:

@implementation SomeClass(FooCategory)

- (void)foo_funky
{
   if ([self respondsToSelector:@selector(funky)]) {
      [self funky];
   } else {
      // Implementation of workaround.
   }
}

@end

Now, every time you'd call foo_funky that decision needs to be made. Pretty inefficient. It just occurred to me that Objective-C can make that more efficient by messing with the runtime, kind of like method-swizzling (following code is untested):

@implementation SomeClass(FooCategory)

- (void)foo_funky
{
    // Empty implementation, it will be replaced.
}

- (void)foo_myFunkyImplementation
{
    // Workaround implementation in case the iOS 7 version is missing.
}

+ (void)load
{
    Method realMethod, dummyMethod;

    realMethod = class_getInstanceMethod(self, @selector(funky));
    if (!realMethod) {
        // iOS7 method not available, use my version.
        realMethod = class_getInstanceMethod(self, @selector(foo_myFunkyImplementation));
    }

    // Get the method that should be replaced.
    dummyMethod = class_getInstanceMethod(self, @selector(foo_funky));

    // Overwrite the dummy implementation with the real implementation.
    method_setImplementation(dummyMethod, method_getImplementation(realMethod));
}

@end

This way every time you call foo_funky the correct method is called without the overhead of responds-to-selector-and-then-call-other-method.

You could also use the runtime class modifications to add your implementation using the official name when it's not available, but I don't recommend that. It's better when you can tell by the method name that it might not be the version you're expecting.

于 2013-08-08T20:34:34.963 回答
0

这确实是一个公平的问题,我认为许多 Objective-C 的 deb 都遇到了这种情况。

我自己在几个地方使用了您建议的方法,使用class category 。至于命名,在大多数情况下,我在我的类别方法中添加了一些额外的功能,所以我的方法名称大部分时间都带有另一个参数——在大多数情况下,一个简单的animated:(BOOL)animated添加到“官方”方法名称的末尾。

是的,存在与未来 SDK 版本冲突的风险,但我不会太担心,Xcode 的重构工作相当好,当类别方法冲突时,您会收到链接器警告。

编辑: 正如 Rob 指出的那样,使用该命名约定可能是一个好主意。

于 2013-08-07T13:35:03.417 回答