1. Trang chủ
  2. » Công Nghệ Thông Tin

IT training NSHipster obscure topics in cocoa objective c thompson 2013 11 12

0 63 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 0
Dung lượng 6,82 MB

Nội dung

first edition nshipster NS obscure topics in cocoa & objective-c mattt thompson ! Mattt Thompson NSHipster: Obscure Topics In Cocoa & Objective-C ! ! ! ! ! ! ! ! ! ! ! ! Copyright © 2013 Mattt Thompson
 All rights reserved Illustrated by Conor Heelan ISBN 978-0-9912182-1-9 NSHipster
 Portland, Oregon
 http://nshipster.com
 Table of Contents Objective-C 10 #pragma 11 nil / Nil / 
 NULL / NSNull 16 BOOL / bool / 
 Boolean / NSCFBoolean 20 Equality 24 Type Encodings 35 C Storage Classes 41 @ 47 attribute 61 instancetype 71 NS_ENUM & 
 NS_OPTIONS 74 Foundation &
 CoreFoundation 79 Key-Value Coding 
 Collection Operators 80 Key-Value Observing 86 NSError 99 NSOperation 108 NSSortDescriptor 115 NSPredicate 120 NSExpression 133 NSFileManager 141 NSValue 153 NSValueTransformer 156 NSDataDetector 160 CFBag 166 NSCache 171 NSIndexSet 174 NSOrderedSet 176 NSHashTable & 
 NSMapTable 181 UIKit 188 UIMenuController 189 UILocalizedIndexedCollation 197 UIAppearance 204 Localization,
 Internationalization &
 Accessibility 209 NSLocale 210 NSLocalizedString 217 UIAccessibility 222 NSFormatter 232 CFStringTransform 241 NSLinguisticTagger 246 API Design 254 The Law of Demeter 255 The Principle of 
 Least Surprise 265 Naming 269 Community 273 Stewardship 274 Empathy 284 Introduction To be an NSHipster is to care deeply about the craft of writing code In cultivating a deep understanding and appreciation of Objective-C, its frameworks and ecosystem, one is able to create apps that delight and inspire users This book takes a structured approach to learning ObjectiveC development, starting from the language and system frameworks, and moving onto high-level concerns, like internationalization, design, and community It is as much a technical manual as it is a meditation on the practice of coding I hope that by reading this, you will share in the excitement of discovering new insights, and taking pride in your work 
 Objective-C ! 10 #pragma #pragma declarations are a mark of craftsmanship in Objective-C Although originally used to make source code portable across different compilers, the Xcode-savvy coder uses #pragma declarations to very different ends In this modern context, #pragma skirts the line between comment and code As a preprocessor directive, #pragma evaluates at compile-time, but unlike other macros, #pragma is not used to change the runtime behavior of an application Instead, #pragma declarations are used by Xcode to accomplish two primary tasks: organizing code and inhibiting compiler warnings Organizing Your Code Code organization is a matter of hygiene How you structure your code is a reflection on you and your work A lack of convention and internal consistency indicates either carelessness or incompetence—and worse, makes a project difficult to maintain and collaborate on.
 11 Good habits start with #pragma mark: @implementation ViewController - (id)init {
 
 } #pragma mark - UIViewController - (void)viewDidLoad {
 
 } #pragma mark - IBAction - (IBAction)cancel:(id)sender {
 
 } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
 {
 
 } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
 } Use #pragma mark in your @implementation to divide code into logical sections Not only these sections make it easier to read through the code itself, but it also adds visual cues to the Xcode source navigator 12 #pragma mark declarations starting with a dash (-) are preceded with a horizontal divider Start by grouping methods according to their originating class For example, an NSInputStream subclass would have a group marked NSInputStream, followed by a group marked NSStream Things like IBAction outlets, or methods corresponding to target / action, notification, or KVO selectors probably deserve their own sections as well Finally, if a class conforms to any @protocols, group all of the methods from each protocol together, and add a #pragma mark header with the name of that protocol Your code should be clean enough to eat off of So take the time to leave your m files better than how you found them Inhibiting Warnings What's even more annoying than poorly-formatted code? Code that generates warnings Especially 3rd-party code There are few things as irksome as that one vendor library that takes forever to compile, and finishes with 200+ warnings Even shipping code with a single warning is in poor form 13 Try setting the -Weverything flag and checking the "Treat Warnings as Errors" box your build settings 
 This turns on Hard Mode in Xcode But sometimes there's no avoiding compiler warnings Deprecation notices and retain-cycle false positives are two common examples where this might happen In those rare cases where you are absolutely certain that a particular compiler warning should be inhibited, #pragma can be used to suppress them: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-variable" OSStatus status = SecItemExport( ); NSCAssert(status == errSecSuccess, @"%d", status); #pragma clang diagnostic pop This code sample is an example of an otherwise unavoidable warning from the static analyzer When compiling in Release mode, assertions are ignored, so Clang warns that status is an unused variable Using #pragma clang diagnostic push/pop, you can tell the compiler to ignore certain warnings for a particular section of code (the original diagnostic settings are restored with the final pop) Just don't use #pragma as a way to sweep legitimate warnings under the rug—it will only come back to bite you later 14 You can read more about the LLVM's use of #pragma in the Clang Compiler User's Manual Like a thrift store 8-track player turned into that lamp in the foyer, #pragma remains a curious vestige of the past: Once the secret language of compilers, it is now re-purposed to bettercommunicate intent to other programmers How delightfully vintage!
 15 nil / Nil / 
 NULL / NSNull Understanding the concept of nothingness is as much a philosophical issue as it is a pragmatic one We are inhabitants of a universe made up of somethings, yet reason in a logical universe with existential uncertainties As a physical manifestation of a logical system, computers are faced with the intractable problem of how to represent nothing with something In Objective-C, there are several different varieties of nothing C represents nothing as for primitive values, and NULL for pointers (which is equivalent to in a pointer context) Objective-C builds on C's representation of nothing by adding nil nil is an object pointer to nothing Although semantically distinct from NULL, they are equivalent to one another 16 On the framework level, Foundation defines the NSNull class, which defines a single class method, +null, which returns a singleton NSNull instance NSNull is different from nil or NULL, in that it is an actual object, rather than a zero value Additionally, in Foundation/NSObjCRuntime.h, Nil is defined as a class pointer to nothing This lesser-known titlecase cousin of nil doesn't show up much very often, but it's at least worth noting There's Something About nil Newly-alloc'd NSObjects start life with their contents set to This means that all pointers an object has to other objects begin as nil, so it's unnecessary to, for instance, set self.association = nil in init methods Perhaps the most notable behavior of nil, though, is that it handles messages sent to it In other languages, like C++, sending a message to a null pointer would crash a program, but in Objective-C, invoking a method on nil returns a zero value This greatly simplifies expressions, as it obviates the need to check for nil before doing anything: 17 // For example, this expression 
 if (name != nil && [name isEqualToString:@"Steve"]) { } ! // can be simplified to:
 if ([name isEqualToString:@"steve"]) { } Being aware of how nil works in Objective-C allows this convenience to be a feature, rather than a source of hard-tofind bugs Guard against cases where nil values are unwanted, either by returning early, or adding a NSParameterAssert to throw an exception NSNull: Something for Nothing NSNull is used throughout Foundation and other system frameworks to skirt around the limitations of collections like NSArray and NSDictionary, which cannot contain nil values NSNull effectively boxes NULL or nil values, so that they can be stored in collections: 18 NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary]; ! mutableDictionary[@"someKey"] = [NSNull null]; 
 // Sets value of NSNull singleton for `someKey` ! NSLog(@"Keys: %@", [mutableDictionary allKeys]); 
 // @[@"someKey"] ! So to recap, here are the four values representing nothing that every Objective-C programmer should know about: Symbol Value Meaning NULL (void *)0 literal null value for C pointers nil (id)0 literal null value for Objective-C objects Nil (Class)0 literal null value for Objective-C classes NSNull [NSNull null] singleton object used to represent null 19 BOOL / bool / 
 Boolean / NSCFBoolean Truth, Vēritās: The entire charter of Philosophy is founded upon the pursuit of it, and yet its exact meaning and implications still elude us Does truth exist independently, or is it defined contingently?
 Can a proposition be at once both true and false? 
 Is there absolute truth in anything, or is everything relative? Once again, encoding our logical universe into the cold, calculating bytecode of computers forces us to deal with these questions one way or another And as you'll see from our discussion of boolean types in C & Objective-C, truth is indeed stranger than fiction Objective-C defines BOOL to encode truth value It is a typedef of a signed char, with the macros YES and NO to represent true and false, respectively 20 Boolean values are used in conditionals, such as if or while statements, to conditionally perform logic or repeat execution When evaluating a conditional statement, the value is considered "false", while any other value is considered "true" Because NULL and nil have a value of 0, they are considered "false" as well In Objective-C, use the BOOL type for parameters, properties, and instance variables dealing with truth values When assigning literal values, use the YES and NO macros The Wrong Answer to the Wrong Question Novice programmers often include an equality operator when evaluating conditionals: if ([a isEqual:b] == YES) { } Not only is this unnecessary, but depending on the left-hand value, it may also lead to unexpected results Consider this function, which returns whether two integers are different: static BOOL different (int a, int b) { return a - b; } A programmer might take some satisfaction in the clever simplicity of this approach: indeed, two integers are equal if and only if their difference is 21 However, because BOOL is typedef 'd as a signed char on 32bit architectures, this will not behave as expected: different(11, 10) // YES different(10, 11) // NO (!) different(512, 256) // NO (!) Now, this might be acceptable for JavaScript, but Objective-C don't suffer fools gladly On a 64-bit iOS, BOOL is defined as a bool, rather than signed char, which precludes the runtime from these type conversion errors Deriving truth value directly from an arithmetic operation is never a good idea Use the == operator, or cast values into booleans with the ! (or !!) operator The Truth About NSNumber and BOOL Pop quiz: what is the output of the following expression? NSLog(@"%@", [@(YES) class]); The answer: NSCFBoolean Wait, what? 22 All this time, we've been led to believe that NSNumber boxes primitives into an object representation Any other integer or float derived NSNumber object shows its class to be NSCFNumber What gives? NSCFBoolean is a private class in the NSNumber class cluster It is a bridge to the CFBooleanRef type, which is used to wrap boolean values for Core Foundation collections and property lists CFBoolean defines the constants kCFBooleanTrue and kCFBooleanFalse Because CFNumberRef and CFBooleanRef are different types in Core Foundation, it makes sense that they are represented by different bridging classes in NSNumber ! Wrapping things up, here is a table with the truth types and values in Objective-C: Name Type Header True False BOOL signed char / bool objc.h YES NO bool _Bool (int) stdbool.h TRUE FALSE Boolean unsigned char MacTypes.h TRUE FALSE NSNumber NSCFBoolean Foundation.h @(YES) @(NO) 23 Equality The concept of equality is a central point of debate and inquiry in philosophy and mathematics, with far-reaching implications for matters of ethics, justice, and public policy From an empiricist perspective of the universe, two objects are equal if they are indistinguishable from one another in measurable observations Egalitarians, operating on a human scale, hold that individuals should be considered equal members of the societal, economic, political, and judicial systems they inhabit It is the task of programmers to reconcile our logical and physical understanding of equality with the semantic domains we model Equality & Identity First and foremost, it is important to make a distinction between equality and identity 24 Two objects may be equal or equivalent to one another, if they share a common set of properties Yet, those two objects may still be thought to be distinct, each with their own identity In code, an object's identity is tied to its memory address NSObject tests equality with another object with the method isEqual: In its base implementation, an equality check is essentially a test for identity: @implementation NSObject (Approximate) - (BOOL)isEqual:(id)object { return self == object; } @end isEqual Subclasses of NSObject implementing their own isEqual: method are expected to the following: • Implement a new isEqualToClassName: method, which performs the meaningful value comparison • Override isEqual: to make class and object identity checks, falling back on the aforementioned class comparison method • Override hash, which will be described in the next section 25 For container classes like NSArray, NSDictionary, and NSString, equality deep equality comparison, testing equality for each member in pairwise fashion: @implementation NSArray (Approximate) - (BOOL)isEqualToArray:(NSArray *)array { if (!array || [self count] != [array count]) { return NO; ! } for (NSUInteger idx = 0; idx < [array count]; idx++) { if (![self[idx] isEqual:array[idx]]) { return NO; } ! } return YES; } ! - (BOOL)isEqual:(id)object { if (self == object) { return YES; ! } if (![object isKindOfClass:[NSArray class]]) { return NO; ! } return [self isEqualToArray:(NSArray *)object]; } @end ! 26 isEqualTo : The following NSObject subclasses in Foundation have custom equality implementations, with the corresponding method: NSAttributedString -isEqualToAttributedString: NSData -isEqualToData: NSDate -isEqualToDate: NSDictionary -isEqualToDictionary: NSHashTable -isEqualToHashTable: NSIndexSet -isEqualToIndexSet: NSNumber -isEqualToNumber: NSOrderedSet -isEqualToOrderedSet: NSSet -isEqualToSet: NSString -isEqualToString: NSTimeZone -isEqualToTimeZone: NSValue -isEqualToValue: When comparing two instances of any of these classes, one is encouraged to use these high-level methods rather than isEqual: However, our theoretical implementation is yet incomplete Let's turn our attention now to hash, after a quick detour to clear something up about NSString 27 The Curious Case of NSString Equality As an interesting aside, consider the following: NSString *a = @"Hello"; NSString *b = @"Hello"; BOOL wtf = (a == b); // YES (!) To be perfectly clear: the correct way to compare two NSString objects is -isEqualToString: Under no circumstances should NSString objects be compared with the == operator So what's going on here? Why does this work, when the same code for NSArray or NSDictionary literals wouldn't this? It all has to with an optimization technique known as string interning, whereby one copy of immutable string values NSString *a and *b point to the same copy of the interned string value @"Hello" Again, this only works for statically-defined, immutable strings Constructing identical strings with NSString +stringWithFormat: will objects with different pointers Interestingly enough, Objective-C selector names are also stored as interned strings in a shared string pool.
 28 Hashing The primary use case of object equality tests for objectoriented programming is to determine collection membership To keep this fast, subclasses with custom equality implementations are expected to implement hash: • Object equality is commutative 
 ([a isEqual:b] [b isEqual:a]) • If objects are equal, their hash values must also be equal 
 ([a isEqual:b] [a hash] == [b hash]) • However, the converse does not hold: two objects need not be equal in order for their hash values to be equal 
 ([a hash] == [b hash] ¬ [a isEqual:b]) Now for a quick flashback to Computer Science 101: Hashing Fundamentals A hash table is a fundamental data structure in programming, and it's what enables NSSet & NSDictionary to have fast (O(1)) lookup of elements We can best understand hash tables by contrasting them to arrays 29 Arrays store elements in sequential indexes, such that an Array of size n will have slots at positions 0, 1, up to n - To determine where an element is stored in the array (if at all), each position would have to be checked one-by-one (unless the array happens to be sorted, but that's another story) Hash Tables take a slightly different approach Rather than storing elements sequentially (0, 1, , n-1), a hash table allocates n positions in memory, and uses a function to calculate a position within that range A hash function is deterministic, and a good hash function generates values in a relatively uniform distribution without being too computationally expensive A hash collision occurs when two different objects calculate the same hash value When this happens, the hash table will seek from the point of collision and place the new object in the first open slot As a hash table becomes more congested, the likelihood of collision increases, which leads to more time spent looking for a free space One of the most common misconceptions about implementing a custom hash function is that hash values must be distinct This often leads to needlessly complicated implementations, with incantations copied from Java textbooks In reality, a simple XOR over the hash values of critical properties is sufficient most of the time 30 The trick is in determining the critical values of an object For an NSDate, the time interval since a reference date would be enough to go on: @implementation NSDate (Approximate) - (NSUInteger)hash { return abs([self timeIntervalSinceReferenceDate]); } For a UIColor, a bit-shifted sum of RGB components would be a convenient calculation: @implementation UIColor (Approximate) - (NSUInteger)hash { CGFloat red, green, blue; [self getRed:&red green:&green blue:&blue alpha:nil]; return ((NSUInteger)(red * 255) 'mattt@nshipster.com' } s.source = { :git => 'https://github.com/nshipster/ NSHipsterKit.git', :tag => '1.0.0' } s.source_files = 'NSHipsterKit' end ! 278 Once the podspec has been submitted to the CocoaPods specs repository, a consumer would be able to add it to their own project with a Podfile: Podfile platform :ios, '7.0' pod 'NSHipsterKit', '~> 1.0' Maintaining Once the initial thrill of releasing a library has passed, the real work begins The thing to remember is that a flurry of stars, watchers, and tweets may be exciting, but they don't amount to anything of real importance Only when users start to come with their questions, issues, and pull requests does code become software Versioning Versioning is a contract that library authors make to consumers in how software will be changed over time The prevailing convention is Semantic Versioning, in which a release has a major, minor, and patch version, with each level signifying particular usage implications 279 • A patch, or bugfix, release changes only implementation, keeping the public API and thus all documented intact Consumers should be able to update between patch versions without any change to their own code • A minor, or point, release changes the public API in nonbreaking ways, such as adding a new feature Again, developers should expect to have consumer code between minor versions work pretty much as expected • A major release is anything that changes the public API in a backwards-incompatible way Updating between major versions effectively means migrating consumer code to a new library A comprehensive set of guidelines for semantic versioning can be found at http://semver.org By following a few basic rules for versioning, developers are able to set clear expectations for how changes affect will affect shipping code Deviating from these conventions as an author is disrespectful to anyone using the software, so take this responsibility seriously ! 280 Answering Questions One of our greatest flaws as humans is our relative inability to comprehend not knowing or understanding something that we ourselves This makes is extremely difficult to diagnose (and at times empathize with) misunderstandings that someone else might be having There's also a slight sadistic tendency for developers to lord knowledge over anyone who doesn't know as much as they We had to figure it out for ourselves (uphill both ways, in the snow) so why shouldn't they have to as well? We must learn how to better than this RTFM is a lame answer to any question It's also a dead-end to a potential learning experience for yourself Rather than disdaining questions, take them as an opportunity to understand what you can better Each question is a data point for what could be clarified or improved within your own software and documentation And one thing to consider: for each person who asks a question, there are dozens of others who don't and get frustrated and give up Answering one question on a mailing list or developer forum helps many more people than just the asker ! 281 Transitioning The fate of any successful enterprise is to outgrow its original creators While this may be a troubling or unwelcome notion, it is nevertheless something that any responsible creator should keep in mind If anything, the reminder that all of this is fleeting gives reason to find enjoyment in even the minutia of a preoccupation Recruiting & Delegating As a project grows, natural leaders will emerge If you see someone consistently answering questions in issues or sending pull requests with bug fixes, ask if they would like some more responsibility Co-maintainers don't come pre-baked; individuals must grow into that role And that role is something that must be defined over time by everyone involved Avoid drama and hard feelings by communicating honestly and often with collaborators Sunsetting All software has a lifecycle At some point, all things must come to an end Libraries outgrow their usefulness, or 282 supplanted by another piece of software, or simply fall out of favor In any case, there will come a time when the lights need to be turned off, and it is the responsibility of the maintainer to wrap things up • Announce the ending of the project, offering suggestions for how to migrate to another solution • Keep the project around, but make a commit that removes source files from the master branch.
 (Git will keep everything safe in history) • Thank everyone involved for their help and contributions The alternative is to become a liability, an attractive nuisance a mockery of what once was a respectable code base ! Creating is one of the most fulfilling experiences in life, and it's something that's only improved by sharing with others As software developers, we have a unique opportunity to be unbounded by physical limitations to help one another On the occasion that you have the opportunity to participate in the community, be sure to make the most of it— you'll be happy you did.
 283 Empathy Great software is created to scratch one's own itch Being close to a problem provides not only insight for how to solve it, but the motivation to actually follow through It's the better angels of our nature that compel us to share these solutions with one other And in the open source world, we so freely, with only a karmic expectation of paying the favor forward We naturally want to help one another, to explain ideas, to be generous and patient However, on the Internet, human nature seems to drop a few packets Practicing empathy online becomes a feat of moral athleticism Lacking many of the faculties to humanize and understand one another (facial expressions, voice tonality, non-verbal cues) we can lose sight of who we're talking to, and become less human ourselves ! 284 Before engaging with someone, take a moment to visualize how that encounter would play out in real life Would you be proud of how you conducted yourself? Rather than responding defensively to snark or aggression, stop to consider what could have motivated that reaction Is there something you could be doing better as a programmer or community member? Or are they just having a bad day? (We've all had our bad days) And let it never be that someone is marginalized for their ability to communicate in English Be patient and ask questions Respond simply and clearly ! Everything you need to succeed as a software developer extends from a conscious practice of empathy.
 285 ! About NSHipster NSHipster is a journal of the overlooked bits in Objective-C and Cocoa Updated weekly Launched in the Summer of 2012, NSHipster has become an essential resource for iOS and Mac developers around the world Colophon The text is set in Minion, by Robert Slimbach, with code excerpts set in Menlo, by Jim Lyles The cover is set in Trajan by Carol Twombly, in an homage to The C Programming Language by Brian Kernighan and Dennis Ritchie.
 About the Author Mattt Thompson is the creator & maintainer of AFNetworking and other popular open-source projects, including Postgres.app, Induction, Helios, and Nomad Previously, Mattt has worked as Mobile Lead at Heroku, an iPhone & iPad Developer at Gowalla, and a Rails and FrontEnd Engineer at Cerego His work has taken him across the United States and around the world, to speak at conferences and meetups about topics in Objective-C, Ruby, Javascript, web development, design, linguistics, and philosophy Mattt holds a Bachelor's degree in Philosphy and Linguistics from Carnegie Mellon University ... and community It is as much a technical manual as it is a meditation on the practice of coding I hope that by reading this, you will share in the excitement of discovering new insights, and taking... Mattt Thompson NSHipster: Obscure Topics In Cocoa & Objective- C ! ! ! ! ! ! ! ! ! ! ! ! Copyright © 2013 Mattt Thompson All rights reserved Illustrated by Conor Heelan ISBN 978-0-9 9121 82-1-9 NSHipster ... To be an NSHipster is to care deeply about the craft of writing code In cultivating a deep understanding and appreciation of Objective- C, its frameworks and ecosystem, one is able to create apps

Ngày đăng: 05/11/2019, 14:34

TỪ KHÓA LIÊN QUAN