Cocoa编程之IBAction和IBOutlet含义

IBAction / IBOutlet / IBOutletCollection

时间 2014-05-05 15:00:00  NSHipster原文  http://nshipster.com/ibaction-iboutlet-iboutletcollection/

In programming, what often begins as a necessary instruction eventually becomes a vestigial cue for humans. In the case of Objective-C,  #pragma directives  method
type encodings
 , and all but the most essentialstorage classeshave been rendered essentially meaningless, as the compiler becomes increasingly sophisticated. Discarded and disregarded during the compilation phase, they nonetheless remain useful to the
development process as a whole, insofar as what they can tell other developers about the code itself. 

For developers just starting with Cocoa & Cocoa Touch, the  IBAction , IBOutlet ,
and  IBOutletCollection macros
are particularly bewildering examples of this phenomenon. As we'll learn in this week's article, though having outgrown their technical necessity, they remain a vibrant tradition in the culture of Objective-C development. 



Unlike othertwo-letter prefixes,  IB does
not refer to a system framework, but rather Interface Builder. 

Interface Builder can trace its roots to the halcyon days of Objective-C, when it and Project Builder comprised the NeXTSTEP developer tools (circa
1988). Before it was subsumed into Xcode 4, Interface Builder remained remarkably unchanged from its 1.0 release. An iOS developer today would feel right at home on a NeXTSTEP workstation, control-dragging views into outlets. 

Back when they were separate applications, it was a challenge to keep the object graph represented in a  .nib document
in Interface Builder synchronized with its corresponding  .h &  .m files
in  Project Builder (what would eventually become Xcode).  IBOutlet and  IBAction were
used as keywords, to denote what parts of the code should be visible to Interface Builder. 

IBAction and  IBOutlet are,
themselves, computationally meaningless, as their macro definitions (in  UINibDeclarations.h )
demonstrate: 

#define IBAction void
#define IBOutlet

Well actually, there's more than meets the eye. Scrying the  Clang source code , we see that they're actually defined by  attribute -backed
attributes: 

#define IBOutlet __attribute__((iboutlet))
#define IBAction __attribute__((ibaction))

IBAction

As early as 2004 (and perhaps earlier),  IBAction was
no longer necessary for a method to be noticed by Interface Builder. Any method with a signature -
(void){name}:(id)sender
 would be visible in the outlets pane. 

Nevertheless, many developers find it useful to still use the  IBAction return
type in method declarations to denote that a particular method is connected to an outlet. Even for projects  not using Storyboards / XIBs may choose to employ IBAction to
call out  target / action methods. 

Naming IBAction Methods

Thanks to strong, and often compiler-enforced conventions, naming is especially important in Objective-C, so the question of how to name IBAction methods is one not taken lightly. Though there is some disagreement, the preferred convention is as follows:

  • Return type of  IBAction . 
  • Method name of an active verb, describing the specific action performed.Method names like  didTapButton: or  didPerformAction: sound
    more like things a  delegate might
    be sent. 
  • Required  sender parameter
    of type  id .  
    All
    target / action methods will pass the  sender of
    the action (usually the responder) to methods that take a parameter. If omitted in the method signature, things will still work. 
  • Optional event parameter of type  UIEvent
    *
     , named  withEvent:
    (iOS
    only)
     . In UIKit, a second  UIEvent
    *
     parameter, corresponding to the touch, motion, or remote control event triggering the responder, will be passed to target / action methods accepting this second parameter. The convention is to use  withEvent: in
    the method signature, to match the  UIResponder APIs. 

For example:

// YES
- (IBAction)refresh:(id)sender;

- (IBAction)toggleVisibility:(id)sender
  withEvent:(UIEvent *)event;

// NO
- (IBAction)peformSomeAction;

- (IBAction)didTapButton:(id)sender;

IBOutlet

Unlike  IBAction ,  IBOutlet is
still required for hooking up properties in code with objects in a Storyboard or XIB. 

An  IBOutlet connection is usually
established between a view or control and its managing view controller (this is often done in addition to any  IBAction s
that a view controller might be targeted to perform by a responder). However, an IBOutlet can
also be used to expose a top-level property, like another controller or a property that could then be accessed by a referencing view controller. 

When to use  @property or ivar 

