• What's the difference between declaring a variable “id” and “NSObject *”? 不及格的程序员


    With a variable typed id, you can send it any known message and the compiler will not complain. With a variable typed NSObject *, you can only send it messages declared by NSObject (not methods of any subclass) or else it will generate a warning. In general, id is what you want.

    id means "an object", NSObject * means "an instance of NSObject or one of its subclasses". There are objects in Objective-C which are not NSObjects (the ones you'll meet in Cocoa at the moment are NSProxyProtocol and Class). If some code expects an object of a particular class, declaring that helps the compiler check that you're using it properly. If you really can take "any object" - for instance you are declaring a delegate and will test all method sends with respondsToSelector:calls - you can use an id.

    Another way to declare an object variable is like "id <NSObject>", which means "any object which implements the NSObject protocol.

    There's often confusion about the difference between the following three declarations in Objective-C:

    1. id foo1;
    2. NSObject *foo2;
    3. id<NSObject> foo3;


    The first one is the most common. It simply declares a pointer to some Objective-C object (see /usr/include/objc/objc.h). id gives the compiler no information about the actual type of the object, so the compiler cannot do compile-time type checking for you. Thus, the compiler will let you send any (*) message to objects declared id. Actually, this is why the common idiom of [[Foo alloc] init] doesn't cause the compiler to complain. +alloc is declared to return type id, so the compiler won't yell when you then send the returned object the message init (or even initWithMyFoo:blah).

    So, objects declared using id are just dynamically typed at runtime. The compiler has no useful information about the object's real type, so it can't warn you if you send it a message that it may not respond to.

    Just because we know that an id is an Objective-C object does not mean that it points to an object that derives from NSObject, or that it even has common methods like retain and release. One solution is to statically type our variable using NSObject* as shown in number 2 above. This gives the compiler information about the class of the object pointed to by foo2 so the compiler can warn if you send a message to foo2 that an NSObject doesn't respond to. This means you can safely call retain, release, description, etc., but the compiler will warn if you call length or count or anything that an NSObject doesn't respond to.

    So, declaring a generic pointer of type NSObject* is very similar to what you would do in other languages, like Java, but it's really a bit too restrictive for a language as flexible as Objective-C. Despite what you may have learned at one point, not all Foundation/Cocoa objects derive from NSObject. As an example, NSProxy is not derived from NSObject, so the foo2 pointer above would not be able to hold an NSProxy subclass, even though NSProxy does implement common methods like retain and release. What you really want is a pointer to any object that behaves like an NSObject. And that's exactly what the third case does.

    Declaring an object as id<NSObject> tells the compiler that you don't care what type the object is, but you do care that it conforms to the specified NSObject protocol**. The compiler will ensure that all objects you assign to that pointer conform to the required protocol. A pointer typed like this can safely hold any NSObject (because NSObject conforms to the NSObject protocol), but it could also hold any NSProxy, because NSProxy also conforms to the NSObject protocol. In english, the declaration id<NSObject> foo3; says "foo3 is a pointer to an object of any type that behaves like an NSObject". This is very powerful, convenient, and expressive. In reality, we often don't care what type an object is, we just care that it responds to the messages that we want to send it (e.g., retain, release). 

    So how do you decide which form you want to use? It's pretty easy. If you don't want (or can't have) any type checking, then use a plain id. This is very common for return types on methods that don't know the type of object they're returning (e.g., +alloc). It is also common to declare delegates to be type id, because delegates are generally checked at runtime with respondsToSelector:, and they usually aren't retained. 

    However, if you do want compile-time type checking, you must decide between the second and third cases. Well, let me just help you out—you want the third case! :-) I've very, very, VERY rarely seen a situation where NSObject * worked but id<NSObject> would not. And using the protocol form has the advantage that it will work with NSProxys. You may think that you never use NSProxys, but Cocoa's distributed objects system makes heavy use of NSProxy subclasses. Additionally, the common case is that you simply want to ensure that an object can be retained or released, and in that case the protocol form conveys that intent better; you really don't care what class the object is, you only care that it behaves like an NSObject.

    by the way , from  Using Protocols in learning objective-c 2.0   

    TablePrinter
    Listing 13.2 shows the header file for the TablePrinter class:

    Listing 13.2 TablePrinter/TablePrinter.h

    #import <Foundation/Foundation.h> 
    @protocol TablePrinterDataSource; @interface TablePrinter : NSObject
    { id <TablePrinterDataSource> dataSource;
    } @property(nonatomic, assign) id <TablePrinterDataSource> dataSource; - (void) printTable; @end

    Listing 13.3 TablePrinter/TablePrinter.m

    #import "TablePrinter.h"
    #import "TablePrinterDataSource.h"
    
    @implementation TablePrinter 
    @synthesize dataSource; - (void) printTable { NSString* separator = @"-------------------------";
    NSString* title = @"Table"; if ( [dataSource respondsToSelector: @selector( tableTitle )] )
    { title = [dataSource tableTitle];
    } printf( "\n%s\n%s\n", [title UTF8String], [separator UTF8String] ); int numRows = [dataSource numberOfRowsInTable]; int j; BOOL printLineNumbers = NO; if ( [dataSource respondsToSelector: @selector(printLineNumbers)] ) { }

    A Problem

    Although the program worked, you should have noticed a small problem when you built it; some compiler warnings such as the following:

    TablePrinter.m:23: warning: '-respondsToSelector:' not found in protocol(s)

    The compiler is complaining that respondsToSelector: isn’t part of the <TablePrinterDataSource> protocol. It isn’t, but it shouldn’t matter because respondsToSelector: is implemented by NSObject, and every object (including the object passed in as the data source) inherits fromNSObject.And it doesn’t matter—the program worked just fine, didn’t it? Well, yes, but it’s a bad idea to get in the habit of ignoring compiler warnings, even semi-spurious ones. If you start ignoring warnings, one day, as sure as the Sun rises, you’ll ignore one that isn’t spurious and there will be trouble.

    You can fix this in one of two ways. In TablePrinter.h, you can change the type in the instance variable and property declarations to:

    NSObject <TablePrinterDataSource> *dataSource;

    This tells the compiler explicitly that dataSource is a subclass of NSObject as well as implementing <TablePrinterDataSource>. A more elegant solution is to change the @protocol line in TablePrinterDataSource.h to be:

    @protocol TablePrinterDataSource <NSObject>

    This means that anything that adopts <TablePrinterDataSource> also adopts the <NSObject> protocol. (This illustrates the important point that one protocol can adopt another.) <NSObject> is a protocol that NSObject adopts (see the sidebar, NSObject and <NSObject>). Again, because NSObject implements respondsToSelector:, you don’t have to do any work beyond making the compiler understand that everything is OK. 

    NSObject and <NSObject>

    This can be a bit confusing(迷惑). <NSObject> is a formal protocol that lists the methods that any class must implement to be a good Objective-C citizen(公民). These are some of the very basic methods like respondsToSelector:, superclass, and the reference counting retain and release. (For a complete list, see the header file NSObject.h.) NSObject (the class) adopts the <NSObject> protocol and then most classes acquire these methods by inheriting, directly or indirectly, from NSObject. NSProxy, Foundation’s other root class (used for building distributed systems), also adopts <NSObject>. 

     

    Protocol Objects and Testing for Conformance

    Protocol objects are objects that represent protocols.They are members of the class Protocol.You obtain a protocol object from the protocol’s name with the @protocol() directive:

    Protocol myDataSourceProtocol = @protocol( TablePrinterDataSource );

    Unlike class objects, protocol objects have no methods and their use is confined to being an argument to the NSObject method conformsToProtocol:.This method returns YES if the receiver implements all the required methods in the protocol and NO other- wise.You could use a protocol object to build a safer version of TablePrinter by cod- ing the accessor methods for the dataSource instance variable yourself and doing a bit of extra work. A safer setter is shown in Listing 13.7.

    Listing 13.7 A safer setter for the TablePrinter data source

    - (void) setDataSource: (id <TablePrinterDataSource>) newDataSource 
    { if ( ! [newDataSource conformsToProtocol: @protocol(TablePrinterDataSource)] ) { dataSource = nil; NSLog(@"TablePrinter: non-conforming data source."); }
    else dataSource = newDataSource; }

    The preceding version of setDataSource: prevents assigning an object that does not conform to the <TablePrinterDataSource> protocol as the dataSource. If such an object were assigned as the dataSource, it would likely cause a crash when one of the required protocol methods was invoked.

    NSObject also has a class method conformsToProtocol: that you can use to test if a class (rather than an instance of a class) conforms to a protocol.The following would return YES:

    [FruitBasket conformsToProtocol: @protocol(TablePrinterDataSource)];

    Testing for protocol conformance is an example of defensive coding.With a few extra lines of code, you can prevent a run time crash.

    Informal(非正式) Protocols

    The protocols discussed so far(到目前为止) in this chapter are called formal(正式) protocols.A formal proto- col is declared with a @protocol statement. Classes that adopt a formal protocol are marked with a <ProtocolName> on the class’s @interface line. Objective-C also has informal protocols. Like formal protocols, informal protocols are a group of related meth- ods that a class might want to implement.An informal protocol declares its methods in a category (usually a category on NSObject), but without a corresponding category implementation:

    @interface NSObject (MyInformalProtocol)

    - (void) informalProtocolMethod;

    @end

    An informal protocol is actually in the nature of a gentle-person’s agreement on the part of the programmer writing a class that adopts the protocol.The programmer agrees to implement the informal protocol’s methods when coding that class, but the compiler does nothing to check that he or she follows through on the agreement.The category functions as a piece of documentation listing the methods in the protocol.There is no type checking at compile time and no way of determining at run time if a class imple- ments an informal protocol. Classes that adopt an informal protocol must declare the protocol methods in their interface section and put the code for those methods in their implementation section.

    page296image19440

    Note

    When you code a class that adopts an informal protocol, the protocol method implementations must go in the class’s @implementation section, not in a category @implementation section. If you place the implementations in a separate category @implementation sec- tion, your method implementations are added to all classes (assuming the protocol is declared as a category on NSObject). This is unlikely to be what you intended to do.

    So what’s the point of using informal protocols? Before Objective-C 2.0, all the methods in a formal protocol were required.The only way to have optional methods in a proto- col was to use an informal protocol and note the optional methods in the documenta- tion. With the advent of @optional and @required, there isn’t much reason to use an informal protocol:You can have optional methods and still enjoy the benefits of a formal protocol.

    You can see this progression in the Apple frameworks: In earlier versions of the AppKit, NSTableDataSource (the data source protocol for AppKit’s NSTableView) was an informal protocol. Beginning in Mac OS X Snow Leopard (v 10.6), NSTableDataSource has been replaced with the NSTableViewDataSource formal protocol.

    Summary

    Protocols add flexibility to program design by letting you type objects by behavior rather than by class.There are many situations where all that is required of an object is that it implement a particular set of methods. In these situations, the object’s class and other behavior are immaterial. Protocols let you formalize this pattern:

    You declare a protocol by giving it a name and declaring its methods between @protocol and @end directives.

    Protocol methods may be either @required or @optional.

    A class adopts a protocol by implementing all of the protocol’s required methods and, perhaps, some or all of the protocol’s optional methods.The class advertises that it has adopted the protocol by appending the protocol name inside angle brackets after the superclass name on the class’s @interface line.The header file containing the protocol declaration must be visible to the adopting class’s @inter- face section.

    You can add a protocol name to the type declaration of an instance variable or a method argument. If you do this, the compiler checks to see that an object that you assign to the instance variable or use as the method argument adopts the spec- ified protocol.

    You can use the class or the instance version of conformsToProtocol: to see if a given class or a given object adopts a particular protocol.

    Before calling an @optional method, you should use respondsToSelector: to make sure the receiver has implemented the method.

    The protocols described in the preceding points are called formal protocols.There are also informal protocols, which are just a list of methods that a class might choose to implement.There is no compiler support for checking informal proto- cols.They were originally used to declare protocols that had optional methods. With the introduction of the @optional directive in Objective-C 2.0, there is no reason to use an informal protocol instead of a formal protocol. 

     

     

     

    page289image11144
    page289image11416
    page289image11688
    page289image11960
  • 相关阅读:
    战略就是做出各种选择和不断权衡取舍;战略就是要刻意与众不同
    获取基目录,它由程序集冲突解决程序用来探测程序集
    Entity Framework的原理及使用方式
    NHibernate使用之详细图解
    最重要的不是你认识多少个人,而是你认识多少种人
    强关系利于执行,弱关系利于创新
    判断它是不是你的社群成员,你要看它对你的态度
    粉丝不在于多,在于够残
    没有请不起的人才,只有付不起的诚意
    所有有可能被互联网取代的组织一定会被取代--颠覆式创新研习社
  • 原文地址:https://www.cnblogs.com/ioriwellings/p/2561066.html
Copyright © 2020-2023  润新知