In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces the iOS development skills that can not be missed, which have a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
The following questions are mainly recorded:
When does the NSString property use copy and when does strong use?
Assertion handling in Foundation
IBOutletCollection
Use of NSRecursiveLock Recursive Lock
NSHashTable
When does the NSString property use copy and when does strong use?
When we declare a NSString property, we usually have two choices (based on the ARC environment) for its memory-related features: strong and copy. So what's the difference between the two? When should I use strong and when should I use copy? Let's look at an example first.
Example
We define a class and declare two string properties for it, as follows:
@ interface TestStringClass ()
@ property (nonatomic, strong) NSString * strongString
@ property (nonatomic, copy) NSString * copyedString
@ end
The above code declares two string properties, one of which is strong and the other is copy. Let's see the difference between them.
First, we use an immutable string to assign values to these two attributes
-(void) test {
NSString * string = [NSString stringWithFormat:@ "abc"]
Self.strongString = string
Self.copyedString = string
NSLog (@ "origin string:% p,% p", string, & string)
NSLog (@ "strong string:% p,% p", _ strongString, & _ strongString)
NSLog (@ "copy string:% p,% p", _ copyedString, & _ copyedString)
}
The output is as follows:
Origin string: 0x7fe441592e20, 0x7fff57519a48 strong string: 0x7fe441592e20, 0x7fe44159e1f8 copy string: 0x7fe441592e20, 0x7fe44159e200
We will see that in this case, both the object with the strong property and the object with the copy property point to the same address, that is, the address pointed to by the string. If we change to the MRC environment and print the reference count of string, we will see that its reference count value is 3, that is, both the strong operation and the copy operation increase the reference count value of the original string object by 1.
Next, let's change the string from immutable to mutable and see what happens. The following sentence is about to follow.
NSString * string = [NSString stringWithFormat:@ "abc"]
Change it to:
NSMutableString * string = [NSMutableString stringWithFormat:@ "abc"]
The output is as follows:
Origin string: 0x7ff5f2e33c90, 0x7fff59937a48 strong string: 0x7ff5f2e33c90, 0x7ff5f2e2aec8 copy string: 0x7ff5f2e2aee0, 0x7ff5f2e2aed0
You can see that the copy property string no longer points to the string string object, but instead makes a deep copy of the string string and lets the _ copyedString object point to it. In the MRC environment, printing the reference count of both, you can see that the reference count of the string object is 2, while the reference count of the _ copyedString object is 1.
At this point, if we modify the string string, we can see that since _ strongString and string point to the same object, the value of _ strongString will also change (note that at this time, the type of _ strongString is actually NSMutableString, not NSString); while _ copyedString points to another object, so it will not change.
Conclusion
Since NSMutableString is a subclass of NSString, a NSString pointer can point to a NSMutableString object, so our strongString pointer points to a variable string that is OK.
As can be seen in the above example, when the source string is NSString, because the string is immutable, both the objects with strong and copy attributes point to the source object, and the copy operation only makes a shallow copy.
When the source string is NSMutableString, the strong property simply increases the reference count of the source string, while the copy property makes a deep copy of the source string to produce a new object, and the copy property object points to the new object. It is also important to note that this copy property object is always of type NSString, not NSMutableString, so it is immutable.
There is also a performance problem, that is, when the source string is NSMutableString,strong, it simply increases the reference count of the object, while the copy operation performs a deep copy, so there will be differences in performance. This is not a problem if the source string is NSString.
Therefore, when declaring the NSString attribute, whether to choose strong or copy can be determined according to the actual situation. However, in general, when we declare an object as NSString, we don't want it to change, so in most cases, we recommend using copy to avoid unexpected problems caused by mutable string modifications.
There are also some interesting things about string memory management, which can be learned by referring to the NSString feature analysis.
Referenc
NSString copy not copying?
NSString characteristic analysis learning
When does NSString use copy and when does strong use?
Assertion handling in Foundation
Assertions are often used when looking at the code of some third-party libraries, or when writing some basic classes. So I'd like to summarize some of the problems with assertions in Objective-C.
Two sets of macros related to assertions are defined in Foundation, namely:
NSAssert / NSCAssert NSParameterAssert / NSCParameterAssert
The two groups of macros should be different in function and semantics, and these differences mainly have the following two points:
If we need to ensure that the input parameters of the method or function are correct, we should use NSParameterAssert / NSCParameterAssert; at the top of the method (function) and NSAssert / NSCAssert in other cases.
Another difference is between C and Objective-C. NSAssert / NSParameterAssert should be used in the context (method) of Objective-C, while NSCAssert / NSCParameterAssert should be used in the context (function) of C.
When an assertion fails, an exception is usually thrown as follows:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:' true is not equal to false'
In order to deal with assertions, Foundation specifically defines a NSAssertionHandler to handle the failure of assertions. The NSAssertionHandler object is created automatically to handle failed assertions. When the assertion fails, a string is passed to the NSAssertionHandler object to describe the cause of the failure. Each thread has its own NSAssertionHandler object. When called, an assertion handler prints an error message containing methods and classes (or functions) and throws a NSInternalInconsistencyException exception. As you can see above.
We rarely call NSAssertionHandler's assertion handling methods directly, usually automatically.
NSAssertionHandler does not provide many methods, just three, as follows:
/ / returns the NSAssertionHandler object with the current thread.
/ / if the current thread does not have an associated assertion processor, the method creates one and assigns it to the current thread
+ (NSAssertionHandler *) currentHandler
/ / this method is called when NSCAssert or NSCParameterAssert assertions fail.
-(void) handleFailureInFunction: (NSString *) functionName file: (NSString *) object lineNumber: (NSInteger) fileName description: (NSString *) line, format,...
/ / this method is called when NSAssert or NSParameterAssert assertions fail.
-(void) handleFailureInMethod: (SEL) selector object: (id) object file: (NSString *) fileName lineNumber: (NSInteger) line description: (NSString *) format,...
In addition, a constant string is defined.
NSString * const NSAssertionHandlerKey
It is mainly used to get or set the assertion processor in the thread's threadDictionary dictionary.
One more thing to note about assertions is that after Xcode 4.2, assertions are turned off by default in the release version, which is handled by the macro NS_BLOCK_ASSERTIONS. That is, when compiling the release version, all assertion calls are invalid.
We can customize an assertion handling class that inherits from NSAssertionHandler to implement some of our own requirements. Like the NSAssertionHandler instance of Mattt Thompson:
@ interface LoggingAssertionHandler: NSAssertionHandler
@ end
@ implementation LoggingAssertionHandler
-(void) handleFailureInMethod: (SEL) selector
Object: (id) object
File: (NSString *) fileName
LineNumber: (NSInteger) line
Description: (NSString *) format,...
{
NSLog (@ "NSAssert Failure: Method% @ for object% @ in% @ #% I", NSStringFromSelector (selector), object, fileName, line)
}
-(void) handleFailureInFunction: (NSString *) functionName
File: (NSString *) fileName
LineNumber: (NSInteger) line
Description: (NSString *) format,...
{
NSLog (@ "NSCAssert Failure: Function (% @) in% @ #% I", functionName, fileName, line)
}
@ end
As mentioned above, each thread has its own assertion processor. We can change the thread's assertion processor by specifying a new value for NSAssertionHandlerKey in the thread's threadDictionary dictionary.
The following code is shown:
-(BOOL) application: (UIApplication *) application
DidFinishLaunchingWithOptions: (NSDictionary *) launchOptions
{
NSAssertionHandler * assertionHandler = [[LoggingAssertionHandler alloc] init]
[NSThread currentThread] threadDictionary] setValue:assertionHandler
ForKey:NSAssertionHandlerKey]
/ /...
Return YES
}
And when should assertions be used? Usually we can use assertions when we expect the program to run as we expect, such as when the process cannot continue when the called parameter is empty. But on the other hand, we also need to consider, is it really necessary to add assertions here? Can we make the program work properly through more fault-tolerant processing?
The penultimate paragraph of Mattt Thompson in NSAssertionHandler is interesting. Here's an excerpt:
But if we look deeper into NSAssertionHandler-and indeed, into our own hearts, there are lessons to be learned about our capacity for kindness and compassion; about our ability to forgive others, and to recover from our own missteps. We can't be right all of the time. We all make mistakes. By accepting limitations in ourselves and others, only then are we able to grow as individuals.
Referenc
NSAssertionHandler
NSAssertionHandler Class Reference
IBOutletCollection
When IB connects to related files, we often use two keywords: IBOutlet and IBAction. Children's shoes that often use xib or storyboard should be very familiar with these two keywords. However, UIKit also provides another pseudo keyword IBOutletCollection, which we can use to connect the same set of controls on the interface to the same array.
Let's first look at the definition of this pseudo keyword, which can be found in the header file UINibDeclarations.h of UIKit.framework as follows:
# ifndef IBOutletCollection # define IBOutletCollection (ClassName) # endif
In addition, there is a more secure definition in the Clang source code, as follows:
# define IBOutletCollection (ClassName) _ _ attribute__ ((iboutletcollection (ClassName)
As you can see from the above definition, unlike IBOutlet, IBOutletCollection takes a parameter, which is a class name.
Typically, when we use an IBOutletCollection attribute, the property must be strong and of type NSArray, as follows:
@ property (strong, nonatomic) IBOutletCollection (UIScrollView) NSArray * scrollViews
Assuming that we have three horizontal scrollView in our xib file, we can connect all three scrollView to the scrollViews property, and then do some unified processing in our code, as shown below:
-(void) setupScrollViewImages {for (UIScrollView * scrollView in self.scrollViews) {[self.imagesData enumerateObjectsUsingBlock: ^ (NSString * imageName, NSUInteger idx, BOOL * stop) {UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake (CGRectGetWidth (scrollView.frame) * idx, 0, CGRectGetWidth (scrollView.frame), CGRectGetHeight (scrollView.frame)]]; imageView.contentMode = UIViewContentModeScaleAspectFill; imageView.image = [UIImage imageNamed:imageName]; [scrollView addSubview:imageView];}];}}
This code affects three scrollView. The advantage of this is that we don't need to manually add scrollView to the scrollViews through the addObject: method.
However, when using IBOutletCollection, you need to be aware of two points:
The order of objects in the IBOutletCollection collection is uncertain. Through the debugging method, we can see that the order of the objects in the collection is the same as the order in which we connect. But this order may vary from version to version of Xcode. So we should not try to assume this order in the code.
Regardless of the control in IBOutletCollection (ClassName), the type of the property is always NSArray. In fact, we can declare any type, such as NSSet,NSMutableArray, or even UIColor, but no matter what class we set here, the IBOutletCollection property always points to a NSArray array.
With regard to the second point, let's take the above scrollViews as an example and make the following changes:
@ property (strong, nonatomic) IBOutletCollection (UIScrollView) NSSet * scrollViews
In fact, when we print this scrollViews on the console, the result is as follows:
(lldb) po self.scrollViews (,)
As you can see, it points to an array of NSArray.
In addition, IBOutletCollection is actually available in iOS 4. However, Objective-C now supports object literals, so defining an array can be done directly with @ [], which is much more convenient. And the object literals approach can add code-defined views that are not in xib, so it is more flexible. Of course, which of the two ways to choose depends on our own actual needs and preferences.
Referenc
IBAction / IBOutlet / IBOutletCollection
IBOutletCollection.m
Use of NSRecursiveLock Recursive Lock
NSRecursiveLock actually defines a recursive lock that can be requested multiple times by the same thread without causing a deadlock. This is mainly used in looping or recursive operations. Let's first look at an example:
NSLock * lock = [[NSLock alloc] init]
Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
Static void (^ RecursiveMethod) (int)
RecursiveMethod = ^ (int value) {
[lock lock]
If (value > 0) {
NSLog (@ "value =% d", value)
Sleep (2)
RecursiveMethod (value-1)
}
[lock unlock]
}
RecursiveMethod (5)
});
This code is a typical deadlock situation. In our thread, RecursiveMethod is called recursively. So every time you enter the block, you add a lock, and from the second time, because the lock is already in use and is not unlocked, it needs to wait for the lock to be released, which leads to a deadlock and the thread is blocked. The following information is output in the debugger:
Value = 5 * *-[NSLock lock]: deadlock ('(null)') * Break on _ NSLockError () to debug.
In this case, we can use NSRecursiveLock. It allows the same thread to lock multiple times without causing a deadlock. The recursive lock tracks the number of times it has been lock. Each successful lock must balance the call to the unlock operation. Only when all reach this balance can the lock be released for use by other threads.
So, make some changes to the above code
NSRecursiveLock * lock = [[NSRecursiveLock alloc] init]
In this way, the program can run normally, and its output is as follows:
Value = 5 value = 4 value = 3 value = 2 value = 1
In addition to implementing the NSLocking protocol, NSRecursiveLock also provides two methods, which are as follows:
/ / attempt to request a lock before the given time
-(BOOL) lockBeforeDate: (NSDate *) limit
/ / attempts to request a lock and immediately returns a Boolean value indicating whether the attempt was successful or not
-(BOOL) tryLock
Both of these methods can be used in the case of multithreading to try to request a recursive lock and then process it accordingly according to the Boolean value returned. The following code is shown:
NSRecursiveLock * lock = [[NSRecursiveLock alloc] init]
Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
Static void (^ RecursiveMethod) (int)
RecursiveMethod = ^ (int value) {
[lock lock]
If (value > 0) {
NSLog (@ "value =% d", value)
Sleep (2)
RecursiveMethod (value-1)
}
[lock unlock]
}
RecursiveMethod (5)
});
Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
Sleep (2)
BOOL flag = [lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow:1]]
If (flag) {
NSLog (@ "lock before date")
[lock unlock]
} else {
NSLog (@ "fail to lock before date")
}
});
In the previous code, we added another piece of code, adding a thread to acquire the recursive lock. We try to acquire the recursive lock in the second thread, which, of course, will fail. The output is as follows:
Value = 5 value = 4 fail to lock before date value = 3 value = 2 value = 1
In addition, NSRecursiveLock declares a name attribute, as follows:
@ property (copy) NSString * name
We can use this string to identify a lock. Cocoa will also use this name as part of the error description message.
Referenc
NSRecursiveLock Class Reference
Different ways to implement Lock in Objective-C (2)
NSHashTable
When you look at the KVOController code, you see the NSHashTable class again, so clean it up a little bit.
NSHashTable follows the example of NSSet (NSMutableSet), but provides more operation options than NSSet, especially in terms of support for weak reference relationships, NSHashTable is more flexible in object / memory handling. Compared to NSSet,NSHashTable, it has the following characteristics:
NSSet (NSMutableSet) holds strong references to its elements, and these elements use hash values and isEqual: methods to do hash detection and determine whether they are equal.
NSHashTable is mutable, and there is no immutable version.
It can hold a weak reference to an element and correctly remove the object after it is destroyed. This cannot be done in NSSet.
Its members can be copied when added.
Its members can use pointers to identify equality and do hash detection.
It can contain any pointer, and its members are not restricted to objects. We can configure an instance of NSHashTable to manipulate arbitrary pointers, not just objects.
When initializing the NSHashTable, we can set an initial option that determines all the subsequent behavior of the NSHashTable object. This option is defined by the NSHashTableOptions enumeration, as follows:
Enum {/ / default behavior, strongly referencing objects in the collection, which is equivalent to NSSet NSHashTableStrongMemory = 0. / before adding objects to the collection, the object NSHashTableCopyIn = NSPointerFunctionsCopyIn is copied, / / the shift pointer (shifted pointer) is used to do hash detection and determine whether the two objects are equal. / / use the description method to do the description string NSHashTableObjectPointerPersonality = NSPointerFunctionsObjectPointerPersonality, / / weakly reference the object in the collection, and it will be removed correctly after the object is released. NSHashTableWeakMemory = NSPointerFunctionsWeakMemory}; typedef NSUInteger NSHashTableOptions
Of course, we can also use NSPointerFunctions for initialization, but only with these values defined by NSHashTableOptions can we ensure that the API of NSHashTable works correctly-including copying, archiving, and quick enumeration.
Personally, I think the attraction of NSHashTable is that it can hold weak references to elements and correctly remove objects after they are destroyed. Let's write an example:
/ / the specific calls are as follows
@ implementation TestHashAndMapTableClass {
NSMutableDictionary * _ dic
NSSet * _ set
NSHashTable * _ hashTable
}
-(instancetype) init {
Self = [super init]
If (self) {
[self testWeakMemory]
NSLog (@ "hash table [init]:% @", _ hashTable)
}
Return self
}
-(void) testWeakMemory {
If (! _ hashTable) {
_ hashTable = [NSHashTable weakObjectsHashTable]
}
NSObject * obj = [[NSObject alloc] init]
[_ hashTable addObject:obj]
NSLog (@ "hash table [testWeakMemory]:% @", _ hashTable)
}
The output of this code is as follows:
Hash table [testWeakMemory]: NSHashTable {
[6]
}
Hash table [init]: NSHashTable {
}
As you can see, after leaving the testWeakMemory method, the obj object is released and the reference to the object in the collection is safely deleted.
In this way, NSHashTable seems to be better than NSSet (NSMutableSet). Does it mean that we all use NSHashTable in our apps? Peter Steinberger gives us a set of data in The Foundation Collection Classes, showing that NSHashTable takes almost twice as long as NSMutableSet in adding objects, while in other operations, performance is roughly the same. So, if we only need the features of NSSet, try to use NSSet.
In addition, Mattt Thompson also wrote an interesting paragraph at the end of NSHashTable & NSMapTable. Here is a direct excerpt:
As always, it's important to remember that programming is not about being clever: always approach a problem from the highest viable level of abstraction. NSSet and NSDictionary are great classes. For 99% of problems, they are undoubtedly the correct tool for the job. If, however, your problem has any of the particular memory management constraints described above, then NSHashTable & NSMapTable may be worth a look.
Referenc
NSHashTable Class Reference
NSHashTable & NSMapTable
NSHashTable & NSMapTable
The Foundation Collection Classes
Bits and pieces
(1) dealing with the problem of "Unknown class XXViewController in Interface Builder file."
Recently, a XXViewController class was written in the static library, and then in the xib of the main project, the class of xib was specified as XXViewController. The following error was reported when the program was running:
Unknown class XXViewController in Interface Builder file.
I've encountered this problem before, but I can't remember it clearly, so I started to look for answers on stackoverflow.
In fact, this problem has nothing to do with Interface Builder, the most direct reason is that the related symbol is not loaded from the static library. The way to deal with this problem is to add "- all_load-ObjC" to the "Build Setting"-> "Other Link Flags" of Target, so that it is OK.
(2) about Unbalanced calls to begin/end appearance transitions for... The handling of problems
One of our businesses has such a requirement that we need to push another web page immediately after entering a list to promote some activities. On iOS 8, our implementation is all OK; but on iOS 7, we find that the push of this web page does not come out, and the console gives a warning message, which is as follows:
Unbalanced calls to begin/end appearance transitions for...
In this case, a black screen is displayed directly when you click the back button in the navigation bar.
We looked it up on stackoverflow and there was a hint:
Occurs when you try and display a new viewcontroller before the current viewcontroller is finished displaying.
It means to try to display a new view controller before the current view controller completes the display.
So we went to check the code, and sure enough, we found that we had done a network request operation in viewDidLoad, and after the request was returned, we went to push this web activity promotion page. At this point, the current view controller may not show completion (that is, the push operation is not complete).
Basically you are trying to push two view controllers onto the stack at almost the same time.
Uncertain results occur when two view controllers are push into the current navigation controller stack almost at the same time, or when two different view controllers are pop at the same time. So we should make sure that there is only one operation on the same navigation controller stack at the same time, and even if the current view controller is in the process of animation, we should not push or pop a new view controller.
So * We put the data requests for web activities into viewDidAppear and did some processing, so the problem was solved.
Thank you for reading this article carefully. I hope the article "what are the iOS development skills that can't be missed" shared by the editor will be helpful to you. At the same time, I also hope you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.