类别正在实现一个方法,该方法也将在主要类中实现: `viewWillAppear:`。

4
我尝试对 UIViewController 进行分类以覆盖 viewWillAppear: 方法。但是遇到了这个警告。

类别正在实现一个在主要类中也要实现的方法

@implementation UIViewController (ViewWillAppearCategory)

-(void)viewWillAppear:(BOOL)animated
{
    //.........
}

@end

我希望在所有屏幕中出现视图时进行一些操作,因此我不想触摸所有屏幕。所以,按类别进行操作。

我可以在子类中实现一些方法,并在所有VC(所有屏幕)中调用该方法。但我不想这样做。它会自动调用view will appear call中的方法。有没有什么好的方法来解决这个问题,或者上面有任何错误吗?

注意: 这段代码将仅出现在开发阶段,用于某些测试目的。因此,当进入应用商店时,我将删除此代码。因此,在删除时应该更容易,也就是说,我不会触碰所有屏幕。提交到应用商店时,我不会保留此代码。


5
不应使用类别来覆盖方法。 - Andrey Chernukha
子类化课程 - Andrey Chernukha
不可以。这也会修改所有屏幕。我正在实现一个仅在开发阶段可用的视图。在分发时,我应该从所有 VC 中删除此屏幕。我不能进行子类化。 - Mani
按照我的建议进行操作,然后在发布时只需修复一个父类以消除所有行为。 - RobP
是的,这是一个想法。但是如果我们移除行为,它就没有用了,而且对于每个 VC 的分配来说,它将保持为浪费。有没有其他好的方法?因为我不想修改所有的 xib 和 VC。 - Mani
显示剩余5条评论
5个回答

11

在这种情况下,您必须尝试使用方法混淆(Method Swizzling),这是一个非常好的概念形式,可以让您更改现有选择器的实现。

欲了解更多详情和代码,请访问下面的链接。

http://nshipster.com/method-swizzling/


它会调用两个实现(class_addMethod(...))吗?还是只替换第一个方法并仅调用我们自己的方法?我想调用类别中可用的方法,并调用视图控制器中的默认实现。 - Mani
虽然这个方法可以运行,但我认为在这种情况下这是一个可怕的想法,因为它会质疑可能已经完成的大量测试的有效性。在我的看法中,花费一点努力来创建一个UIViewController的子类并将许多其他类都改为该子类的子类是非常值得的。 - RobP
它远远优于其他解决此类问题的方案。我希望每个人都能更经常地给出像这样精彩的答案,包括我自己。今天我学到了一件很棒的事情,多亏了你。 - M. Porooshani
@Mani 所以类别方法和默认实现将被调用? - Shabarinath Pabba
@ShabarinathPabba 是的。基于此,我已经实现了这个库。如果你想要样例,请查看这个链接。https://github.com/npctech/Tattle-UI-iOS/blob/master/TattleControl/UIController%2BSnapShotButton.m - Mani

6
类别是为了添加新的方法,而不是覆盖现有的方法。也许可以创建一个UIViewController的子类,比如MyUIViewController,并使用下面的代码:
-(void) viewWillAppear:(BOOL) animated {
    // do your "category" stuff
}

然后使用以下代码,将所有其他的UIViewControllers子类化为MyUIViewController:

-(void) viewWillAppear:(BOOL) animated {
    [super viewWillAppear:animated];
    // rest of code for this class
}

请仔细阅读我的问题,如果我采用上述方法,我需要在所有屏幕中编写代码。我不想这样做。 - Mani
我看到了你的问题。如果不更改代码,你无法一次性更改所有视图控制器的行为。上述方法是最安全和最少侵入性的。 - RobP
@Mani,你错了。如果你创建一个类别,那么所有的视图控制器都会有这种行为。通过子类化,你只会在一个屏幕上获得所需的行为。 - Andrey Chernukha
你可以这样做,但是不安全。如果他将所有这些屏幕都作为新父类的子类,那么子类化可能会影响到他的所有屏幕,就像我建议的那样。请参见https://dev59.com/WW435IYBdhLWcg3wrSPN。 - RobP
@AndreyChernukha 是的,你说得对。一旦我使用了那个子类,我就会进入所有VC,如果我不想这样做,我必须重新工作。我是对的吗? - Mani
@RobP 谢谢你。我已经发布了答案。请看这个。如果您有任何建议,请也说出来。 - Mani

