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 directivesmethod
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.

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

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

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  2. [深入浅出Cocoa]iOS网络编程之Socket

    http://blog.csdn.net/kesalin/article/details/8798039 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   [深入浅出Co ...

  3. [Cocoa]深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  4. iOS多线程编程之NSThread的使用

      目录(?)[-] 简介 iOS有三种多线程编程的技术分别是 三种方式的有缺点介绍 NSThread的使用 NSThread 有两种直接创建方式 参数的意义 PS不显式创建线程的方法 下载图片的例子 ...

  5. iOS多线程编程之NSThread的使用(转)

    本文由http://blog.csdn.net/totogo2010/原创 1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation  ...

  6. [转]iOS多线程编程之NSThread的使用

    1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue ...

  7. iOS多线程编程之NSThread的使用(转载)

    1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1.NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的 ...

  8. C++混合编程之idlcpp教程(一)

    我是C++语言的忠实拥趸,由于在上学时经历了资源匮乏的DOS时代,对C/C++这种更加接近硬件的语言由衷的喜爱.一直以来也是已C++作为工作的语言,对别的语言那是不屑一顾.在java火爆流行的时候,没 ...

  9. ##DAY13——可视化编程之XIB

    ##DAY13——可视化编程之XIB 1.关联控件 2.关联事件 3.关联手势 4.关联代理 这个时候即使不给控制器用下面方法添加代理,代理方法也是可以使用的,只是没有方法提示: 其他重要地方: #i ...

随机推荐

  1. JDBC编程-事务编程(四)

    事务的概念 事务的概念在我看来是指的是一组sql序列,这个序列是一块执行的单位,要么全部执行,要不全部执行,这样可以很好的对数据库进行并发控制. 因为数据库是多个用户都可以同时操作的,如果多个用户同时 ...

  2. hive的strict模式;where,group by,having,order by同时使用的执行顺序

    主要限制三种情况 (1) 有partition的表查询需要加上where子句,筛选部分数据实现分区裁剪,即不允许全表全分区扫描,防止数据过大 (2) order by 执行时只产生一个reduce,必 ...

  3. Android Service详解

    service作为四大组件值得我们的更多的关注 在Android中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务.例如,一个从service播放音乐的音乐播放器,应 ...

  4. 手把手带你走进MVP +Dagger2 + DataBinding+ Rxjava+Retrofit 的世界

    0.0 Android开发现在的变化用一个词来形容就是 :翻天覆地 越来越多的项目使用了MVP + Rxjava+Retrofit +Dagger2 + DataBinding等等东西.. 但是这些东 ...

  5. Framework启动过程浅析

    浅显的总结一下Framework启动大概过程 总体 Android底层是linux系统,因而在开机时仍然是运行天字第一号进程inti,读取init.rc来创建第一个Dalvik进程zygote,下面是 ...

  6. 网站开发进阶(三十四)编码中的setCharacterEncoding 理解

    编码中的setCharacterEncoding 理解 1.pageEncoding="UTF-8"的作用是设置JSP编译成Servlet时使用的编码. 2.contentType ...

  7. “XcodeGhost”病毒之后,苹果更应注…

    虽然大家都在期待中秋假期的到来,不过让开发者挺闹心的一件事就是这几天网上.朋友圈以及各种群中炒得沸沸扬扬的"XcodeGhost"病毒事件,就连央视也惊动了!! 事件起源 事件起源 ...

  8. Chapter 2 User Authentication, Authorization, and Security(8):创建映射到登录名的数据库用户

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/38944121,专题目录:http://blog.csdn.net/dba_huangzj ...

  9. C++ Primer 有感(异常处理)(四)

    查看普通函数的声明的时候,不可能知道该函数会抛出什么异常,但是在捕获异常的时候要知道一个函数会抛出什么样的异常,以便捕获异常. 异常说明:指定,如果函数抛出异常,抛出的异常将是包含在该说明中的一种,或 ...

  10. Linux 共享内存 详解

    一.什么是共享内存区 共享内存区是最快的可用IPC形式.它允许多个不相关的进程去访问同一部分逻辑内存.如果需要在两个运行中的进程之间传输数据,共享内存将是一种效率极高的解决方案.一旦这样的内存区映射到 ...