问题
I am downloading ~1300 images. Those are small images total size is around ~500KB. However, after downloading and putting them into userDefault, I get error as below:
libsystem_network.dylib: nw_route_get_ifindex :: socket(PF_ROUTE, SOCK_RAW, PF_ROUTE) failed: [24] Too many open files
Assumingely, downloaded png images are not being closed.
I already extended cache size via below:
// Configuring max network request cache size
let memoryCapacity = 30 * 1024 * 1024 // 30MB
let diskCapacity = 30 * 1024 * 1024 // 30MB
let urlCache = URLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: "myDiscPath")
URLCache.shared = urlCache
And this is the approach I got to store images:
func storeImages (){
for i in stride(from: 0, to: Cur.count, by: 1) {
// Saving into userDefault
saveIconsToDefault(row: i)
}
}
I get the error after all of them being added into userDefault. So, I know they are there.
EDIT:
FUNCTIONS:
func getImageFromWeb(_ urlString: String, closure: @escaping (UIImage?) -> ()) {
guard let url = URL(string: urlString) else {
return closure(nil)
}
let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
guard error == nil else {
print("error: \(String(describing: error))")
return closure(nil)
}
guard response != nil else {
print("no response")
return closure(nil)
}
guard data != nil else {
print("no data")
return closure(nil)
}
DispatchQueue.main.async {
closure(UIImage(data: data!))
}
}; task.resume()
}
func getIcon (id: String, completion: @escaping (UIImage) -> Void) {
var icon = UIImage()
let imageUrl = "https://files/static/img/\(id).png"
getImageFromWeb(imageUrl) { (image) in
if verifyUrl(urlString: imageUrl) == true {
if let image = image {
icon = image
completion(icon)
}
} else {
if let image = UIImage(named: "no_image_icon") {
icon = image
completion(icon)
}
}
}
}
USAGE:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CurrencyCell", for: indexPath) as? CurrencyCell else { return UITableViewCell() }
if currencies.count > 0 {
let noVal = currencies[indexPath.row].rank ?? "N/A"
let nameVal = currencies[indexPath.row].name ?? "N/A"
let priceVal = currencies[indexPath.row].price_usd ?? "N/A"
getIcon(id: currencies[indexPath.row].id!, completion: { (retImg) in
cell.configureCell(no: noVal, name: nameVal, price: priceVal, img: retImg)
})
}
return cell
}
回答1:
The URLSession(configuration: .default)
syntax is creating a new URLSession
for each request. Create a single URLSession
(saving it in some property) and then reuse it for all of the requests. Or, if you're really not doing any custom configuration of the URLSession
, just use URLSession.shared
:
let task = URLSession.shared.dataTask(with: url) { data, response, error in
...
}
task.resume()
You mention that you're saving 1300 images in UserDefaults
. That's not the right place to store that type of data nor for that quantity of files. I'd suggest you use the "Caches" folder as outlined in the File System Programming Guide: The Library Directory Stores App-Specific Files.
let cacheURL = try! FileManager.default
.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("images")
// create your subdirectory before you try to save files into it
try? FileManager.default.createDirectory(at: cacheURL, withIntermediateDirectories: true)
Do not be tempted to store them in the "Documents" folder, either. For more information, see iOS Storage Best Practices.
来源:https://stackoverflow.com/questions/47419462/how-do-you-close-open-files-using-swift