1
我了解您为什么想要一个简单的解决方案来测试所有屏幕并轻松删除它,但是:
  • 在分类中无法调用super,而不调用[super viewWillAppear:]可能会产生意想不到的结果,这取决于类和其特定的实现。
  • 方法混编是一种hack方法,如果您从最终版本中删除它,则您的测试版本变得无用,因为它可能表现出非常不同的行为。

另一方面,创建一个UIViewController超类,在其中正确地重写viewWillAppear:并不那么复杂:

  • 代码仅属于单个类。无需为每个“屏幕”重复/维护代码。
  • 您只需要一次更改其他控制器的超类和Nib或Storyboard引用。
  • 您可以将超类保留用于测试和发布,并且行为将类似。
  • 在超类中可以做的事情比在分类中多得多。

最终,了解您试图实现什么是很有趣的。您可能可以通过实现UINavigationControllerDelegate并跟踪推送和弹出的控制器来实现类似的功能。


关于 viewWillAppear 文档:

此方法在将接收方的视图添加到视图层次结构之前和在为显示视图配置任何动画之前调用。您可以重写此方法以执行与显示视图相关的自定义任务。例如,您可以使用此方法更改状态栏的方向或样式,以与要呈现的视图的方向或样式协调。如果您重写此方法,则必须在实现中的某个时刻调用super。

同样,您无法从类别中执行此操作。


你在哪里找到我代码中的 [super viewWillAppear:]?你完整地阅读了我的问题和对这篇文章的评论吗?你如何确定 该代码仅属于一个类?如果我使用 UIViewController,UINavigationController,UITabbarController,UIPageViewController,那么我是否需要全部使用?不是吗?无论如何,至少我需要在所有文件中导入该类,并在所有 VC 中执行此操作 `UIViewController: BaseController"?在我的情况下,这是个坏主意。 - Mani
你的代码应该调用[super viewWillAppear:],但是在类别中却不能这样做。如果你有除了UIViewController之外的其他东西,需要多个超类,你说得没错,但是知道你实际想要实现什么会更有帮助。 - Rivera
我需要[super viewWillAppear:],一旦我在屏幕上编写了viewWillAppear代码?我可以保留viewWillAppear在我的屏幕上。即使我离开,category方法也会被调用。这就是我所需要的。哦,天啊,我想你没有理解我的问题。只要阅读交换概念,你就会明白我的问题。 - Mani

0

你想要实现的目标违背了Category的初衷。不过,除了子类化UIViewController之外,还有另一种方法,但你需要为每个控制器都触碰viewWillAppear方法。

//UIViewController+CustomCategory.h
@interface UIViewConctroller (CustomCategory)

- (void)performCustomization;

@end

//UIViewController+CustomCategory.m
@implementation UIViewController (CustomCategory)

- (void)performCustomization {
    // Do custom stuff…
}

@end

然后在每个控制器中

//MYViewController.m
#import "UIViewController+CustomCategory.h"

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self performCustomization];
}

0

根据您的说法,代码只会在调试模式下执行。那么为什么要担心警告呢?让警告出现,当发布时再删除您的类别。

如果您甚至不需要看到警告信息,可以继续使用相同的答案。

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"

-(void)viewWillAppear:(BOOL)animated
{
    NSLog(@"I get callback here too");
}

#pragma clang diagnostic pop

但我建议选择子类化,因为在XCode工具中删除现有类也不是很困难。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接