As with anything in modern Objective-C,  properties are preferred to direct ivar access . The same is true of  IBOutlet s: 

// YES
@interface GallantViewController : UIViewController
@property (nonatomic, weak) IBOutlet UISwitch *switch;
@end

// NO
@interface GoofusViewController : UIViewController {
    IBOutlet UISwitch *_switch
}
@end

With the advent of  ARC , it became possible to reference an  IBOutlet from
an instance variable. However, since properties are the conventional way to expose and access members of a class, both externally and internally, they are preferred in this case as well, if only for consistency. 

When to use  weak or  strong

One unfortunate consequence (if you want to call it that) of ARC is the ambiguity of when a  IBOutlet @property should
be declared as  weak or  strong .
The ambiguity arises from the fact that most outlets have no discernible behavioral differences between  weak or  strong —it
just works. 

...except when it doesn't... and things crash, or the compiler warns about  weakor  strong use. 

So what should one do?  Always declare  IBOutlet properties
as  weak , except when they
need to be  strong ,
as explained by Apple in their  Resource Programming Guide section on Nib Files : 

Outlets should be changed to  strong when
the outlet should be considered to own the referenced object: 

  • This is often the case with File’s Owner—top level objects in a nib file are frequently considered to be owned by the  File’s
    Owner
     . 
  • You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view
    hierarchy and must therefore be maintained independently.

The reason why most  IBOutlet views
can get away with  weak ownership
is that they are already owned within their respective view hierarchy, by their superview. This chain of ownership eventually works its way up to the  view owned
by the view controller itself. Spurious use of  strong ownership
on a view outlet has the potential to create a retain cycle. 

IBOutletCollection

IBOutlet 's obscure step-cousin-in-law-once-removed
is IBOutletCollection . Introduced
in iOS 4, this pseudo-keyword allows collections of  IBOutlet s
to be defined in Interface Builder, by dragging connections to its collection members. 

IBOutletCollection is  #define 'd
in  UINibDeclarations.h as: 

#define IBOutletCollection(ClassName)

...which is defined in a much more satisfying way, again,  in the Clang source code : 

#define IBOutletCollection(ClassName) __attribute__((iboutletcollection(ClassName)))

Unlike  IBAction or  IBOutlet ,  IBOutletCollection takes
a class name as an argument, which is, incidentally, as close to Apple-sanctioned  generics as one gets in Objective-C. 

As a top-level object, an  IBOutletCollection @property should
be declared  strong , with an  NSArray
*
 type: 

@property (nonatomic, strong) IBOutletCollection(UIButton) NSArray *buttons;

There are two rather curious things to note about an  IBOutletCollectionarray: 

  • Its order is not necessarily guaranteed . The order of an outlet collection appears to be roughly the order in which their connections are established in Interface Builder. However, there
    are numerous reports of that order changing across versions of Xcode, or as a natural consequence of version control. Nonetheless, having code rely on a fixed order is strongly discouraged. 
  • No matter what type is declared for the property, an IBOutletCollection is
    always an  NSArray 
    .
    In fact, any type can be declared:  NSSet
    *
     ,  id <NSFastEnumeration> —heck,
    even  UIColor *! No matter what
    you put, an  IBOutletCollection will
    always be stored as an  NSArray ,
    so you might as well have that type match up in your declaration to avoid compiler warnings. 

With the advent of Objective-Cobject literals,  IBOutletCollection has
fallen slightly out of favor—at least for the common use case of convenience accessors, as in: 

for (UILabel *label in labels) {
    label.font = [UIFont systemFontOfSize:14];
}

Since declaring a collection of outlets is now as easy as comma-delimiting them within  @[] ,
it may make just as much sense to do that as create a distinct collection. 

Where  IBOutletCollection really
shines is how it allows for multiple to define a unique collection of outlets under a shared identifier. Another advantage over a code-defined  NSArray literal
is that a collection can contain outlets that themselves are not connected to  File's
Owner
 . 

The next time you're managing a significant or variable number of outlets in an iOS view, take a look at  IBOutletCollection . 



IBAction ,  IBOutlet ,
and  IBOutletCollection play
important roles in development, on both the compiler level and human level . As Objective-C continues to rapidly evolve as a platform, it is likely that they may someday be as completely vestigial as the wings of flightless birds or eyes of cavefish. For now,
though, it's important to understand what they are, and how to use them, if you plan on creating apps in any capacity.

