This is the upper part of "OBJC and Duck Object", "ObjC and Duck object (bottom)" introduced the duck type of advanced usage, Dependency injection and demo.
I'm the preface.
鸭子类型
(Duck Type) that is:"When you see a bird walking like a duck, swimming like a duck, and barking like a duck, then this bird can be called a duck", the Program Ape language is:"When the caller knows what method this object can invoke, What kind of an instance of this object is it? " This paper makes a simple study of duck type object in OBJC, and uses the magic of this idea in a small demo of "using only one class to implement JSON Entity". Please look at the lower part of the advanced article.
OBJC and Duck type ID type is a big duck.
Duck type is the characteristic of dynamic language, and does not determine the function call relationship at compile time, so all type declarations are given to the compiler. OBJC has found a good balance between dynamic and static, preserving strict static checks and not destroying the dynamic characteristics of the runtime.
We know that sending a message to a OBJC object (or Class) is actually isa
looking for a real function address along its pointer, so that if an object satisfies the structure below, it can send a message to it:
struct Objc_object { Class Isa; } *ID;
|
Known as the id
type, OBJC supports this basic duck type at the language level, and we can convert any object to an ID type to send a message to it, even if it doesn't respond to the message, and the compiler doesn't know it.
As the short definition of the OBJC object in this article: The best definition for a Smalltalk or Objective-C "object" is "something that can respond to messages.
object is not necessarily an instance of a particular type, as long as it responds to the required message.
From @interface to @protocol
As OBJC's innate support for dynamic id
types, the @protocol
duck type provides a compile-time strong typing check that implements the classic duck type usage scenario in cocoa:
@property (ID <uitableviewdatasource> dataSource; @property (ID <uitableviewdelegate> delegate;
|
The use of duck type design interface will give users greater flexibility. And @protocol
can be used to build 伪继承
relationships.
uiscrollviewdelegate<nsobject> uitableviewdelegate<uiscrollviewdelegate>
|
<NSObject>
The existence of the Protocol is on the one hand given to NSProxy
other root classes such as the Duck protocol type a root type, as given to most classes a nsobject root class. To say an episode, because the OBJC class is also id
type, id<UITableViewDataSource>
the shape of the duck type can be used as class object to play, only need to replace the instance method into a class method, such as:
DataSource + (Nsinteger) Numberofsectionsintableview: (UITableView *) TableView { 0; } + (Nsinteger) TableView: (uitableview *) TableView numberofrowsinsection: (nsinteger) Section { 0; } @end
|
Set the data source for table view:
Self.tableView.dataSource = (class<uitableviewdatasource>) [DataSource Class];
|
This non-mainstream writing is legitimate and functioning properly, thanks to the OBJC Plus and minus methods are not reflected in the, but also in the form of a @selector
@protocol
fake, this code I believe no one really write, but it does reflect the flexibility of duck type.
[Demo] A class implements JSON Entity
Entity
object represents the structure of a pure data , such as:
@interface xxuserentity: nsobject @ Property (nonatomic, copy) nsstring *name; @property (nonatomic, copy) nsstring *sex; @property (nonatomic, Assign) nsinteger age; //Balabala .... @end |
In real-world development, this class often corresponds to a JSON string returned by the server, such as:
{"Name": "Sunnyxx", "Sex": "Boy", "age": 24, ...}
|
Parsing these mappings is a purely repetitive work, building classes, writing properties, parsing ... Now there are jsonmodel,mantle and other good frameworks to help. This demo we are going to use the duck type idea to redesign, to simplify these entity classes into a duck class.
Because of the above UserEntity
class, only the getter and setter of the property, this corresponds to the NSMutableDictionary
objectForKey:
and setObjectForKey:
, at the same time, the JSON data will be parsed into a dictionary, which completes the clever docking, the following to implement this class.
The real work is a dictionary that guarantees encapsulation and purity, and this class is used directly NSProxy
as a purely proxy class, exposing only one initialization method:
//XXDuckEntity.h @interface xxduckentity: Nsproxy -(instancetype) initwithjsonstring: ( nsstring *) JSON; @end //xxduckentity.m @interface xxduckentity () @property (nonatomic, Strong) nsmutabledictionary *innerdictionary; @end |
The
nsproxy
defaults to no initialization method, and eliminates the hassle of avoiding other initialization methods, and the JSON string is unpacked to a dictionary for simple direct initialization (the JSON is an array, for the time being):
-(instancetype) initwithjsonstring: (NSString *) JSON { nsdata *data = [JSON datausingencoding:< Span class= "built_in" >nsutf8stringencoding]; id jsonobject = [nsjsonserialization Jsonobjectwithdata:data options:nsjsonreadingallowfragments error: NIL]; if ([Jsonobject iskindofclass:[nsdictionary Class]] { self.innerdictionary = [Jsonobject mutablecopy]; return self; /span> |
NSProxy
It can be said that in addition to the overload message forwarding mechanism, there is no other usage, this is the original intention of it was designed to do nothing, to the proxy object is good. To this proxy message is destined to go the message forwarded, first judge is not a getter or setter of the selector:
- (Nsmethodsignature *) Methodsignatureforselector: (SEL) Aselector { SEL changedselector = Aselector; if ([selfpropertynamescanfromgetterselector:aselector]) { Changedselector = @selector (objectforkey:); } Else if ([selfpropertynamescanfromsetterselector:aselector]) { Changedselector = @selector (setobject:forkey:); } return [[Self.innerdictionary class] instancemethodsignatureforselector:changedselector]; }
|
The signature is replaced with a dictionary of two methods after starting to go forward, where parameters are set and the real call to the internal dictionary:
- (void) Forwardinvocation: (Nsinvocation *) Invocation { NSString *propertyname =Nil Try Getter PropertyName = [Self PropertyNameScanFromGetterSelector:invocation.selector]; if (PropertyName) { Invocation.selector =@selector (objectforkey:); [Invocation setargument:&propertyname Atindex:2];Self, _cmd, key [Invocation Invokewithtarget:Self.innerdictionary]; return; //Try setters propertyname = [self propertyNameScanFromSetterSelector:invocation.selector]; if (propertyname) { Invocation.selector = @selector (setobject:forkey:); [invocation setargument:&propertyname Atindex:3]; //self, _cmd, obj, key [invocation invokewithtarget: self.innerdictionary]; return; [super forwardinvocation:invocation]; /span> |
And, of course, these two essential helpers to get property names from getter and setter:
- (NSString *) Propertynamescanfromgetterselector: (SEL) Selector { NSString *selectorname =Nsstringfromselector (selector); Nsuinteger parametercount = [[Selectorname componentsseparatedbystring:@ ":"] count]-1; if (Parametercount = =0) { return selectorname; } ReturnNil } - (NSString *) Propertynamescanfromsetterselector: (SEL) Selector { NSString *selectorname =Nsstringfromselector (selector); nsuinteger parametercount = [[Selectorname componentsseparatedbystring:< Span class= "string" >@ ":"] count]-1; if ([Selectorname hasprefix:@ "set"] && Parametercount = = 1) { nsuinteger Firstcolonlocation = [Selectorname rangeofstring:@ ":"].location; return [selectorname substringwithrange: Nsmakerange (3, firstcolonlocation-3)].lowercasestring; return nil; /span> |
A simple duck entity is completed, and then all the entity can be defined in a way that is @protocol
not subclass, such as:
@protocolXxuserentity <Nsobject> @property (Nonatomic,CopyNSString *name; @property (nonatomic, copy) nsstring *sex; @property (nonatomic, Strong) nsnumber *age; @protocol xxstudententity <XXUSERENTITY> @property (nonatomic, copy) nsstring *school; @property (nonatomic, copy) nsstring *teacher; @end |
When the data comes back from the network layer, the duck type makes this object as good as it really is:
-(void) requestfinished: (xxduckentity<xxstudententity> *) Student { NSLog (@ "name:%@, school:%@", Student.name, Student.school); }
|
At this point, all the entity is represented as n <Protocol>
.h
files plus a XXDuckEntity
class, and the rest depends on the imagination.
This demo's source code will be given after the next half
http://blog.sunnyxx.com/2014/08/24/objc-duck/
OBJC and Duck object (upper)