时间: 2024-09-20 22:24:27

Cocoa编程之IBAction和IBOutlet含义的相关文章

深入浅出Cocoa多线程编程之 block 与 dispatch quene

深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Apple 在 GCC 4.2 中扩充的新语法特性,其目的是支持多核并行编程.我们可以将 dispatch_queue 与 block 结合起来使用,方便进行多线程编程. 本文源代码下载:点击下载 1,实验工程准备 在 XCode 4.0 中,我们建立一个 Mac OS X Application 类型

iOS开发:多线程编程之NSThread的使用详解

  1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用) 3.GCD 全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的. 这篇我们主要介绍和使用NSThr

iOS多线程编程之NSThread的使用

1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread  2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用) 3.GCD  全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的. 这篇我们主要介绍和使用NSThr

Python中线程编程之threading模块的使用详解

  这篇文章主要介绍了Python中线程编程之threading模块的使用详解,由于GIL的存在,线程一直是Python编程中的焦点问题,需要的朋友可以参考下 threading.Thread Thread 是threading模块中最重要的类之一,可以使用它来创建线程.有两种方式来创建线程:一种是通过继承Thread类,重写它的run方法;另一种是创建一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象作为参数传入.下面分别举例说明.先来看看通过继承th

ruby元编程之method

  这篇文章主要介绍了ruby元编程之method_missing的一个使用细节,本文介绍在使用method_missing时造成死循环的一个现象,需要的朋友可以参考下 我们知道顶级域,定义域的self是啥? 代码如下: puts self #main puts self.class #Object 我们知道当一个方法被调用的时候,如果没有对象接受,默认就是self,如: 代码如下: def tell_me_who puts self end tell_me_who #main 方法调用是这样的

Node.js 异步编程之 Callback介绍(一)

 这篇文章主要介绍了Node.js 异步编程之 Callback介绍(一),本文用实例讲解Callback的相关知识,本文是第一篇,下一篇小编会跟进,需要的朋友可以参考下     Node.js 基于 JavaScript 引擎 v8,是单线程的.Node.js 采用了与通常 Web 上的 JavaScript 异步编程的方式来处理会造成阻塞的I/O操作.在 Node.js 中读取文件.访问数据库.网络请求等等都有可能是异步的.对于 Node.js 新人或者从其他语言背景迁移到 Node.js

Android编程之Activity中onDestroy()调用分析_Android

本文分析了Android编程之Activity中onDestroy()调用方法.分享给大家供大家参考,具体如下: 刚刚一个BUG让我发现,如果 activity 实现了一个回调接口,然后使用 this 设置给需要回调接口的方法,这种应用场景比较常见,最常见的就是实现 onClickListener 接口,然后 findViewById().setOnClickListenr(this) 如果,这个回调接口设置到了一个静态对象(单例模式),当 activity finish() 的时候(按返回键,

Android编程之ICS式下拉菜单PopupWindow实现方法详解(附源码下载)_Android

本文实例讲述了Android编程之ICS式下拉菜单PopupWindow实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 右边这个就是下拉菜单啦,看见有的地方叫他 ICS式下拉菜单,哎哟,不错哦! 下面先讲一下实现原理: 这种菜单实际上就是一个弹出式的菜单,于是我们想到android PopupWindow 类,给他设置一个view 在弹出来不就OK了吗. PopupWindow 的用法也很简单 主要方法: 步骤1.new 一个实例出来,我们使用这个构造方法即可, 复制代码 代码如

Android编程之在SD卡上进行文件读写操作实例详解_Android

本文实例讲述了Android编程之在SD卡上进行文件读写操作的方法.分享给大家供大家参考,具体如下: 很多知识只有真正理解掌握之后才能运用自如,举一反三.对Java中的文件操作和android系统SD卡里面的文件操作,你觉得有区别吗,显然没有本质区别,如果勉强说有,那也是不足为道滴,但我们在实际运用中却要注意如下几点,不然问题会缠上你. 1.首先想要对android系统SD卡里文件操作需要添加使用权限: android系统是不会让外来程序随意动自己内存的,如果没有许可证,不好意思,不准你动我地盘